Explorar el Código

Chore: chat log refactor (#5523)

KVOJJJin hace 10 meses
padre
commit
8294e97113
Se han modificado 97 ficheros con 165 adiciones y 5243 borrados
  1. 1 4
      web/app/(shareLayout)/chat/[token]/page.tsx
  2. 1 3
      web/app/(shareLayout)/chatbot/[token]/page.tsx
  3. 2 5
      web/app/(shareLayout)/completion/[token]/page.tsx
  4. 2 4
      web/app/(shareLayout)/workflow/[token]/page.tsx
  5. 0 428
      web/app/components/app/chat/answer/index.tsx
  6. 0 43
      web/app/components/app/chat/icon-component/index.tsx
  7. 0 3
      web/app/components/app/chat/icons/answer.svg
  8. BIN
      web/app/components/app/chat/icons/default-avatar.jpg
  9. 0 3
      web/app/components/app/chat/icons/edit.svg
  10. 0 3
      web/app/components/app/chat/icons/question.svg
  11. 0 7
      web/app/components/app/chat/icons/robot.svg
  12. 0 3
      web/app/components/app/chat/icons/send-active.svg
  13. 0 3
      web/app/components/app/chat/icons/send.svg
  14. 0 19
      web/app/components/app/chat/icons/typing.svg
  15. 0 7
      web/app/components/app/chat/icons/user.svg
  16. 0 455
      web/app/components/app/chat/index.tsx
  17. 0 23
      web/app/components/app/chat/more-info/index.tsx
  18. 0 14
      web/app/components/app/chat/operation/index.tsx
  19. 0 52
      web/app/components/app/chat/question/index.tsx
  20. 0 136
      web/app/components/app/chat/style.module.css
  21. 0 7
      web/app/components/app/chat/thought/style.module.css
  22. 2 2
      web/app/components/app/configuration/debug/debug-with-multiple-model/chat-item.tsx
  23. 2 2
      web/app/components/app/configuration/debug/debug-with-single-model/index.tsx
  24. 112 45
      web/app/components/app/log/list.tsx
  25. 1 1
      web/app/components/app/overview/embedded/index.tsx
  26. 1 1
      web/app/components/app/store.ts
  27. 1 1
      web/app/components/app/text-generate/item/index.tsx
  28. 1 1
      web/app/components/base/agent-log-modal/detail.tsx
  29. 1 1
      web/app/components/base/agent-log-modal/index.tsx
  30. 2 2
      web/app/components/base/chat/chat-with-history/config-panel/index.tsx
  31. 1 1
      web/app/components/base/chat/chat-with-history/sidebar/index.tsx
  32. 0 0
      web/app/components/base/chat/chat-with-history/sidebar/rename-modal.tsx
  33. 1 1
      web/app/components/base/chat/chat/answer/agent-content.tsx
  34. 2 2
      web/app/components/base/chat/chat/answer/index.tsx
  35. 2 2
      web/app/components/base/chat/chat/answer/operation.tsx
  36. 0 0
      web/app/components/base/chat/chat/citation/index.tsx
  37. 0 0
      web/app/components/base/chat/chat/citation/popup.tsx
  38. 0 0
      web/app/components/base/chat/chat/citation/progress-tooltip.tsx
  39. 0 0
      web/app/components/base/chat/chat/citation/tooltip.tsx
  40. 3 0
      web/app/components/base/chat/chat/index.tsx
  41. 0 0
      web/app/components/base/chat/chat/loading-anim/index.tsx
  42. 0 0
      web/app/components/base/chat/chat/loading-anim/style.module.css
  43. 1 1
      web/app/components/base/chat/chat/log/index.tsx
  44. 1 1
      web/app/components/base/chat/chat/thought/index.tsx
  45. 0 0
      web/app/components/base/chat/chat/thought/panel.tsx
  46. 0 0
      web/app/components/base/chat/chat/thought/tool.tsx
  47. 1 1
      web/app/components/base/chat/chat/type.ts
  48. 2 2
      web/app/components/base/chat/embedded-chatbot/config-panel/index.tsx
  49. 2 7
      web/app/components/base/chat/embedded-chatbot/header.tsx
  50. 1 1
      web/app/components/base/chat/types.ts
  51. 0 0
      web/app/components/base/copy-btn/index.tsx
  52. 0 0
      web/app/components/base/copy-btn/style.module.css
  53. 3 3
      web/app/components/base/markdown.tsx
  54. 0 0
      web/app/components/base/mermaid/index.tsx
  55. 1 1
      web/app/components/base/message-log-modal/index.tsx
  56. 1 1
      web/app/components/base/prompt-log-modal/index.tsx
  57. 0 0
      web/app/components/base/svg/index.tsx
  58. 0 0
      web/app/components/base/svg/style.module.css
  59. 0 13
      web/app/components/share/chat/config-scence/index.tsx
  60. 0 73
      web/app/components/share/chat/hooks/use-conversation.ts
  61. 0 953
      web/app/components/share/chat/index.tsx
  62. 0 28
      web/app/components/share/chat/sidebar/app-info/index.tsx
  63. 0 3
      web/app/components/share/chat/sidebar/card.module.css
  64. 0 19
      web/app/components/share/chat/sidebar/card.tsx
  65. 0 167
      web/app/components/share/chat/sidebar/index.tsx
  66. 0 143
      web/app/components/share/chat/sidebar/list/index.tsx
  67. 0 77
      web/app/components/share/chat/sidebar/list/item.tsx
  68. 0 77
      web/app/components/share/chat/value-panel/index.tsx
  69. 0 3
      web/app/components/share/chat/value-panel/style.module.css
  70. 0 388
      web/app/components/share/chat/welcome/index.tsx
  71. 0 74
      web/app/components/share/chat/welcome/massive-component.tsx
  72. 0 22
      web/app/components/share/chat/welcome/style.module.css
  73. 0 13
      web/app/components/share/chatbot/config-scence/index.tsx
  74. 0 72
      web/app/components/share/chatbot/hooks/use-conversation.ts
  75. 0 824
      web/app/components/share/chatbot/index.tsx
  76. 0 28
      web/app/components/share/chatbot/sidebar/app-info/index.tsx
  77. 0 3
      web/app/components/share/chatbot/sidebar/card.module.css
  78. 0 19
      web/app/components/share/chatbot/sidebar/card.tsx
  79. 0 152
      web/app/components/share/chatbot/sidebar/index.tsx
  80. 0 115
      web/app/components/share/chatbot/sidebar/list/index.tsx
  81. 0 7
      web/app/components/share/chatbot/sidebar/list/style.module.css
  82. 0 77
      web/app/components/share/chatbot/value-panel/index.tsx
  83. 0 3
      web/app/components/share/chatbot/value-panel/style.module.css
  84. 0 389
      web/app/components/share/chatbot/welcome/index.tsx
  85. 0 75
      web/app/components/share/chatbot/welcome/massive-component.tsx
  86. 0 22
      web/app/components/share/chatbot/welcome/style.module.css
  87. 0 88
      web/app/components/share/header.tsx
  88. 1 1
      web/app/components/share/text-generation/result/content.tsx
  89. 1 1
      web/app/components/share/text-generation/result/header.tsx
  90. 1 1
      web/app/components/share/text-generation/result/index.tsx
  91. 1 1
      web/app/components/tools/utils/index.ts
  92. 5 5
      web/app/components/workflow/panel/chat-record/index.tsx
  93. 1 1
      web/app/components/workflow/run/output-panel.tsx
  94. 1 1
      web/app/components/workflow/run/result-text.tsx
  95. 1 1
      web/service/base.ts
  96. 1 1
      web/service/debug.ts
  97. 1 1
      web/service/share.ts

+ 1 - 4
web/app/(shareLayout)/chat/[token]/page.tsx

@@ -1,11 +1,8 @@
 'use client'
-import type { FC } from 'react'
 import React from 'react'
-
-import type { IMainProps } from '@/app/components/share/chat'
 import ChatWithHistoryWrap from '@/app/components/base/chat/chat-with-history'
 
-const Chat: FC<IMainProps> = () => {
+const Chat = () => {
   return (
     <ChatWithHistoryWrap />
   )

+ 1 - 3
web/app/(shareLayout)/chatbot/[token]/page.tsx

@@ -1,14 +1,12 @@
 'use client'
-import type { FC } from 'react'
 import React, { useEffect } from 'react'
 import cn from 'classnames'
-import type { IMainProps } from '@/app/components/share/chat'
 import EmbeddedChatbot from '@/app/components/base/chat/embedded-chatbot'
 import Loading from '@/app/components/base/loading'
 import { fetchSystemFeatures } from '@/service/share'
 import LogoSite from '@/app/components/base/logo/logo-site'
 
-const Chatbot: FC<IMainProps> = () => {
+const Chatbot = () => {
   const [isSSOEnforced, setIsSSOEnforced] = React.useState(true)
   const [loading, setLoading] = React.useState(true)
 

+ 2 - 5
web/app/(shareLayout)/completion/[token]/page.tsx

@@ -1,13 +1,10 @@
-import type { FC } from 'react'
 import React from 'react'
-
-import type { IMainProps } from '@/app/components/share/chat'
 import Main from '@/app/components/share/text-generation'
 
-const TextGeneration: FC<IMainProps> = () => {
+const Completion = () => {
   return (
     <Main />
   )
 }
 
-export default React.memo(TextGeneration)
+export default React.memo(Completion)

+ 2 - 4
web/app/(shareLayout)/workflow/[token]/page.tsx

@@ -1,13 +1,11 @@
-import type { FC } from 'react'
 import React from 'react'
 
-import type { IMainProps } from '@/app/components/share/text-generation'
 import Main from '@/app/components/share/text-generation'
 
-const TextGeneration: FC<IMainProps> = () => {
+const Workflow = () => {
   return (
     <Main isWorkflow />
   )
 }
 
-export default React.memo(TextGeneration)
+export default React.memo(Workflow)

+ 0 - 428
web/app/components/app/chat/answer/index.tsx

@@ -1,428 +0,0 @@
-'use client'
-import type { FC, ReactNode } from 'react'
-import React, { useEffect, useMemo, useRef, useState } from 'react'
-import { useTranslation } from 'react-i18next'
-import { UserCircleIcon } from '@heroicons/react/24/solid'
-import cn from 'classnames'
-import type { CitationItem, DisplayScene, FeedbackFunc, Feedbacktype, IChatItem } from '../type'
-import OperationBtn from '../operation'
-import LoadingAnim from '../loading-anim'
-import { RatingIcon } from '../icon-component'
-import s from '../style.module.css'
-import MoreInfo from '../more-info'
-import CopyBtn from '../copy-btn'
-import Thought from '../thought'
-import Citation from '../citation'
-import AudioBtn from '@/app/components/base/audio-btn'
-import { randomString } from '@/utils'
-import type { MessageRating } from '@/models/log'
-import Tooltip from '@/app/components/base/tooltip'
-import { Markdown } from '@/app/components/base/markdown'
-import type { DataSet } from '@/models/datasets'
-import AnnotationCtrlBtn from '@/app/components/app/configuration/toolbox/annotation/annotation-ctrl-btn'
-import EditReplyModal from '@/app/components/app/annotation/edit-annotation-modal'
-import { EditTitle } from '@/app/components/app/annotation/edit-annotation-modal/edit-item'
-import { MessageFast } from '@/app/components/base/icons/src/vender/solid/communication'
-import type { Emoji } from '@/app/components/tools/types'
-import type { VisionFile } from '@/types/app'
-import ImageGallery from '@/app/components/base/image-gallery'
-import Log from '@/app/components/app/chat/log'
-
-const IconWrapper: FC<{ children: React.ReactNode | string }> = ({ children }) => {
-  return <div className={'rounded-lg h-6 w-6 flex items-center justify-center hover:bg-gray-100'}>
-    {children}
-  </div>
-}
-export type IAnswerProps = {
-  item: IChatItem
-  index: number
-  feedbackDisabled: boolean
-  isHideFeedbackEdit: boolean
-  onQueryChange: (query: string) => void
-  onFeedback?: FeedbackFunc
-  displayScene: DisplayScene
-  isResponding?: boolean
-  answerIcon?: ReactNode
-  citation?: CitationItem[]
-  dataSets?: DataSet[]
-  isShowCitation?: boolean
-  isShowCitationHitInfo?: boolean
-  isShowTextToSpeech?: boolean
-  // Annotation props
-  supportAnnotation?: boolean
-  appId?: string
-  question: string
-  onAnnotationEdited?: (question: string, answer: string, index: number) => void
-  onAnnotationAdded?: (annotationId: string, authorName: string, question: string, answer: string, index: number) => void
-  onAnnotationRemoved?: (index: number) => void
-  allToolIcons?: Record<string, string | Emoji>
-  isShowPromptLog?: boolean
-}
-// The component needs to maintain its own state to control whether to display input component
-const Answer: FC<IAnswerProps> = ({
-  item,
-  index,
-  onQueryChange,
-  feedbackDisabled = false,
-  isHideFeedbackEdit = false,
-  onFeedback,
-  displayScene = 'web',
-  isResponding,
-  answerIcon,
-  citation,
-  isShowCitation,
-  isShowCitationHitInfo = false,
-  isShowTextToSpeech,
-  supportAnnotation,
-  appId,
-  question,
-  onAnnotationEdited,
-  onAnnotationAdded,
-  onAnnotationRemoved,
-  allToolIcons,
-  isShowPromptLog,
-}) => {
-  const { id, content, more, feedback, adminFeedback, annotation, agent_thoughts } = item
-  const isAgentMode = !!agent_thoughts && agent_thoughts.length > 0
-  const hasAnnotation = useMemo(() => !!annotation, [annotation])
-  // const [annotation, setAnnotation] = useState<Annotation | undefined | null>(initAnnotation)
-  // const [inputValue, setInputValue] = useState<string>(initAnnotation?.content ?? '')
-  const [localAdminFeedback, setLocalAdminFeedback] = useState<Feedbacktype | undefined | null>(adminFeedback)
-  // const { userProfile } = useContext(AppContext)
-  const { t } = useTranslation()
-
-  const [isShowReplyModal, setIsShowReplyModal] = useState(false)
-
-  /**
- * Render feedback results (distinguish between users and administrators)
- * User reviews cannot be cancelled in Console
- * @param rating feedback result
- * @param isUserFeedback Whether it is user's feedback
- * @param isWebScene Whether it is web scene
- * @returns comp
- */
-  const renderFeedbackRating = (rating: MessageRating | undefined, isUserFeedback = true, isWebScene = true) => {
-    if (!rating)
-      return null
-
-    const isLike = rating === 'like'
-    const ratingIconClassname = isLike ? 'text-primary-600 bg-primary-100 hover:bg-primary-200' : 'text-red-600 bg-red-100 hover:bg-red-200'
-    const UserSymbol = <UserCircleIcon className='absolute top-[-2px] left-[18px] w-3 h-3 rounded-lg text-gray-400 bg-white' />
-    // The tooltip is always displayed, but the content is different for different scenarios.
-    return (
-      <Tooltip
-        selector={`user-feedback-${randomString(16)}`}
-        content={((isWebScene || (!isUserFeedback && !isWebScene)) ? isLike ? t('appDebug.operation.cancelAgree') : t('appDebug.operation.cancelDisagree') : (!isWebScene && isUserFeedback) ? `${t('appDebug.operation.userAction')}${isLike ? t('appDebug.operation.agree') : t('appDebug.operation.disagree')}` : '') as string}
-      >
-        <div
-          className={`relative box-border flex items-center justify-center h-7 w-7 p-0.5 rounded-lg bg-white cursor-pointer text-gray-500 hover:text-gray-800 ${(!isWebScene && isUserFeedback) ? '!cursor-default' : ''}`}
-          style={{ boxShadow: '0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.05)' }}
-          {...((isWebScene || (!isUserFeedback && !isWebScene))
-            ? {
-              onClick: async () => {
-                const res = await onFeedback?.(id, { rating: null })
-                if (res && !isWebScene)
-                  setLocalAdminFeedback({ rating: null })
-              },
-            }
-            : {})}
-        >
-          <div className={`${ratingIconClassname} rounded-lg h-6 w-6 flex items-center justify-center`}>
-            <RatingIcon isLike={isLike} />
-          </div>
-          {!isWebScene && isUserFeedback && UserSymbol}
-        </div>
-      </Tooltip>
-    )
-  }
-
-  /**
-   * Different scenarios have different operation items.
-   * @param isWebScene  Whether it is web scene
-   * @returns comp
-   */
-  const renderItemOperation = (isWebScene = true) => {
-    const userOperation = () => {
-      return feedback?.rating
-        ? null
-        : <div className='flex gap-1'>
-          <Tooltip selector={`user-feedback-${randomString(16)}`} content={t('appLog.detail.operation.like') as string}>
-            {OperationBtn({ innerContent: <IconWrapper><RatingIcon isLike={true} /></IconWrapper>, onClick: () => onFeedback?.(id, { rating: 'like' }) })}
-          </Tooltip>
-          <Tooltip selector={`user-feedback-${randomString(16)}`} content={t('appLog.detail.operation.dislike') as string}>
-            {OperationBtn({ innerContent: <IconWrapper><RatingIcon isLike={false} /></IconWrapper>, onClick: () => onFeedback?.(id, { rating: 'dislike' }) })}
-          </Tooltip>
-        </div>
-    }
-
-    const adminOperation = () => {
-      return <div className='flex gap-1'>
-        {!localAdminFeedback?.rating && <>
-          <Tooltip selector={`user-feedback-${randomString(16)}`} content={t('appLog.detail.operation.like') as string}>
-            {OperationBtn({
-              innerContent: <IconWrapper><RatingIcon isLike={true} /></IconWrapper>,
-              onClick: async () => {
-                const res = await onFeedback?.(id, { rating: 'like' })
-                if (res)
-                  setLocalAdminFeedback({ rating: 'like' })
-              },
-            })}
-          </Tooltip>
-          <Tooltip selector={`user-feedback-${randomString(16)}`} content={t('appLog.detail.operation.dislike') as string}>
-            {OperationBtn({
-              innerContent: <IconWrapper><RatingIcon isLike={false} /></IconWrapper>,
-              onClick: async () => {
-                const res = await onFeedback?.(id, { rating: 'dislike' })
-                if (res)
-                  setLocalAdminFeedback({ rating: 'dislike' })
-              },
-            })}
-          </Tooltip>
-        </>}
-      </div>
-    }
-
-    return (
-      <div className={`${s.itemOperation} flex gap-2`}>
-        {isWebScene ? userOperation() : adminOperation()}
-      </div>
-    )
-  }
-
-  const getImgs = (list?: VisionFile[]) => {
-    if (!list)
-      return []
-    return list.filter(file => file.type === 'image' && file.belongs_to === 'assistant')
-  }
-
-  const agentModeAnswer = (
-    <div>
-      {agent_thoughts?.map((item, index) => (
-        <div key={index}>
-          {item.thought && (
-            <Markdown content={item.thought} />
-          )}
-          {/* {item.tool} */}
-          {/* perhaps not use tool */}
-          {!!item.tool && (
-            <Thought
-              thought={item}
-              allToolIcons={allToolIcons || {}}
-              isFinished={!!item.observation || !isResponding}
-            />
-          )}
-
-          {getImgs(item.message_files).length > 0 && (
-            <ImageGallery srcs={getImgs(item.message_files).map(item => item.url)} />
-          )}
-        </div>
-      ))}
-    </div>
-  )
-
-  const [containerWidth, setContainerWidth] = useState(0)
-  const [contentWidth, setContentWidth] = useState(0)
-  const containerRef = useRef<HTMLDivElement>(null)
-  const contentRef = useRef<HTMLDivElement>(null)
-
-  const getContainerWidth = () => {
-    if (containerRef.current)
-      setContainerWidth(containerRef.current?.clientWidth + 24)
-  }
-  const getContentWidth = () => {
-    if (contentRef.current)
-      setContentWidth(contentRef.current?.clientWidth)
-  }
-
-  useEffect(() => {
-    getContainerWidth()
-  }, [])
-
-  useEffect(() => {
-    if (!isResponding)
-      getContentWidth()
-  }, [isResponding])
-
-  const operationWidth = useMemo(() => {
-    let width = 0
-    if (!item.isOpeningStatement)
-      width += 28
-    if (!item.isOpeningStatement && isShowPromptLog)
-      width += 102 + 8
-    if (!item.isOpeningStatement && isShowTextToSpeech)
-      width += 33
-    if (!item.isOpeningStatement && supportAnnotation)
-      width += 96 + 8
-    if (!feedbackDisabled && !item.feedbackDisabled)
-      width += 60 + 8
-    if (!feedbackDisabled && localAdminFeedback?.rating && !item.isOpeningStatement)
-      width += 60 + 8
-    if (!feedbackDisabled && feedback?.rating && !item.isOpeningStatement)
-      width += 28 + 8
-    return width
-  }, [item.isOpeningStatement, item.feedbackDisabled, isShowPromptLog, isShowTextToSpeech, supportAnnotation, feedbackDisabled, localAdminFeedback?.rating, feedback?.rating])
-
-  const positionRight = useMemo(() => operationWidth < containerWidth - contentWidth - 4, [operationWidth, containerWidth, contentWidth])
-
-  return (
-    // data-id for debug the item message is right
-    <div key={id} data-id={id}>
-      <div className='flex items-start'>
-        {
-          answerIcon || (
-            <div className={`${s.answerIcon} w-10 h-10 shrink-0`}>
-              {isResponding
-                && <div className={s.typeingIcon}>
-                  <LoadingAnim type='avatar' />
-                </div>
-              }
-            </div>
-          )
-        }
-        <div ref={containerRef} className={cn(s.answerWrapWrap, 'chat-answer-container')}>
-          <div className={cn(s.answerWrap, 'group')}>
-            <div ref={contentRef} className={`${s.answer} relative text-sm text-gray-900`}>
-              <div className={'ml-2 py-3 px-4 bg-gray-100 rounded-tr-2xl rounded-b-2xl'}>
-                {(isResponding && (isAgentMode ? (!content && (agent_thoughts || []).filter(item => !!item.thought || !!item.tool).length === 0) : !content))
-                  ? (
-                    <div className='flex items-center justify-center w-6 h-5'>
-                      <LoadingAnim type='text' />
-                    </div>
-                  )
-                  : (
-                    <div>
-                      {annotation?.logAnnotation && (
-                        <div className='mb-1'>
-                          <div className='mb-3'>
-                            {isAgentMode
-                              ? (<div className='line-through !text-gray-400'>{agentModeAnswer}</div>)
-                              : (
-                                <Markdown className='line-through !text-gray-400' content={content} />
-                              )}
-                          </div>
-                          <EditTitle title={t('appAnnotation.editBy', {
-                            author: annotation?.logAnnotation.account?.name,
-                          })} />
-                        </div>
-                      )}
-                      <div>
-                        {annotation?.logAnnotation
-                          ? (
-                            <Markdown content={annotation?.logAnnotation.content || ''} />
-                          )
-                          : (isAgentMode
-                            ? agentModeAnswer
-                            : (
-                              <Markdown content={content} />
-                            ))}
-                      </div>
-                      {(hasAnnotation && !annotation?.logAnnotation) && (
-                        <EditTitle className='mt-1' title={t('appAnnotation.editBy', {
-                          author: annotation?.authorName,
-                        })} />
-                      )}
-                      {item.isOpeningStatement && item.suggestedQuestions && item.suggestedQuestions.filter(q => !!q && q.trim()).length > 0 && (
-                        <div className='flex flex-wrap'>
-                          {item.suggestedQuestions.filter(q => !!q && q.trim()).map((question, index) => (
-                            <div
-                              key={index}
-                              className='mt-1 mr-1 max-w-full last:mr-0 shrink-0 py-[5px] leading-[18px] items-center px-4 rounded-lg border border-gray-200 shadow-xs bg-white text-xs font-medium text-primary-600 cursor-pointer'
-                              onClick={() => onQueryChange(question)}
-                            >
-                              {question}
-                            </div>),
-                          )}
-                        </div>
-                      )}
-                    </div>
-                  )}
-                {
-                  !!citation?.length && isShowCitation && !isResponding && (
-                    <Citation data={citation} showHitInfo={isShowCitationHitInfo} />
-                  )
-                }
-              </div>
-              {hasAnnotation && (
-                <div
-                  className={cn(s.hasAnnotationBtn, '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]')}
-                  style={{ boxShadow: '0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.05)' }}
-                >
-                  <div className='p-1 rounded-lg bg-[#EEF4FF] '>
-                    <MessageFast className='w-4 h-4' />
-                  </div>
-                </div>
-              )}
-              <div
-                className={cn(
-                  'absolute -top-3.5 flex justify-end gap-1',
-                  positionRight ? '!top-[9px]' : '-right-3.5',
-                )}
-                style={positionRight ? { left: contentWidth + 8 } : {}}
-              >
-                {!item.isOpeningStatement && (
-                  <CopyBtn
-                    value={content}
-                    className={cn(s.copyBtn, 'mr-1')}
-                  />
-                )}
-                {((isShowPromptLog && !isResponding) || (!item.isOpeningStatement && isShowTextToSpeech)) && (
-                  <div className='hidden group-hover:flex items-center w-max h-[28px] p-0.5 rounded-lg bg-white border-[0.5px] border-gray-100 shadow-md shrink-0'>
-                    {isShowPromptLog && !isResponding && (
-                      <Log logItem={item} />
-                    )}
-                    {!item.isOpeningStatement && isShowTextToSpeech && (
-                      <>
-                        <div className='mx-1 w-[1px] h-[14px] bg-gray-200'/>
-                        <AudioBtn
-                          value={content}
-                          noCache={false}
-                          className={cn(s.playBtn)}
-                        />
-                      </>
-                    )}
-                  </div>
-                )}
-                {(!item.isOpeningStatement && supportAnnotation) && (
-                  <AnnotationCtrlBtn
-                    appId={appId!}
-                    messageId={id}
-                    annotationId={annotation?.id || ''}
-                    className={cn(s.annotationBtn, 'ml-1 shrink-0')}
-                    cached={hasAnnotation}
-                    query={question}
-                    answer={content}
-                    onAdded={(id, authorName) => onAnnotationAdded?.(id, authorName, question, content, index)}
-                    onEdit={() => setIsShowReplyModal(true)}
-                    onRemoved={() => onAnnotationRemoved!(index)}
-                  />
-                )}
-
-                <EditReplyModal
-                  isShow={isShowReplyModal}
-                  onHide={() => setIsShowReplyModal(false)}
-                  query={question}
-                  answer={content}
-                  onEdited={(editedQuery, editedAnswer) => onAnnotationEdited!(editedQuery, editedAnswer, index)}
-                  onAdded={(annotationId, authorName, editedQuery, editedAnswer) => onAnnotationAdded!(annotationId, authorName, editedQuery, editedAnswer, index)}
-                  appId={appId!}
-                  messageId={id}
-                  annotationId={annotation?.id || ''}
-                  createdAt={annotation?.created_at}
-                  onRemove={() => { }}
-                />
-
-                {!feedbackDisabled && !item.feedbackDisabled && renderItemOperation(displayScene !== 'console')}
-                {/* Admin feedback is displayed only in the background. */}
-                {!feedbackDisabled && renderFeedbackRating(localAdminFeedback?.rating, false, false)}
-                {/* User feedback must be displayed */}
-                {!feedbackDisabled && renderFeedbackRating(feedback?.rating, !isHideFeedbackEdit, displayScene !== 'console')}
-              </div>
-            </div>
-            {more && <MoreInfo className='invisible group-hover:visible' more={more} isQuestion={false} />}
-          </div>
-        </div>
-      </div>
-    </div>
-  )
-}
-export default React.memo(Answer)

+ 0 - 43
web/app/components/app/chat/icon-component/index.tsx

@@ -1,43 +0,0 @@
-import type { FC, SVGProps } from 'react'
-import { HandThumbDownIcon, HandThumbUpIcon } from '@heroicons/react/24/outline'
-
-export const stopIcon = (
-  <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
-    <path fillRule="evenodd" clipRule="evenodd" d="M7.00004 0.583313C3.45621 0.583313 0.583374 3.45615 0.583374 6.99998C0.583374 10.5438 3.45621 13.4166 7.00004 13.4166C10.5439 13.4166 13.4167 10.5438 13.4167 6.99998C13.4167 3.45615 10.5439 0.583313 7.00004 0.583313ZM4.73029 4.98515C4.66671 5.10993 4.66671 5.27328 4.66671 5.59998V8.39998C4.66671 8.72668 4.66671 8.89003 4.73029 9.01481C4.78621 9.12457 4.87545 9.21381 4.98521 9.26973C5.10999 9.33331 5.27334 9.33331 5.60004 9.33331H8.40004C8.72674 9.33331 8.89009 9.33331 9.01487 9.26973C9.12463 9.21381 9.21387 9.12457 9.2698 9.01481C9.33337 8.89003 9.33337 8.72668 9.33337 8.39998V5.59998C9.33337 5.27328 9.33337 5.10993 9.2698 4.98515C9.21387 4.87539 9.12463 4.78615 9.01487 4.73023C8.89009 4.66665 8.72674 4.66665 8.40004 4.66665H5.60004C5.27334 4.66665 5.10999 4.66665 4.98521 4.73023C4.87545 4.78615 4.78621 4.87539 4.73029 4.98515Z" fill="#667085" />
-  </svg>
-)
-
-export const OpeningStatementIcon = ({ className }: SVGProps<SVGElement>) => (
-  <svg className={className} width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
-    <path fillRule="evenodd" clipRule="evenodd" d="M6.25002 1C3.62667 1 1.50002 3.12665 1.50002 5.75C1.50002 6.28 1.58702 6.79071 1.7479 7.26801C1.7762 7.35196 1.79285 7.40164 1.80368 7.43828L1.80722 7.45061L1.80535 7.45452C1.79249 7.48102 1.77339 7.51661 1.73766 7.58274L0.911727 9.11152C0.860537 9.20622 0.807123 9.30503 0.770392 9.39095C0.733879 9.47635 0.674738 9.63304 0.703838 9.81878C0.737949 10.0365 0.866092 10.2282 1.05423 10.343C1.21474 10.4409 1.38213 10.4461 1.475 10.4451C1.56844 10.444 1.68015 10.4324 1.78723 10.4213L4.36472 10.1549C4.406 10.1506 4.42758 10.1484 4.44339 10.1472L4.44542 10.147L4.45161 10.1492C4.47103 10.1562 4.49738 10.1663 4.54285 10.1838C5.07332 10.3882 5.64921 10.5 6.25002 10.5C8.87338 10.5 11 8.37335 11 5.75C11 3.12665 8.87338 1 6.25002 1ZM4.48481 4.29111C5.04844 3.81548 5.7986 3.9552 6.24846 4.47463C6.69831 3.9552 7.43879 3.82048 8.01211 4.29111C8.58544 4.76175 8.6551 5.562 8.21247 6.12453C7.93825 6.47305 7.24997 7.10957 6.76594 7.54348C6.58814 7.70286 6.49924 7.78255 6.39255 7.81466C6.30103 7.84221 6.19589 7.84221 6.10436 7.81466C5.99767 7.78255 5.90878 7.70286 5.73098 7.54348C5.24694 7.10957 4.55867 6.47305 4.28444 6.12453C3.84182 5.562 3.92117 4.76675 4.48481 4.29111Z" fill="#667085" />
-  </svg>
-)
-
-export const RatingIcon: FC<{ isLike: boolean }> = ({ isLike }) => {
-  return isLike ? <HandThumbUpIcon className='w-4 h-4' /> : <HandThumbDownIcon className='w-4 h-4' />
-}
-
-export const EditIcon = ({ className }: SVGProps<SVGElement>) => {
-  return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
-    <path d="M14 11.9998L13.3332 12.7292C12.9796 13.1159 12.5001 13.3332 12.0001 13.3332C11.5001 13.3332 11.0205 13.1159 10.6669 12.7292C10.3128 12.3432 9.83332 12.1265 9.33345 12.1265C8.83359 12.1265 8.35409 12.3432 7.99998 12.7292M2 13.3332H3.11636C3.44248 13.3332 3.60554 13.3332 3.75899 13.2963C3.89504 13.2637 4.0251 13.2098 4.1444 13.1367C4.27895 13.0542 4.39425 12.9389 4.62486 12.7083L13 4.33316C13.5523 3.78087 13.5523 2.88544 13 2.33316C12.4477 1.78087 11.5523 1.78087 11 2.33316L2.62484 10.7083C2.39424 10.9389 2.27894 11.0542 2.19648 11.1888C2.12338 11.3081 2.0695 11.4381 2.03684 11.5742C2 11.7276 2 11.8907 2 12.2168V13.3332Z" stroke="#6B7280" strokeLinecap="round" strokeLinejoin="round" />
-  </svg>
-}
-
-export const EditIconSolid = ({ className }: SVGProps<SVGElement>) => {
-  return <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
-    <path fillRule="evenodd" clipRule="evenodd" d="M10.8374 8.63108C11.0412 8.81739 11.0554 9.13366 10.8691 9.33747L10.369 9.88449C10.0142 10.2725 9.52293 10.5001 9.00011 10.5001C8.47746 10.5001 7.98634 10.2727 7.63157 9.8849C7.45561 9.69325 7.22747 9.59515 7.00014 9.59515C6.77271 9.59515 6.54446 9.69335 6.36846 9.88517C6.18177 10.0886 5.86548 10.1023 5.66201 9.91556C5.45853 9.72888 5.44493 9.41259 5.63161 9.20911C5.98678 8.82201 6.47777 8.59515 7.00014 8.59515C7.52251 8.59515 8.0135 8.82201 8.36867 9.20911L8.36924 9.20974C8.54486 9.4018 8.77291 9.50012 9.00011 9.50012C9.2273 9.50012 9.45533 9.40182 9.63095 9.20979L10.131 8.66276C10.3173 8.45895 10.6336 8.44476 10.8374 8.63108Z" fill="#6B7280" />
-    <path fillRule="evenodd" clipRule="evenodd" d="M7.89651 1.39656C8.50599 0.787085 9.49414 0.787084 10.1036 1.39656C10.7131 2.00604 10.7131 2.99419 10.1036 3.60367L3.82225 9.88504C3.81235 9.89494 3.80254 9.90476 3.79281 9.91451C3.64909 10.0585 3.52237 10.1855 3.3696 10.2791C3.23539 10.3613 3.08907 10.4219 2.93602 10.4587C2.7618 10.5005 2.58242 10.5003 2.37897 10.5001C2.3652 10.5001 2.35132 10.5001 2.33732 10.5001H1.50005C1.22391 10.5001 1.00005 10.2763 1.00005 10.0001V9.16286C1.00005 9.14886 1.00004 9.13497 1.00003 9.1212C0.999836 8.91776 0.999669 8.73838 1.0415 8.56416C1.07824 8.4111 1.13885 8.26479 1.22109 8.13058C1.31471 7.97781 1.44166 7.85109 1.58566 7.70736C1.5954 7.69764 1.60523 7.68783 1.61513 7.67793L7.89651 1.39656Z" fill="#6B7280" />
-  </svg>
-}
-
-export const TryToAskIcon = (
-  <svg width="11" height="10" viewBox="0 0 11 10" fill="none" xmlns="http://www.w3.org/2000/svg">
-    <path d="M5.88889 0.683718C5.827 0.522805 5.67241 0.416626 5.5 0.416626C5.3276 0.416626 5.173 0.522805 5.11111 0.683718L4.27279 2.86334C4.14762 3.18877 4.10829 3.28255 4.05449 3.35821C4.00051 3.43413 3.93418 3.50047 3.85826 3.55445C3.78259 3.60825 3.68881 3.64758 3.36338 3.77275L1.18376 4.61106C1.02285 4.67295 0.916668 4.82755 0.916668 4.99996C0.916668 5.17236 1.02285 5.32696 1.18376 5.38885L3.36338 6.22717C3.68881 6.35234 3.78259 6.39167 3.85826 6.44547C3.93418 6.49945 4.00051 6.56578 4.05449 6.6417C4.10829 6.71737 4.14762 6.81115 4.27279 7.13658L5.11111 9.3162C5.173 9.47711 5.3276 9.58329 5.5 9.58329C5.67241 9.58329 5.82701 9.47711 5.8889 9.3162L6.72721 7.13658C6.85238 6.81115 6.89171 6.71737 6.94551 6.6417C6.99949 6.56578 7.06583 6.49945 7.14175 6.44547C7.21741 6.39167 7.31119 6.35234 7.63662 6.22717L9.81624 5.38885C9.97715 5.32696 10.0833 5.17236 10.0833 4.99996C10.0833 4.82755 9.97715 4.67295 9.81624 4.61106L7.63662 3.77275C7.31119 3.64758 7.21741 3.60825 7.14175 3.55445C7.06583 3.50047 6.99949 3.43413 6.94551 3.35821C6.89171 3.28255 6.85238 3.18877 6.72721 2.86334L5.88889 0.683718Z" fill="#667085" />
-  </svg>
-)
-
-export const ReplayIcon = ({ className }: SVGProps<SVGElement>) => (
-  <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
-    <path d="M1.33301 6.66667C1.33301 6.66667 2.66966 4.84548 3.75556 3.75883C4.84147 2.67218 6.34207 2 7.99967 2C11.3134 2 13.9997 4.68629 13.9997 8C13.9997 11.3137 11.3134 14 7.99967 14C5.26428 14 2.95642 12.1695 2.23419 9.66667M1.33301 6.66667V2.66667M1.33301 6.66667H5.33301" stroke="white" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
-  </svg>
-)

+ 0 - 3
web/app/components/app/chat/icons/answer.svg

@@ -1,3 +0,0 @@
-<svg width="8" height="12" viewBox="0 0 8 12" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M1.03647 1.5547C0.59343 0.890144 1.06982 0 1.86852 0H8V12L1.03647 1.5547Z" fill="#F3F4F6"/>
-</svg>

BIN
web/app/components/app/chat/icons/default-avatar.jpg


+ 0 - 3
web/app/components/app/chat/icons/edit.svg

@@ -1,3 +0,0 @@
-<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M14 11.9998L13.3332 12.7292C12.9796 13.1159 12.5001 13.3332 12.0001 13.3332C11.5001 13.3332 11.0205 13.1159 10.6669 12.7292C10.3128 12.3432 9.83332 12.1265 9.33345 12.1265C8.83359 12.1265 8.35409 12.3432 7.99998 12.7292M2 13.3332H3.11636C3.44248 13.3332 3.60554 13.3332 3.75899 13.2963C3.89504 13.2637 4.0251 13.2098 4.1444 13.1367C4.27895 13.0542 4.39425 12.9389 4.62486 12.7083L13 4.33316C13.5523 3.78087 13.5523 2.88544 13 2.33316C12.4477 1.78087 11.5523 1.78087 11 2.33316L2.62484 10.7083C2.39424 10.9389 2.27894 11.0542 2.19648 11.1888C2.12338 11.3081 2.0695 11.4381 2.03684 11.5742C2 11.7276 2 11.8907 2 12.2168V13.3332Z" stroke="#6B7280" strokeLinecap="round" strokeLinejoin="round" />
-</svg>

+ 0 - 3
web/app/components/app/chat/icons/question.svg

@@ -1,3 +0,0 @@
-<svg width="8" height="12" viewBox="0 0 8 12" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M6.96353 1.5547C7.40657 0.890144 6.93018 0 6.13148 0H0V12L6.96353 1.5547Z" fill="#E1EFFE"/>
-</svg>

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 7
web/app/components/app/chat/icons/robot.svg


+ 0 - 3
web/app/components/app/chat/icons/send-active.svg

@@ -1,3 +0,0 @@
-<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M23.447 16.8939C23.6128 16.8108 23.7523 16.6832 23.8498 16.5253C23.9473 16.3674 23.9989 16.1855 23.9989 15.9999C23.9989 15.8144 23.9473 15.6325 23.8498 15.4746C23.7523 15.3167 23.6128 15.1891 23.447 15.1059L9.44697 8.10595C9.27338 8.01909 9.07827 7.98463 8.88543 8.00677C8.69259 8.02891 8.51036 8.1067 8.36098 8.23064C8.2116 8.35458 8.10151 8.51931 8.04415 8.70475C7.9868 8.89019 7.98465 9.08831 8.03797 9.27495L9.46697 14.2749C9.52674 14.4839 9.65297 14.6677 9.82655 14.7985C10.0001 14.9294 10.2116 15.0001 10.429 14.9999H15C15.2652 14.9999 15.5195 15.1053 15.7071 15.2928C15.8946 15.4804 16 15.7347 16 15.9999C16 16.2652 15.8946 16.5195 15.7071 16.7071C15.5195 16.8946 15.2652 16.9999 15 16.9999H10.429C10.2116 16.9998 10.0001 17.0705 9.82655 17.2013C9.65297 17.3322 9.52674 17.516 9.46697 17.7249L8.03897 22.7249C7.98554 22.9115 7.98756 23.1096 8.04478 23.2951C8.10201 23.4805 8.21195 23.6453 8.36122 23.7693C8.51049 23.8934 8.69263 23.9713 8.88542 23.9936C9.07821 24.0159 9.27332 23.9816 9.44697 23.8949L23.447 16.8949V16.8939Z" fill="#1C64F2"/>
-</svg>

+ 0 - 3
web/app/components/app/chat/icons/send.svg

@@ -1,3 +0,0 @@
-<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M23.447 16.8939C23.6128 16.8108 23.7523 16.6832 23.8498 16.5253C23.9473 16.3674 23.9989 16.1855 23.9989 15.9999C23.9989 15.8144 23.9473 15.6325 23.8498 15.4746C23.7523 15.3167 23.6128 15.1891 23.447 15.1059L9.44697 8.10595C9.27338 8.01909 9.07827 7.98463 8.88543 8.00677C8.69259 8.02891 8.51036 8.1067 8.36098 8.23064C8.2116 8.35458 8.10151 8.51931 8.04415 8.70475C7.9868 8.89019 7.98465 9.08831 8.03797 9.27495L9.46697 14.2749C9.52674 14.4839 9.65297 14.6677 9.82655 14.7985C10.0001 14.9294 10.2116 15.0001 10.429 14.9999H15C15.2652 14.9999 15.5195 15.1053 15.7071 15.2928C15.8946 15.4804 16 15.7347 16 15.9999C16 16.2652 15.8946 16.5195 15.7071 16.7071C15.5195 16.8946 15.2652 16.9999 15 16.9999H10.429C10.2116 16.9998 10.0001 17.0705 9.82655 17.2013C9.65297 17.3322 9.52674 17.516 9.46697 17.7249L8.03897 22.7249C7.98554 22.9115 7.98756 23.1096 8.04478 23.2951C8.10201 23.4805 8.21195 23.6453 8.36122 23.7693C8.51049 23.8934 8.69263 23.9713 8.88542 23.9936C9.07821 24.0159 9.27332 23.9816 9.44697 23.8949L23.447 16.8949V16.8939Z" fill="#D1D5DB"/>
-</svg>

+ 0 - 19
web/app/components/app/chat/icons/typing.svg

@@ -1,19 +0,0 @@
-<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g filter="url(#filter0_d_2358_1380)">
-<rect x="2" y="1" width="16" height="16" rx="8" fill="white"/>
-<path opacity="0.7" d="M13.5 9H13.505M14 9C14 9.13261 13.9473 9.25979 13.8536 9.35355C13.7598 9.44732 13.6326 9.5 13.5 9.5C13.3674 9.5 13.2402 9.44732 13.1464 9.35355C13.0527 9.25979 13 9.13261 13 9C13 8.86739 13.0527 8.74021 13.1464 8.64645C13.2402 8.55268 13.3674 8.5 13.5 8.5C13.6326 8.5 13.7598 8.55268 13.8536 8.64645C13.9473 8.74021 14 8.86739 14 9Z" stroke="#155EEF" stroke-linecap="round" stroke-linejoin="round"/>
-<path opacity="0.6" d="M10 9H10.005M10.5 9C10.5 9.13261 10.4473 9.25979 10.3536 9.35355C10.2598 9.44732 10.1326 9.5 10 9.5C9.86739 9.5 9.74021 9.44732 9.64645 9.35355C9.55268 9.25979 9.5 9.13261 9.5 9C9.5 8.86739 9.55268 8.74021 9.64645 8.64645C9.74021 8.55268 9.86739 8.5 10 8.5C10.1326 8.5 10.2598 8.55268 10.3536 8.64645C10.4473 8.74021 10.5 8.86739 10.5 9Z" stroke="#155EEF" stroke-linecap="round" stroke-linejoin="round"/>
-<path opacity="0.3" d="M6.5 9H6.505M7 9C7 9.13261 6.94732 9.25979 6.85355 9.35355C6.75979 9.44732 6.63261 9.5 6.5 9.5C6.36739 9.5 6.24021 9.44732 6.14645 9.35355C6.05268 9.25979 6 9.13261 6 9C6 8.86739 6.05268 8.74021 6.14645 8.64645C6.24021 8.55268 6.36739 8.5 6.5 8.5C6.63261 8.5 6.75979 8.55268 6.85355 8.64645C6.94732 8.74021 7 8.86739 7 9Z" stroke="#155EEF" stroke-linecap="round" stroke-linejoin="round"/>
-</g>
-<defs>
-<filter id="filter0_d_2358_1380" x="0" y="0" width="20" height="20" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
-<feFlood flood-opacity="0" result="BackgroundImageFix"/>
-<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
-<feOffset dy="1"/>
-<feGaussianBlur stdDeviation="1"/>
-<feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.0941176 0 0 0 0 0.156863 0 0 0 0.05 0"/>
-<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_2358_1380"/>
-<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_2358_1380" result="shape"/>
-</filter>
-</defs>
-</svg>

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 7
web/app/components/app/chat/icons/user.svg


+ 0 - 455
web/app/components/app/chat/index.tsx

@@ -1,455 +0,0 @@
-'use client'
-import type { FC, ReactNode } from 'react'
-import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'
-import Textarea from 'rc-textarea'
-import { useContext } from 'use-context-selector'
-import cn from 'classnames'
-import Recorder from 'js-audio-recorder'
-import { useTranslation } from 'react-i18next'
-import s from './style.module.css'
-import type { DisplayScene, FeedbackFunc, IChatItem } from './type'
-import { TryToAskIcon, stopIcon } from './icon-component'
-import Answer from './answer'
-import Question from './question'
-import TooltipPlus from '@/app/components/base/tooltip-plus'
-import { ToastContext } from '@/app/components/base/toast'
-import Button from '@/app/components/base/button'
-import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
-import VoiceInput from '@/app/components/base/voice-input'
-import { Microphone01 } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
-import { Microphone01 as Microphone01Solid } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'
-import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
-import type { DataSet } from '@/models/datasets'
-import ChatImageUploader from '@/app/components/base/image-uploader/chat-image-uploader'
-import ImageList from '@/app/components/base/image-uploader/image-list'
-import { TransferMethod, type VisionFile, type VisionSettings } from '@/types/app'
-import { useClipboardUploader, useDraggableUploader, useImageFiles } from '@/app/components/base/image-uploader/hooks'
-import type { Annotation } from '@/models/log'
-import type { Emoji } from '@/app/components/tools/types'
-
-export type IChatProps = {
-  appId?: string
-  configElem?: React.ReactNode
-  chatList: IChatItem[]
-  onChatListChange?: (chatList: IChatItem[]) => void
-  controlChatUpdateAllConversation?: number
-  /**
-   * Whether to display the editing area and rating status
-   */
-  feedbackDisabled?: boolean
-  /**
-   * Whether to display the input area
-   */
-  isHideFeedbackEdit?: boolean
-  isHideSendInput?: boolean
-  onFeedback?: FeedbackFunc
-  checkCanSend?: () => boolean
-  query?: string
-  onQueryChange?: (query: string) => void
-  onSend?: (message: string, files: VisionFile[]) => void
-  displayScene?: DisplayScene
-  useCurrentUserAvatar?: boolean
-  isResponding?: boolean
-  canStopResponding?: boolean
-  abortResponding?: () => void
-  controlClearQuery?: number
-  controlFocus?: number
-  isShowSuggestion?: boolean
-  suggestionList?: string[]
-  isShowSpeechToText?: boolean
-  isShowTextToSpeech?: boolean
-  isShowCitation?: boolean
-  answerIcon?: ReactNode
-  isShowConfigElem?: boolean
-  dataSets?: DataSet[]
-  isShowCitationHitInfo?: boolean
-  isShowPromptLog?: boolean
-  visionConfig?: VisionSettings
-  supportAnnotation?: boolean
-  allToolIcons?: Record<string, string | Emoji>
-  customDisclaimer?: string
-}
-
-const Chat: FC<IChatProps> = ({
-  configElem,
-  chatList,
-  query = '',
-  onQueryChange = () => { },
-  feedbackDisabled = false,
-  isHideFeedbackEdit = false,
-  isHideSendInput = false,
-  onFeedback,
-  checkCanSend,
-  onSend = () => { },
-  displayScene,
-  useCurrentUserAvatar,
-  isResponding,
-  canStopResponding,
-  abortResponding,
-  controlClearQuery,
-  controlFocus,
-  isShowSuggestion,
-  suggestionList,
-  isShowSpeechToText,
-  isShowTextToSpeech,
-  isShowCitation,
-  answerIcon,
-  isShowConfigElem,
-  dataSets,
-  isShowCitationHitInfo,
-  isShowPromptLog,
-  visionConfig,
-  appId,
-  supportAnnotation,
-  onChatListChange,
-  allToolIcons,
-  customDisclaimer,
-}) => {
-  const { t } = useTranslation()
-  const { notify } = useContext(ToastContext)
-  const {
-    files,
-    onUpload,
-    onRemove,
-    onReUpload,
-    onImageLinkLoadError,
-    onImageLinkLoadSuccess,
-    onClear,
-  } = useImageFiles()
-  const { onPaste } = useClipboardUploader({ onUpload, visionConfig, files })
-  const { onDragEnter, onDragLeave, onDragOver, onDrop, isDragActive } = useDraggableUploader<HTMLTextAreaElement>({ onUpload, files, visionConfig })
-  const isUseInputMethod = useRef(false)
-
-  const handleContentChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
-    const value = e.target.value
-    onQueryChange(value)
-  }
-
-  const logError = (message: string) => {
-    notify({ type: 'error', message, duration: 3000 })
-  }
-
-  const valid = (q?: string) => {
-    const sendQuery = q || query
-    if (!sendQuery || sendQuery.trim() === '') {
-      logError('Message cannot be empty')
-      return false
-    }
-    return true
-  }
-
-  useEffect(() => {
-    if (controlClearQuery)
-      onQueryChange('')
-  }, [controlClearQuery])
-
-  const handleSend = (q?: string) => {
-    if (!valid(q) || (checkCanSend && !checkCanSend()))
-      return
-    onSend(q || query, files.filter(file => file.progress !== -1).map(fileItem => ({
-      type: 'image',
-      transfer_method: fileItem.type,
-      url: fileItem.url,
-      upload_file_id: fileItem.fileId,
-    })))
-    if (!files.find(item => item.type === TransferMethod.local_file && !item.fileId)) {
-      if (files.length)
-        onClear()
-      if (!isResponding)
-        onQueryChange('')
-    }
-  }
-
-  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) {
-      onQueryChange(query.replace(/\n$/, ''))
-      e.preventDefault()
-    }
-  }
-
-  const media = useBreakpoints()
-  const isMobile = media === MediaType.mobile
-  const sendBtn = <div className={cn(!(!query || query.trim() === '') && s.sendBtnActive, `${s.sendBtn} w-8 h-8 cursor-pointer rounded-md`)} onClick={() => handleSend()}></div>
-
-  const suggestionListRef = useRef<HTMLDivElement>(null)
-  const [hasScrollbar, setHasScrollbar] = useState(false)
-  useLayoutEffect(() => {
-    if (suggestionListRef.current) {
-      const listDom = suggestionListRef.current
-      const hasScrollbar = listDom.scrollWidth > listDom.clientWidth
-      setHasScrollbar(hasScrollbar)
-    }
-  }, [suggestionList])
-
-  const [voiceInputShow, setVoiceInputShow] = useState(false)
-  const handleVoiceInputShow = () => {
-    (Recorder as any).getPermission().then(() => {
-      setVoiceInputShow(true)
-    }, () => {
-      logError(t('common.voiceInput.notAllow'))
-    })
-  }
-  const handleQueryChangeFromAnswer = useCallback((val: string) => {
-    onQueryChange(val)
-    handleSend(val)
-  }, [])
-  const handleAnnotationEdited = useCallback((query: string, answer: string, index: number) => {
-    onChatListChange?.(chatList.map((item, i) => {
-      if (i === index - 1) {
-        return {
-          ...item,
-          content: query,
-        }
-      }
-      if (i === index) {
-        return {
-          ...item,
-          annotation: {
-            ...item.annotation,
-            logAnnotation: {
-              ...item.annotation?.logAnnotation,
-              content: answer,
-            },
-          } as any,
-        }
-      }
-      return item
-    }))
-  }, [chatList])
-  const handleAnnotationAdded = useCallback((annotationId: string, authorName: string, query: string, answer: string, index: number) => {
-    onChatListChange?.(chatList.map((item, i) => {
-      if (i === index - 1) {
-        return {
-          ...item,
-          content: query,
-        }
-      }
-      if (i === index) {
-        const answerItem = {
-          ...item,
-          content: item.content,
-          annotation: {
-            id: annotationId,
-            authorName,
-            logAnnotation: {
-              content: answer,
-              account: {
-                id: '',
-                name: authorName,
-                email: '',
-              },
-            },
-          } as Annotation,
-        }
-        return answerItem
-      }
-      return item
-    }))
-  }, [chatList])
-  const handleAnnotationRemoved = useCallback((index: number) => {
-    onChatListChange?.(chatList.map((item, i) => {
-      if (i === index) {
-        return {
-          ...item,
-          content: item.content,
-          annotation: undefined,
-        }
-      }
-      return item
-    }))
-  }, [chatList])
-
-  return (
-    <div className={cn('px-3.5', 'h-full')}>
-      {isShowConfigElem && (configElem || null)}
-      {/* Chat List */}
-      <div className={cn((isShowConfigElem && configElem) ? 'h-0' : 'h-full', 'space-y-[30px]')}>
-        {chatList.map((item, index) => {
-          if (item.isAnswer) {
-            const isLast = item.id === chatList[chatList.length - 1].id
-            const citation = item.citation
-            return <Answer
-              key={item.id}
-              item={item}
-              index={index}
-              onQueryChange={handleQueryChangeFromAnswer}
-              feedbackDisabled={feedbackDisabled}
-              isHideFeedbackEdit={isHideFeedbackEdit}
-              onFeedback={onFeedback}
-              displayScene={displayScene ?? 'web'}
-              isResponding={isResponding && isLast}
-              answerIcon={answerIcon}
-              citation={citation}
-              dataSets={dataSets}
-              isShowCitation={isShowCitation}
-              isShowCitationHitInfo={isShowCitationHitInfo}
-              isShowTextToSpeech={isShowTextToSpeech}
-              supportAnnotation={supportAnnotation}
-              appId={appId}
-              question={chatList[index - 1]?.content}
-              onAnnotationEdited={handleAnnotationEdited}
-              onAnnotationAdded={handleAnnotationAdded}
-              onAnnotationRemoved={handleAnnotationRemoved}
-              allToolIcons={allToolIcons}
-              isShowPromptLog={isShowPromptLog}
-            />
-          }
-          return (
-            <Question
-              key={item.id}
-              id={item.id}
-              content={item.content}
-              more={item.more}
-              useCurrentUserAvatar={useCurrentUserAvatar}
-              item={item}
-              isShowPromptLog={isShowPromptLog}
-              isResponding={isResponding}
-            />
-          )
-        })}
-      </div>
-      {!isHideSendInput && (
-        <div className={cn(!feedbackDisabled && '!left-3.5 !right-3.5', 'absolute z-10 bottom-0 left-0 right-0')}>
-          {/* Thinking is sync and can not be stopped */}
-          {(isResponding && canStopResponding && ((!!chatList[chatList.length - 1]?.content) || (chatList[chatList.length - 1]?.agent_thoughts && chatList[chatList.length - 1].agent_thoughts!.length > 0))) && (
-            <div className='flex justify-center mb-4'>
-              <Button className='flex items-center space-x-1 bg-white' onClick={() => abortResponding?.()}>
-                {stopIcon}
-                <span className='text-xs text-gray-500 font-normal'>{t('appDebug.operation.stopResponding')}</span>
-              </Button>
-            </div>
-          )}
-          {isShowSuggestion && (
-            <div className='pt-2'>
-              <div className='flex items-center justify-center mb-2.5'>
-                <div className='grow h-[1px]'
-                  style={{
-                    background: 'linear-gradient(270deg, #F3F4F6 0%, rgba(243, 244, 246, 0) 100%)',
-                  }}></div>
-                <div className='shrink-0 flex items-center px-3 space-x-1'>
-                  {TryToAskIcon}
-                  <span className='text-xs text-gray-500 font-medium'>{t('appDebug.feature.suggestedQuestionsAfterAnswer.tryToAsk')}</span>
-                </div>
-                <div className='grow h-[1px]'
-                  style={{
-                    background: 'linear-gradient(270deg, rgba(243, 244, 246, 0) 0%, #F3F4F6 100%)',
-                  }}></div>
-              </div>
-              {/* has scrollbar would hide part of first item */}
-              <div ref={suggestionListRef} className={cn(!hasScrollbar && 'justify-center', 'flex overflow-x-auto pb-2')}>
-                {suggestionList?.map((item, index) => (
-                  <div key={item} className='shrink-0 flex justify-center mr-2'>
-                    <Button
-                      key={index}
-                      onClick={() => onQueryChange(item)}
-                    >
-                      <span className='text-primary-600 text-xs font-medium'>{item}</span>
-                    </Button>
-                  </div>
-                ))}
-              </div>
-            </div>
-          )}
-          <div className='relative'>
-            <div className={cn('relative p-[5.5px] max-h-[150px] bg-white border-[1.5px] border-gray-200 rounded-xl overflow-y-auto', isDragActive && 'border-primary-600')}>
-              {visionConfig?.enabled && (
-                <>
-                  <div className='absolute bottom-2 left-2 flex items-center'>
-                    <ChatImageUploader
-                      settings={visionConfig}
-                      onUpload={onUpload}
-                      disabled={files.length >= visionConfig.number_limits}
-                    />
-                    <div className='mx-1 w-[1px] h-4 bg-black/5' />
-                  </div>
-                  <div className='pl-[52px]'>
-                    <ImageList
-                      list={files}
-                      onRemove={onRemove}
-                      onReUpload={onReUpload}
-                      onImageLinkLoadSuccess={onImageLinkLoadSuccess}
-                      onImageLinkLoadError={onImageLinkLoadError}
-                    />
-                  </div>
-                </>
-              )}
-              <Textarea
-                className={`
-                  block w-full px-2 pr-[118px] py-[7px] leading-5 max-h-none text-sm text-gray-700 outline-none appearance-none resize-none
-                  ${visionConfig?.enabled && 'pl-12'}
-                `}
-                value={query}
-                onChange={handleContentChange}
-                onKeyUp={handleKeyUp}
-                onKeyDown={handleKeyDown}
-                onPaste={onPaste}
-                onDragEnter={onDragEnter}
-                onDragLeave={onDragLeave}
-                onDragOver={onDragOver}
-                onDrop={onDrop}
-                autoSize
-              />
-            </div>
-            <div className="absolute bottom-2 right-2 flex items-center h-8">
-              <div className={`${s.count} mr-4 h-5 leading-5 text-sm bg-gray-50 text-gray-500`}>{query.trim().length}</div>
-              {
-                query
-                  ? (
-                    <div className='flex justify-center items-center w-8 h-8 cursor-pointer hover:bg-gray-100 rounded-lg' onClick={() => onQueryChange('')}>
-                      <XCircle className='w-4 h-4 text-[#98A2B3]' />
-                    </div>
-                  )
-                  : isShowSpeechToText
-                    ? (
-                      <div
-                        className='group flex justify-center items-center w-8 h-8 hover:bg-primary-50 rounded-lg cursor-pointer'
-                        onClick={handleVoiceInputShow}
-                      >
-                        <Microphone01 className='block w-4 h-4 text-gray-500 group-hover:hidden' />
-                        <Microphone01Solid className='hidden w-4 h-4 text-primary-600 group-hover:block' />
-                      </div>
-                    )
-                    : null
-              }
-              <div className='mx-2 w-[1px] h-4 bg-black opacity-5' />
-              {isMobile
-                ? sendBtn
-                : (
-                  <TooltipPlus
-                    popupContent={
-                      <div>
-                        <div>{t('common.operation.send')} Enter</div>
-                        <div>{t('common.operation.lineBreak')} Shift Enter</div>
-                      </div>
-                    }
-                  >
-                    {sendBtn}
-                  </TooltipPlus>
-                )}
-            </div>
-            {voiceInputShow && (
-              <VoiceInput
-                onCancel={() => setVoiceInputShow(false)}
-                onConverted={text => onQueryChange(text)}
-              />
-            )}
-          </div>
-          {customDisclaimer && <div className='text-xs text-gray-500 mt-1 text-center'>
-            {customDisclaimer}
-          </div>}
-        </div>
-      )}
-    </div>
-  )
-}
-export default React.memo(Chat)

+ 0 - 23
web/app/components/app/chat/more-info/index.tsx

@@ -1,23 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React from 'react'
-import { useTranslation } from 'react-i18next'
-import type { MessageMore } from '../type'
-import { formatNumber } from '@/utils/format'
-
-export type IMoreInfoProps = {
-  more: MessageMore
-  isQuestion: boolean
-  className?: string
-}
-
-const MoreInfo: FC<IMoreInfoProps> = ({ more, isQuestion, className }) => {
-  const { t } = useTranslation()
-  return (<div className={`mt-1 w-full text-xs text-gray-400 ${isQuestion ? 'mr-2 text-right ' : 'pl-2 text-left float-right'} ${className}`}>
-    <span className='mr-2'>{`${t('appLog.detail.timeConsuming')} ${more.latency}${t('appLog.detail.second')}`}</span>
-    <span className='mr-2'>{`${t('appLog.detail.tokenCost')} ${formatNumber(more.tokens)}`}</span>
-    <span className='mr-2'>·</span>
-    <span>{more.time}</span>
-  </div>)
-}
-export default React.memo(MoreInfo)

+ 0 - 14
web/app/components/app/chat/operation/index.tsx

@@ -1,14 +0,0 @@
-'use client'
-import React from 'react'
-
-const OperationBtn = ({ innerContent, onClick, className }: { innerContent: React.ReactNode; onClick?: () => void; className?: string }) => (
-  <div
-    className={`relative box-border flex items-center justify-center h-7 w-7 p-0.5 rounded-lg bg-white cursor-pointer text-gray-500 hover:text-gray-800 ${className ?? ''}`}
-    style={{ boxShadow: '0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.05)' }}
-    onClick={onClick && onClick}
-  >
-    {innerContent}
-  </div>
-)
-
-export default OperationBtn

+ 0 - 52
web/app/components/app/chat/question/index.tsx

@@ -1,52 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React, { useRef } from 'react'
-import { useContext } from 'use-context-selector'
-import s from '../style.module.css'
-import type { IChatItem } from '../type'
-import MoreInfo from '../more-info'
-import AppContext from '@/context/app-context'
-import { Markdown } from '@/app/components/base/markdown'
-import ImageGallery from '@/app/components/base/image-gallery'
-
-type IQuestionProps = Pick<IChatItem, 'id' | 'content' | 'more' | 'useCurrentUserAvatar'> & {
-  isShowPromptLog?: boolean
-  item: IChatItem
-  isResponding?: boolean
-}
-
-const Question: FC<IQuestionProps> = ({ id, content, more, useCurrentUserAvatar, isShowPromptLog, item }) => {
-  const { userProfile } = useContext(AppContext)
-  const userName = userProfile?.name
-  const ref = useRef(null)
-  const imgSrcs = item.message_files?.map(item => item.url)
-
-  return (
-    <div className={`flex items-start justify-end ${isShowPromptLog && 'first-of-type:pt-[14px]'}`} key={id} ref={ref}>
-      <div className={s.questionWrapWrap}>
-
-        <div className={`${s.question} group relative text-sm text-gray-900`}>
-          <div
-            className={'mr-2 py-3 px-4 bg-blue-500 rounded-tl-2xl rounded-b-2xl'}
-          >
-            {imgSrcs && imgSrcs.length > 0 && (
-              <ImageGallery srcs={imgSrcs} />
-            )}
-            <Markdown content={content} />
-          </div>
-        </div>
-        {more && <MoreInfo more={more} isQuestion={true} />}
-      </div>
-      {useCurrentUserAvatar
-        ? (
-          <div className='w-10 h-10 shrink-0 leading-10 text-center mr-2 rounded-full bg-primary-600 text-white'>
-            {userName?.[0].toLocaleUpperCase()}
-          </div>
-        )
-        : (
-          <div className={`${s.questionIcon} w-10 h-10 shrink-0 `}></div>
-        )}
-    </div>
-  )
-}
-export default React.memo(Question)

+ 0 - 136
web/app/components/app/chat/style.module.css

@@ -1,136 +0,0 @@
-.answerIcon {
-  position: relative;
-  background: url(./icons/robot.svg) 100%/100%;
-}
-
-.typeingIcon {
-  position: absolute;
-  top: 0px;
-  left: 0px;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  width: 16px;
-  height: 16px;
-  background: #FFFFFF;
-  box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
-  border-radius: 16px;
-}
-
-
-.questionIcon {
-  background: url(./icons/default-avatar.jpg);
-  background-size: contain;
-  border-radius: 50%;
-}
-
-.answer::before,
-.question::before {
-  content: '';
-  position: absolute;
-  top: 0;
-  width: 8px;
-  height: 12px;
-}
-
-.answer::before {
-  left: 0;
-  background: url(./icons/answer.svg) no-repeat;
-}
-
-.copyBtn,
-.playBtn,
-.annotationBtn {
-  display: none;
-}
-
-pre:hover .copyBtn {
-  display: block;
-}
-
-.answerWrapWrap,
-.questionWrapWrap {
-  width: 0;
-  flex-grow: 1;
-}
-
-.questionWrapWrap {
-  display: flex;
-  justify-content: flex-end;
-}
-
-.question {
-  display: inline-block;
-  max-width: 100%;
-}
-
-.answer {
-  display: inline-block;
-  max-width: 100%;
-}
-
-.answerWrap:hover .copyBtn,
-.answerWrap:hover .playBtn,
-.answerWrap:hover .annotationBtn {
-  display: block;
-}
-
-.answerWrap:hover .hasAnnotationBtn {
-  display: none;
-}
-
-.answerWrap .itemOperation {
-  display: none;
-}
-
-.answerWrap:hover .itemOperation {
-  display: flex;
-}
-
-.question::before {
-  right: 0;
-  background: url(./icons/question.svg) no-repeat;
-}
-
-.textArea {
-  padding-top: 13px;
-  padding-bottom: 13px;
-  padding-right: 130px;
-  border-radius: 12px;
-  line-height: 20px;
-  background-color: #fff;
-}
-
-.textArea:hover {
-  background-color: #fff;
-}
-
-/* .textArea:focus {
-  box-shadow: 0px 3px 15px -3px rgba(0, 0, 0, 0.1), 0px 4px 6px rgba(0, 0, 0, 0.05);
-} */
-
-.count {
-  /* display: none; */
-  padding: 0 2px;
-}
-
-.sendBtn {
-  background: url(./icons/send.svg) center center no-repeat;
-}
-
-.sendBtnActive {
-  background-image: url(./icons/send-active.svg);
-}
-
-.sendBtn:hover {
-  background-image: url(./icons/send-active.svg);
-  background-color: #EBF5FF;
-}
-
-.textArea:focus+div .count {
-  display: block;
-}
-
-.textArea:focus+div .sendBtn {
-  background-image: url(./icons/send-active.svg);
-}

+ 0 - 7
web/app/components/app/chat/thought/style.module.css

@@ -1,7 +0,0 @@
-.wrap {
-  background-color: rgba(255, 255, 255, 0.92);
-}
-
-.wrapHoverEffect:hover{
-  box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.06), 0px 1px 3px 0px rgba(16, 24, 40, 0.1);
-}

+ 2 - 2
web/app/components/app/configuration/debug/debug-with-multiple-model/chat-item.tsx

@@ -20,7 +20,7 @@ import type { OnSend } from '@/app/components/base/chat/types'
 import { useEventEmitterContextContext } from '@/context/event-emitter'
 import { useProviderContext } from '@/context/provider-context'
 import {
-  fetchConvesationMessages,
+  fetchConversationMessages,
   fetchSuggestedQuestions,
   stopChatMessageResponding,
 } from '@/service/debug'
@@ -89,7 +89,7 @@ const ChatItem: FC<ChatItemProps> = ({
       `apps/${appId}/chat-messages`,
       data,
       {
-        onGetConvesationMessages: (conversationId, getAbortController) => fetchConvesationMessages(appId, conversationId, getAbortController),
+        onGetConvesationMessages: (conversationId, getAbortController) => fetchConversationMessages(appId, conversationId, getAbortController),
         onGetSuggestedQuestions: (responseItemId, getAbortController) => fetchSuggestedQuestions(appId, responseItemId, getAbortController),
       },
     )

+ 2 - 2
web/app/components/app/configuration/debug/debug-with-single-model/index.tsx

@@ -15,7 +15,7 @@ import { useDebugConfigurationContext } from '@/context/debug-configuration'
 import type { OnSend } from '@/app/components/base/chat/types'
 import { useProviderContext } from '@/context/provider-context'
 import {
-  fetchConvesationMessages,
+  fetchConversationMessages,
   fetchSuggestedQuestions,
   stopChatMessageResponding,
 } from '@/service/debug'
@@ -94,7 +94,7 @@ const DebugWithSingleModel = forwardRef<DebugWithSingleModelRefType, DebugWithSi
       `apps/${appId}/chat-messages`,
       data,
       {
-        onGetConvesationMessages: (conversationId, getAbortController) => fetchConvesationMessages(appId, conversationId, getAbortController),
+        onGetConvesationMessages: (conversationId, getAbortController) => fetchConversationMessages(appId, conversationId, getAbortController),
         onGetSuggestedQuestions: (responseItemId, getAbortController) => fetchSuggestedQuestions(appId, responseItemId, getAbortController),
       },
     )

+ 112 - 45
web/app/components/app/log/list.tsx

@@ -1,6 +1,6 @@
 'use client'
 import type { FC } from 'react'
-import React, { useEffect, useRef, useState } from 'react'
+import React, { useCallback, useEffect, useRef, useState } from 'react'
 import useSWR from 'swr'
 import {
   HandThumbDownIcon,
@@ -8,6 +8,7 @@ import {
   InformationCircleIcon,
   XMarkIcon,
 } from '@heroicons/react/24/outline'
+import { RiEditFill } from '@remixicon/react'
 import { get } from 'lodash-es'
 import InfiniteScroll from 'react-infinite-scroll-component'
 import dayjs from 'dayjs'
@@ -20,14 +21,13 @@ import cn from 'classnames'
 import s from './style.module.css'
 import VarPanel from './var-panel'
 import { randomString } from '@/utils'
-import { EditIconSolid } from '@/app/components/app/chat/icon-component'
-import type { FeedbackFunc, Feedbacktype, IChatItem, SubmitAnnotationFunc } from '@/app/components/app/chat/type'
-import type { ChatConversationFullDetailResponse, ChatConversationGeneralDetail, ChatConversationsResponse, ChatMessage, ChatMessagesRequest, CompletionConversationFullDetailResponse, CompletionConversationGeneralDetail, CompletionConversationsResponse, LogAnnotation } from '@/models/log'
+import type { FeedbackFunc, Feedbacktype, IChatItem, SubmitAnnotationFunc } from '@/app/components/base/chat/chat/type'
+import type { Annotation, ChatConversationFullDetailResponse, ChatConversationGeneralDetail, ChatConversationsResponse, ChatMessage, ChatMessagesRequest, CompletionConversationFullDetailResponse, CompletionConversationGeneralDetail, CompletionConversationsResponse, LogAnnotation } from '@/models/log'
 import type { App } from '@/types/app'
 import Loading from '@/app/components/base/loading'
 import Drawer from '@/app/components/base/drawer'
 import Popover from '@/app/components/base/popover'
-import Chat from '@/app/components/app/chat'
+import Chat from '@/app/components/base/chat/chat'
 import Tooltip from '@/app/components/base/tooltip'
 import { ToastContext } from '@/app/components/base/toast'
 import { fetchChatConversationDetail, fetchChatMessages, fetchCompletionConversationDetail, updateLogMessageAnnotations, updateLogMessageFeedbacks } from '@/service/log'
@@ -38,8 +38,6 @@ import ModelName from '@/app/components/header/account-setting/model-provider-pa
 import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
 import TextGeneration from '@/app/components/app/text-generate/item'
 import { addFileInfos, sortAgentSorts } from '@/app/components/tools/utils'
-import AgentLogModal from '@/app/components/base/agent-log-modal'
-import PromptLogModal from '@/app/components/base/prompt-log-modal'
 import MessageLogModal from '@/app/components/base/message-log-modal'
 import { useStore as useAppStore } from '@/app/components/app/store'
 import { useAppContext } from '@/context/app-context'
@@ -166,13 +164,9 @@ function DetailPanel<T extends ChatConversationFullDetailResponse | CompletionCo
   const { userProfile: { timezone } } = useAppContext()
   const { formatTime } = useTimestamp()
   const { onClose, appDetail } = useContext(DrawerContext)
-  const { currentLogItem, setCurrentLogItem, showPromptLogModal, setShowPromptLogModal, showAgentLogModal, setShowAgentLogModal, showMessageLogModal, setShowMessageLogModal, currentLogModalActiveTab } = useAppStore(useShallow(state => ({
+  const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal, currentLogModalActiveTab } = useAppStore(useShallow(state => ({
     currentLogItem: state.currentLogItem,
     setCurrentLogItem: state.setCurrentLogItem,
-    showPromptLogModal: state.showPromptLogModal,
-    setShowPromptLogModal: state.setShowPromptLogModal,
-    showAgentLogModal: state.showAgentLogModal,
-    setShowAgentLogModal: state.setShowAgentLogModal,
     showMessageLogModal: state.showMessageLogModal,
     setShowMessageLogModal: state.setShowMessageLogModal,
     currentLogModalActiveTab: state.currentLogModalActiveTab,
@@ -218,6 +212,72 @@ function DetailPanel<T extends ChatConversationFullDetailResponse | CompletionCo
     }
   }
 
+  const handleAnnotationEdited = useCallback((query: string, answer: string, index: number) => {
+    setItems(items.map((item, i) => {
+      if (i === index - 1) {
+        return {
+          ...item,
+          content: query,
+        }
+      }
+      if (i === index) {
+        return {
+          ...item,
+          annotation: {
+            ...item.annotation,
+            logAnnotation: {
+              ...item.annotation?.logAnnotation,
+              content: answer,
+            },
+          } as any,
+        }
+      }
+      return item
+    }))
+  }, [items])
+  const handleAnnotationAdded = useCallback((annotationId: string, authorName: string, query: string, answer: string, index: number) => {
+    setItems(items.map((item, i) => {
+      if (i === index - 1) {
+        return {
+          ...item,
+          content: query,
+        }
+      }
+      if (i === index) {
+        const answerItem = {
+          ...item,
+          content: item.content,
+          annotation: {
+            id: annotationId,
+            authorName,
+            logAnnotation: {
+              content: answer,
+              account: {
+                id: '',
+                name: authorName,
+                email: '',
+              },
+            },
+          } as Annotation,
+        }
+        return answerItem
+      }
+      return item
+    }))
+  }, [items])
+  const handleAnnotationRemoved = useCallback((index: number) => {
+    setItems(items.map((item, i) => {
+      if (i === index) {
+        return {
+          ...item,
+          content: item.content,
+          annotation: undefined,
+        }
+      }
+      return item
+    }))
+  }, [items])
+
   useEffect(() => {
     if (appDetail?.id && detail.id && appDetail?.mode !== 'completion')
       fetchData()
@@ -374,24 +434,36 @@ function DetailPanel<T extends ChatConversationFullDetailResponse | CompletionCo
             isShowTextToSpeech
             appId={appDetail?.id}
             varList={varList}
+            siteInfo={null}
           />
         </div>
         : items.length < 8
-          ? <div className="px-2.5 pt-4 mb-4">
+          ? <div className="pt-4 mb-4">
             <Chat
+              config={{
+                appId: appDetail?.id,
+                text_to_speech: {
+                  enabled: true,
+                },
+                supportAnnotation: true,
+                annotation_reply: {
+                  enabled: true,
+                },
+                supportFeedback: true,
+              } as any}
               chatList={items}
-              isHideSendInput={true}
+              onAnnotationAdded={handleAnnotationAdded}
+              onAnnotationEdited={handleAnnotationEdited}
+              onAnnotationRemoved={handleAnnotationRemoved}
               onFeedback={onFeedback}
-              displayScene='console'
-              isShowPromptLog
-              supportAnnotation
-              isShowTextToSpeech
-              appId={appDetail?.id}
-              onChatListChange={setItems}
+              noChatInput
+              showPromptLog
+              hideProcessDetail
+              chatContainerInnerClassName='px-6'
             />
           </div>
           : <div
-            className="px-2.5 py-4"
+            className="py-4"
             id="scrollableDiv"
             style={{
               height: 1000, // Specify a value
@@ -422,35 +494,30 @@ function DetailPanel<T extends ChatConversationFullDetailResponse | CompletionCo
               inverse={true}
             >
               <Chat
+                config={{
+                  app_id: appDetail?.id,
+                  text_to_speech: {
+                    enabled: true,
+                  },
+                  supportAnnotation: true,
+                  annotation_reply: {
+                    enabled: true,
+                  },
+                  supportFeedback: true,
+                } as any}
                 chatList={items}
-                isHideSendInput={true}
+                onAnnotationAdded={handleAnnotationAdded}
+                onAnnotationEdited={handleAnnotationEdited}
+                onAnnotationRemoved={handleAnnotationRemoved}
                 onFeedback={onFeedback}
-                displayScene='console'
-                isShowPromptLog
+                noChatInput
+                showPromptLog
+                hideProcessDetail
+                chatContainerInnerClassName='px-6'
               />
             </InfiniteScroll>
           </div>
       }
-      {showPromptLogModal && (
-        <PromptLogModal
-          width={width}
-          currentLogItem={currentLogItem}
-          onCancel={() => {
-            setCurrentLogItem()
-            setShowPromptLogModal(false)
-          }}
-        />
-      )}
-      {showAgentLogModal && (
-        <AgentLogModal
-          width={width}
-          currentLogItem={currentLogItem}
-          onCancel={() => {
-            setCurrentLogItem()
-            setShowAgentLogModal(false)
-          }}
-        />
-      )}
       {showMessageLogModal && (
         <MessageLogModal
           width={width}
@@ -575,7 +642,7 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
       <Tooltip
         htmlContent={
           <span className='text-xs text-gray-500 inline-flex items-center'>
-            <EditIconSolid className='mr-1' />{`${t('appLog.detail.annotationTip', { user: annotation?.account?.name })} ${formatTime(annotation?.created_at || dayjs().unix(), 'MM-DD hh:mm A')}`}
+            <RiEditFill className='w-3 h-3 mr-1' />{`${t('appLog.detail.annotationTip', { user: annotation?.account?.name })} ${formatTime(annotation?.created_at || dayjs().unix(), 'MM-DD hh:mm A')}`}
           </span>
         }
         className={(isHighlight && !isChatMode) ? '' : '!hidden'}

+ 1 - 1
web/app/components/app/overview/embedded/index.tsx

@@ -4,7 +4,7 @@ import cn from 'classnames'
 import copy from 'copy-to-clipboard'
 import style from './style.module.css'
 import Modal from '@/app/components/base/modal'
-import copyStyle from '@/app/components/app/chat/copy-btn/style.module.css'
+import copyStyle from '@/app/components/base/copy-btn/style.module.css'
 import Tooltip from '@/app/components/base/tooltip'
 import { useAppContext } from '@/context/app-context'
 import { IS_CE_EDITION } from '@/config'

+ 1 - 1
web/app/components/app/store.ts

@@ -1,6 +1,6 @@
 import { create } from 'zustand'
 import type { App } from '@/types/app'
-import type { IChatItem } from '@/app/components/app/chat/type'
+import type { IChatItem } from '@/app/components/base/chat/chat/type'
 
 type State = {
   appDetail?: App

+ 1 - 1
web/app/components/app/text-generate/item/index.tsx

@@ -16,7 +16,7 @@ import { Markdown } from '@/app/components/base/markdown'
 import Loading from '@/app/components/base/loading'
 import Toast from '@/app/components/base/toast'
 import AudioBtn from '@/app/components/base/audio-btn'
-import type { Feedbacktype } from '@/app/components/app/chat/type'
+import type { Feedbacktype } from '@/app/components/base/chat/chat/type'
 import { fetchMoreLikeThis, updateFeedback } from '@/service/share'
 import { File02 } from '@/app/components/base/icons/src/vender/line/files'
 import { Bookmark } from '@/app/components/base/icons/src/vender/line/general'

+ 1 - 1
web/app/components/base/agent-log-modal/detail.tsx

@@ -12,7 +12,7 @@ import Loading from '@/app/components/base/loading'
 import { fetchAgentLogDetail } from '@/service/log'
 import type { AgentIteration, AgentLogDetailResponse } from '@/models/log'
 import { useStore as useAppStore } from '@/app/components/app/store'
-import type { IChatItem } from '@/app/components/app/chat/type'
+import type { IChatItem } from '@/app/components/base/chat/chat/type'
 
 export type AgentLogDetailProps = {
   activeTab?: 'DETAIL' | 'TRACING'

+ 1 - 1
web/app/components/base/agent-log-modal/index.tsx

@@ -5,7 +5,7 @@ import { RiCloseLine } from '@remixicon/react'
 import { useEffect, useRef, useState } from 'react'
 import { useClickAway } from 'ahooks'
 import AgentLogDetail from './detail'
-import type { IChatItem } from '@/app/components/app/chat/type'
+import type { IChatItem } from '@/app/components/base/chat/chat/type'
 
 type AgentLogModalProps = {
   currentLogItem?: IChatItem

+ 2 - 2
web/app/components/base/chat/chat-with-history/config-panel/index.tsx

@@ -7,7 +7,7 @@ import AppIcon from '@/app/components/base/app-icon'
 import { MessageDotsCircle } from '@/app/components/base/icons/src/vender/solid/communication'
 import { Edit02 } from '@/app/components/base/icons/src/vender/line/general'
 import { Star06 } from '@/app/components/base/icons/src/vender/solid/shapes'
-import { FootLogo } from '@/app/components/share/chat/welcome/massive-component'
+import LogoSite from '@/app/components/base/logo/logo-site'
 
 const ConfigPanel = () => {
   const { t } = useTranslation()
@@ -153,7 +153,7 @@ const ConfigPanel = () => {
                       {
                         customConfig?.replace_webapp_logo
                           ? <img src={customConfig?.replace_webapp_logo} alt='logo' className='block w-auto h-5' />
-                          : <FootLogo />
+                          : <LogoSite className='!h-5' />
                       }
                     </div>
                   </div>

+ 1 - 1
web/app/components/base/chat/chat-with-history/sidebar/index.tsx

@@ -10,7 +10,7 @@ import Button from '@/app/components/base/button'
 import { Edit05 } from '@/app/components/base/icons/src/vender/line/general'
 import type { ConversationItem } from '@/models/share'
 import Confirm from '@/app/components/base/confirm'
-import RenameModal from '@/app/components/share/chat/sidebar/rename-modal'
+import RenameModal from '@/app/components/base/chat/chat-with-history/sidebar/rename-modal'
 
 const Sidebar = () => {
   const { t } = useTranslation()

+ 0 - 0
web/app/components/share/chat/sidebar/rename-modal/index.tsx → web/app/components/base/chat/chat-with-history/sidebar/rename-modal.tsx


+ 1 - 1
web/app/components/base/chat/chat/answer/agent-content.tsx

@@ -5,7 +5,7 @@ import type {
   VisionFile,
 } from '../../types'
 import { Markdown } from '@/app/components/base/markdown'
-import Thought from '@/app/components/app/chat/thought'
+import Thought from '@/app/components/base/chat/chat/thought'
 import ImageGallery from '@/app/components/base/image-gallery'
 import type { Emoji } from '@/app/components/tools/types'
 

+ 2 - 2
web/app/components/base/chat/chat/answer/index.tsx

@@ -16,8 +16,8 @@ import More from './more'
 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 LoadingAnim from '@/app/components/app/chat/loading-anim'
-import Citation from '@/app/components/app/chat/citation'
+import LoadingAnim from '@/app/components/base/chat/chat/loading-anim'
+import Citation from '@/app/components/base/chat/chat/citation'
 import { EditTitle } from '@/app/components/app/annotation/edit-annotation-modal/edit-item'
 import type { Emoji } from '@/app/components/tools/types'
 import type { AppData } from '@/models/share'

+ 2 - 2
web/app/components/base/chat/chat/answer/operation.tsx

@@ -8,7 +8,7 @@ import cn from 'classnames'
 import { useTranslation } from 'react-i18next'
 import type { ChatItem } from '../../types'
 import { useChatContext } from '../context'
-import CopyBtn from '@/app/components/app/chat/copy-btn'
+import CopyBtn from '@/app/components/base/copy-btn'
 import { MessageFast } from '@/app/components/base/icons/src/vender/solid/communication'
 import AudioBtn from '@/app/components/base/audio-btn'
 import AnnotationCtrlBtn from '@/app/components/app/configuration/toolbox/annotation/annotation-ctrl-btn'
@@ -18,7 +18,7 @@ import {
   ThumbsUp,
 } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
 import TooltipPlus from '@/app/components/base/tooltip-plus'
-import Log from '@/app/components/app/chat/log'
+import Log from '@/app/components/base/chat/chat/log'
 
 type OperationProps = {
   item: ChatItem

+ 0 - 0
web/app/components/app/chat/citation/index.tsx → web/app/components/base/chat/chat/citation/index.tsx


+ 0 - 0
web/app/components/app/chat/citation/popup.tsx → web/app/components/base/chat/chat/citation/popup.tsx


+ 0 - 0
web/app/components/app/chat/citation/progress-tooltip.tsx → web/app/components/base/chat/chat/citation/progress-tooltip.tsx


+ 0 - 0
web/app/components/app/chat/citation/tooltip.tsx → web/app/components/base/chat/chat/citation/tooltip.tsx


+ 3 - 0
web/app/components/base/chat/chat/index.tsx

@@ -263,6 +263,9 @@ const Chat: FC<ChatProps> = ({
                 />
               )
             }
+            {appData && appData.site.custom_disclaimer && <div className='text-xs text-gray-500 mt-1 text-center'>
+              {appData.site.custom_disclaimer}
+            </div>}
           </div>
         </div>
         {showPromptLogModal && (

+ 0 - 0
web/app/components/app/chat/loading-anim/index.tsx → web/app/components/base/chat/chat/loading-anim/index.tsx


+ 0 - 0
web/app/components/app/chat/loading-anim/style.module.css → web/app/components/base/chat/chat/loading-anim/style.module.css


+ 1 - 1
web/app/components/app/chat/log/index.tsx → web/app/components/base/chat/chat/log/index.tsx

@@ -1,7 +1,7 @@
 import type { FC } from 'react'
 import { useTranslation } from 'react-i18next'
 import { File02 } from '@/app/components/base/icons/src/vender/line/files'
-import type { IChatItem } from '@/app/components/app/chat/type'
+import type { IChatItem } from '@/app/components/base/chat/chat/type'
 import { useStore as useAppStore } from '@/app/components/app/store'
 
 type LogProps = {

+ 1 - 1
web/app/components/app/chat/thought/index.tsx → web/app/components/base/chat/chat/thought/index.tsx

@@ -3,7 +3,7 @@ import type { FC } from 'react'
 import React from 'react'
 import { useContext } from 'use-context-selector'
 import type { ThoughtItem, ToolInfoInThought } from '../type'
-import Tool from '@/app/components/app/chat/thought/tool'
+import Tool from '@/app/components/base/chat/chat/thought/tool'
 import type { Emoji } from '@/app/components/tools/types'
 
 import I18n from '@/context/i18n'

+ 0 - 0
web/app/components/app/chat/thought/panel.tsx → web/app/components/base/chat/chat/thought/panel.tsx


+ 0 - 0
web/app/components/app/chat/thought/tool.tsx → web/app/components/base/chat/chat/thought/tool.tsx


+ 1 - 1
web/app/components/app/chat/type.ts → web/app/components/base/chat/chat/type.ts

@@ -1,4 +1,4 @@
-import type { TypeWithI18N } from '../../header/account-setting/model-provider-page/declarations'
+import type { TypeWithI18N } from '@/app/components/header/account-setting/model-provider-page/declarations'
 import type { Annotation, MessageRating } from '@/models/log'
 import type { VisionFile } from '@/types/app'
 

+ 2 - 2
web/app/components/base/chat/embedded-chatbot/config-panel/index.tsx

@@ -8,7 +8,7 @@ import AppIcon from '@/app/components/base/app-icon'
 import { MessageDotsCircle } from '@/app/components/base/icons/src/vender/solid/communication'
 import { Edit02 } from '@/app/components/base/icons/src/vender/line/general'
 import { Star06 } from '@/app/components/base/icons/src/vender/solid/shapes'
-import { FootLogo } from '@/app/components/share/chat/welcome/massive-component'
+import LogoSite from '@/app/components/base/logo/logo-site'
 
 const ConfigPanel = () => {
   const { t } = useTranslation()
@@ -154,7 +154,7 @@ const ConfigPanel = () => {
                       {
                         customConfig?.replace_webapp_logo
                           ? <img src={customConfig?.replace_webapp_logo} alt='logo' className='block w-auto h-5' />
-                          : <FootLogo />
+                          : <LogoSite className='!h-5' />
                       }
                     </div>
                   </div>

+ 2 - 7
web/app/components/base/chat/embedded-chatbot/header.tsx

@@ -1,24 +1,19 @@
 import type { FC } from 'react'
 import React from 'react'
+import { RiRefreshLine } from '@remixicon/react'
 import { useTranslation } from 'react-i18next'
-// import AppIcon from '@/app/components/base/app-icon'
-import { ReplayIcon } from '@/app/components/app/chat/icon-component'
 import Tooltip from '@/app/components/base/tooltip'
 
 export type IHeaderProps = {
   isMobile?: boolean
   customerIcon?: React.ReactNode
   title: string
-  // icon: string
-  // icon_background: string
   onCreateNewChat?: () => void
 }
 const Header: FC<IHeaderProps> = ({
   isMobile,
   customerIcon,
   title,
-  // icon,
-  // icon_background,
   onCreateNewChat,
 }) => {
   const { t } = useTranslation()
@@ -48,7 +43,7 @@ const Header: FC<IHeaderProps> = ({
         <div className='flex cursor-pointer hover:rounded-lg hover:bg-black/5 w-8 h-8 items-center justify-center' onClick={() => {
           onCreateNewChat?.()
         }}>
-          <ReplayIcon className="h-4 w-4 text-sm font-bold text-white" />
+          <RiRefreshLine className="h-4 w-4 text-sm font-bold text-white" />
         </div>
       </Tooltip>
     </div>

+ 1 - 1
web/app/components/base/chat/types.ts

@@ -3,7 +3,7 @@ import type {
   VisionFile,
   VisionSettings,
 } from '@/types/app'
-import type { IChatItem } from '@/app/components/app/chat/type'
+import type { IChatItem } from '@/app/components/base/chat/chat/type'
 import type { NodeTracing } from '@/types/workflow'
 import type { WorkflowRunningStatus } from '@/app/components/workflow/types'
 

+ 0 - 0
web/app/components/app/chat/copy-btn/index.tsx → web/app/components/base/copy-btn/index.tsx


+ 0 - 0
web/app/components/app/chat/copy-btn/style.module.css → web/app/components/base/copy-btn/style.module.css


+ 3 - 3
web/app/components/base/markdown.tsx

@@ -9,9 +9,9 @@ import { atelierHeathLight } from 'react-syntax-highlighter/dist/esm/styles/hljs
 import type { RefObject } from 'react'
 import { useEffect, useRef, useState } from 'react'
 import cn from 'classnames'
-import CopyBtn from '@/app/components/app/chat/copy-btn'
-import SVGBtn from '@/app/components/app/chat/svg'
-import Flowchart from '@/app/components/app/chat/mermaid'
+import CopyBtn from '@/app/components/base/copy-btn'
+import SVGBtn from '@/app/components/base/svg'
+import Flowchart from '@/app/components/base/mermaid'
 
 // Available language https://github.com/react-syntax-highlighter/react-syntax-highlighter/blob/master/AVAILABLE_LANGUAGES_HLJS.MD
 const capitalizationLanguageNameMap: Record<string, string> = {

+ 0 - 0
web/app/components/app/chat/mermaid/index.tsx → web/app/components/base/mermaid/index.tsx


+ 1 - 1
web/app/components/base/message-log-modal/index.tsx

@@ -5,7 +5,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'
 import { useBoolean, useClickAway } from 'ahooks'
 import { RiCloseLine } from '@remixicon/react'
 import IterationResultPanel from '../../workflow/run/iteration-result-panel'
-import type { IChatItem } from '@/app/components/app/chat/type'
+import type { IChatItem } from '@/app/components/base/chat/chat/type'
 import Run from '@/app/components/workflow/run'
 import type { NodeTracing } from '@/types/workflow'
 

+ 1 - 1
web/app/components/base/prompt-log-modal/index.tsx

@@ -4,7 +4,7 @@ import { useClickAway } from 'ahooks'
 import { RiCloseLine } from '@remixicon/react'
 import Card from './card'
 import { CopyFeedbackNew } from '@/app/components/base/copy-feedback'
-import type { IChatItem } from '@/app/components/app/chat/type'
+import type { IChatItem } from '@/app/components/base/chat/chat/type'
 
 type PromptLogModalProps = {
   currentLogItem?: IChatItem

+ 0 - 0
web/app/components/app/chat/svg/index.tsx → web/app/components/base/svg/index.tsx


+ 0 - 0
web/app/components/app/chat/svg/style.module.css → web/app/components/base/svg/style.module.css


+ 0 - 13
web/app/components/share/chat/config-scence/index.tsx

@@ -1,13 +0,0 @@
-import type { FC } from 'react'
-import React from 'react'
-import type { IWelcomeProps } from '../welcome'
-import Welcome from '../welcome'
-
-const ConfigSence: FC<IWelcomeProps> = (props) => {
-  return (
-    <div className='mb-5 antialiased font-sans shrink-0'>
-      <Welcome {...props} />
-    </div>
-  )
-}
-export default React.memo(ConfigSence)

+ 0 - 73
web/app/components/share/chat/hooks/use-conversation.ts

@@ -1,73 +0,0 @@
-import { useCallback, useState } from 'react'
-import produce from 'immer'
-import { useGetState } from 'ahooks'
-import type { ConversationItem } from '@/models/share'
-
-const storageConversationIdKey = 'conversationIdInfo'
-
-type ConversationInfoType = Omit<ConversationItem, 'inputs' | 'id'>
-function useConversation() {
-  const [conversationList, setConversationList] = useState<ConversationItem[]>([])
-  const [pinnedConversationList, setPinnedConversationList] = useState<ConversationItem[]>([])
-  const [currConversationId, doSetCurrConversationId, getCurrConversationId] = useGetState<string>('-1')
-  // when set conversation id, we do not have set appId
-  const setCurrConversationId = useCallback((id: string, appId: string, isSetToLocalStroge = true, newConversationName = '') => {
-    doSetCurrConversationId(id)
-    if (isSetToLocalStroge && id !== '-1') {
-      // conversationIdInfo: {[appId1]: conversationId1, [appId2]: conversationId2}
-      const conversationIdInfo = globalThis.localStorage?.getItem(storageConversationIdKey) ? JSON.parse(globalThis.localStorage?.getItem(storageConversationIdKey) || '') : {}
-      conversationIdInfo[appId] = id
-      globalThis.localStorage?.setItem(storageConversationIdKey, JSON.stringify(conversationIdInfo))
-    }
-  }, [doSetCurrConversationId])
-
-  const getConversationIdFromStorage = (appId: string) => {
-    const conversationIdInfo = globalThis.localStorage?.getItem(storageConversationIdKey) ? JSON.parse(globalThis.localStorage?.getItem(storageConversationIdKey) || '') : {}
-    const id = conversationIdInfo[appId]
-    return id
-  }
-
-  const isNewConversation = currConversationId === '-1'
-  // input can be updated by user
-  const [newConversationInputs, setNewConversationInputs] = useState<Record<string, any> | null>(null)
-  const resetNewConversationInputs = () => {
-    if (!newConversationInputs)
-      return
-    setNewConversationInputs(produce(newConversationInputs, (draft) => {
-      Object.keys(draft).forEach((key) => {
-        draft[key] = ''
-      })
-    }))
-  }
-  const [existConversationInputs, setExistConversationInputs] = useState<Record<string, any> | null>(null)
-  const currInputs = isNewConversation ? newConversationInputs : existConversationInputs
-  const setCurrInputs = isNewConversation ? setNewConversationInputs : setExistConversationInputs
-
-  // info is muted
-  const [newConversationInfo, setNewConversationInfo] = useState<ConversationInfoType | null>(null)
-  const [existConversationInfo, setExistConversationInfo] = useState<ConversationInfoType | null>(null)
-  const currConversationInfo = isNewConversation ? newConversationInfo : existConversationInfo
-
-  return {
-    conversationList,
-    setConversationList,
-    pinnedConversationList,
-    setPinnedConversationList,
-    currConversationId,
-    getCurrConversationId,
-    setCurrConversationId,
-    getConversationIdFromStorage,
-    isNewConversation,
-    currInputs,
-    newConversationInputs,
-    existConversationInputs,
-    resetNewConversationInputs,
-    setCurrInputs,
-    currConversationInfo,
-    setNewConversationInfo,
-    existConversationInfo,
-    setExistConversationInfo,
-  }
-}
-
-export default useConversation

+ 0 - 953
web/app/components/share/chat/index.tsx

@@ -1,953 +0,0 @@
-/* eslint-disable @typescript-eslint/no-use-before-define */
-'use client'
-import type { FC } from 'react'
-import React, { useCallback, useEffect, useRef, useState } from 'react'
-import cn from 'classnames'
-import useSWR from 'swr'
-import { useTranslation } from 'react-i18next'
-import { useContext } from 'use-context-selector'
-import produce, { setAutoFreeze } from 'immer'
-import { useBoolean, useGetState } from 'ahooks'
-import AppUnavailable from '../../base/app-unavailable'
-import { checkOrSetAccessToken } from '../utils'
-import { addFileInfos, sortAgentSorts } from '../../tools/utils'
-import useConversation from './hooks/use-conversation'
-import { ToastContext } from '@/app/components/base/toast'
-import Sidebar from '@/app/components/share/chat/sidebar'
-import ConfigSence from '@/app/components/share/chat/config-scence'
-import Header from '@/app/components/share/header'
-import {
-  delConversation,
-  fetchAppInfo,
-  fetchAppMeta,
-  fetchAppParams,
-  fetchChatList,
-  fetchConversations,
-  fetchSuggestedQuestions,
-  generationConversationName,
-  pinConversation,
-  sendChatMessage,
-  stopChatMessageResponding,
-  unpinConversation,
-  updateFeedback,
-} from '@/service/share'
-import type { AppMeta, ConversationItem, SiteInfo } from '@/models/share'
-
-import type {
-  CitationConfig,
-  PromptConfig,
-  SpeechToTextConfig,
-  SuggestedQuestionsAfterAnswerConfig,
-  TextToSpeechConfig,
-} from '@/models/debug'
-import type { Feedbacktype, IChatItem } from '@/app/components/app/chat/type'
-import Chat from '@/app/components/app/chat'
-import { changeLanguage } from '@/i18n/i18next-config'
-import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
-import Loading from '@/app/components/base/loading'
-import { replaceStringWithValues } from '@/app/components/app/configuration/prompt-value-panel'
-import { userInputsFormToPromptVariables } from '@/utils/model-config'
-import type { InstalledApp } from '@/models/explore'
-import Confirm from '@/app/components/base/confirm'
-import type { VisionFile, VisionSettings } from '@/types/app'
-import { Resolution, TransferMethod } from '@/types/app'
-import { fetchFileUploadConfig } from '@/service/common'
-import type { Annotation as AnnotationType } from '@/models/log'
-
-export type IMainProps = {
-  isInstalledApp?: boolean
-  installedAppInfo?: InstalledApp
-  isSupportPlugin?: boolean
-}
-
-const Main: FC<IMainProps> = ({
-  isInstalledApp = false,
-  installedAppInfo,
-}) => {
-  const { t } = useTranslation()
-  const { notify } = useContext(ToastContext)
-  const media = useBreakpoints()
-  const isMobile = media === MediaType.mobile
-
-  /*
-  * app info
-  */
-  const [appUnavailable, setAppUnavailable] = useState<boolean>(false)
-  const [isUnknownReason, setIsUnknwonReason] = useState<boolean>(false)
-  const [appId, setAppId] = useState<string>('')
-  const [isPublicVersion, setIsPublicVersion] = useState<boolean>(true)
-  const [siteInfo, setSiteInfo] = useState<SiteInfo | null>()
-  const [promptConfig, setPromptConfig] = useState<PromptConfig | null>(null)
-  const [inited, setInited] = useState<boolean>(false)
-  const [plan, setPlan] = useState<string>('basic') // basic/plus/pro
-  const [canReplaceLogo, setCanReplaceLogo] = useState<boolean>(false)
-  const [customConfig, setCustomConfig] = useState<any>(null)
-  const [appMeta, setAppMeta] = useState<AppMeta | null>(null)
-  // in mobile, show sidebar by click button
-  const [isShowSidebar, { setTrue: showSidebar, setFalse: hideSidebar }] = useBoolean(false)
-  // Can Use metadata(https://beta.nextjs.org/docs/api-reference/metadata) to set title. But it only works in server side client.
-  useEffect(() => {
-    if (siteInfo?.title) {
-      if (canReplaceLogo)
-        document.title = `${siteInfo.title}`
-      else
-        document.title = `${siteInfo.title} - Powered by Dify`
-    }
-  }, [siteInfo?.title, canReplaceLogo])
-
-  /*
-  * conversation info
-  */
-  const [allConversationList, setAllConversationList] = useState<ConversationItem[]>([])
-  const [isClearConversationList, { setTrue: clearConversationListTrue, setFalse: clearConversationListFalse }] = useBoolean(false)
-  const [isClearPinnedConversationList, { setTrue: clearPinnedConversationListTrue, setFalse: clearPinnedConversationListFalse }] = useBoolean(false)
-  const {
-    conversationList,
-    setConversationList,
-    pinnedConversationList,
-    setPinnedConversationList,
-    currConversationId,
-    getCurrConversationId,
-    setCurrConversationId,
-    getConversationIdFromStorage,
-    isNewConversation,
-    currConversationInfo,
-    currInputs,
-    newConversationInputs,
-    // existConversationInputs,
-    resetNewConversationInputs,
-    setCurrInputs,
-    setNewConversationInfo,
-    existConversationInfo,
-    setExistConversationInfo,
-  } = useConversation()
-  const [suggestedQuestions, setSuggestQuestions] = useState<string[]>([])
-  const [hasMore, setHasMore] = useState<boolean>(true)
-  const [hasPinnedMore, setHasPinnedMore] = useState<boolean>(true)
-  const [isShowSuggestion, setIsShowSuggestion] = useState(false)
-  const onMoreLoaded = useCallback(({ data: conversations, has_more }: any) => {
-    setHasMore(has_more)
-    if (isClearConversationList) {
-      setConversationList(conversations)
-      clearConversationListFalse()
-    }
-    else {
-      setConversationList([...conversationList, ...conversations])
-    }
-  }, [conversationList, setConversationList, isClearConversationList, clearConversationListFalse])
-  const onPinnedMoreLoaded = useCallback(({ data: conversations, has_more }: any) => {
-    setHasPinnedMore(has_more)
-    if (isClearPinnedConversationList) {
-      setPinnedConversationList(conversations)
-      clearPinnedConversationListFalse()
-    }
-    else {
-      setPinnedConversationList([...pinnedConversationList, ...conversations])
-    }
-  }, [pinnedConversationList, setPinnedConversationList, isClearPinnedConversationList, clearPinnedConversationListFalse])
-  const [controlUpdateConversationList, setControlUpdateConversationList] = useState(0)
-  const noticeUpdateList = useCallback(() => {
-    setHasMore(true)
-    clearConversationListTrue()
-
-    setHasPinnedMore(true)
-    clearPinnedConversationListTrue()
-
-    setControlUpdateConversationList(Date.now())
-  }, [clearConversationListTrue, clearPinnedConversationListTrue])
-  const handlePin = useCallback(async (id: string) => {
-    await pinConversation(isInstalledApp, installedAppInfo?.id, id)
-    notify({ type: 'success', message: t('common.api.success') })
-    noticeUpdateList()
-  }, [isInstalledApp, installedAppInfo?.id, t, notify, noticeUpdateList])
-
-  const handleUnpin = useCallback(async (id: string) => {
-    await unpinConversation(isInstalledApp, installedAppInfo?.id, id)
-    notify({ type: 'success', message: t('common.api.success') })
-    noticeUpdateList()
-  }, [isInstalledApp, installedAppInfo?.id, t, notify, noticeUpdateList])
-  const [isShowConfirm, { setTrue: showConfirm, setFalse: hideConfirm }] = useBoolean(false)
-  const [toDeleteConversationId, setToDeleteConversationId] = useState('')
-  const handleDelete = useCallback((id: string) => {
-    setToDeleteConversationId(id)
-    hideSidebar() // mobile
-    showConfirm()
-  }, [hideSidebar, showConfirm])
-
-  const didDelete = async () => {
-    await delConversation(isInstalledApp, installedAppInfo?.id, toDeleteConversationId)
-    notify({ type: 'success', message: t('common.api.success') })
-    hideConfirm()
-    if (currConversationId === toDeleteConversationId)
-      handleConversationIdChange('-1')
-
-    noticeUpdateList()
-  }
-
-  const [suggestedQuestionsAfterAnswerConfig, setSuggestedQuestionsAfterAnswerConfig] = useState<SuggestedQuestionsAfterAnswerConfig | null>(null)
-  const [speechToTextConfig, setSpeechToTextConfig] = useState<SpeechToTextConfig | null>(null)
-  const [textToSpeechConfig, setTextToSpeechConfig] = useState<TextToSpeechConfig | null>(null)
-  const [citationConfig, setCitationConfig] = useState<CitationConfig | null>(null)
-  const [chatList, setChatList, getChatList] = useGetState<IChatItem[]>([])
-  const chatListDomRef = useRef<HTMLDivElement>(null)
-  const [isResponding, { setTrue: setRespondingTrue, setFalse: setRespondingFalse }] = useBoolean(false)
-  const [abortController, setAbortController] = useState<AbortController | null>(null)
-  const [conversationIdChangeBecauseOfNew, setConversationIdChangeBecauseOfNew, getConversationIdChangeBecauseOfNew] = useGetState(false)
-  const [isChatStarted, { setTrue: setChatStarted, setFalse: setChatNotStarted }] = useBoolean(false)
-  const conversationIntroduction = currConversationInfo?.introduction || ''
-  const createNewChat = useCallback(async () => {
-    // if new chat is already exist, do not create new chat
-    abortController?.abort()
-    setRespondingFalse()
-    if (conversationList.some(item => item.id === '-1'))
-      return
-
-    setConversationList(produce(conversationList, (draft) => {
-      draft.unshift({
-        id: '-1',
-        name: t('share.chat.newChatDefaultName'),
-        inputs: newConversationInputs,
-        introduction: conversationIntroduction,
-      })
-    }))
-  }, [
-    abortController,
-    setRespondingFalse,
-    setConversationList,
-    conversationList,
-    newConversationInputs,
-    conversationIntroduction,
-    t,
-  ])
-  const handleStartChat = useCallback((inputs: Record<string, any>) => {
-    createNewChat()
-    setConversationIdChangeBecauseOfNew(true)
-    setCurrInputs(inputs)
-    setChatStarted()
-    // parse variables in introduction
-    setChatList(generateNewChatListWithOpenstatement('', inputs))
-  }, [
-    createNewChat,
-    setConversationIdChangeBecauseOfNew,
-    setCurrInputs,
-    setChatStarted,
-    setChatList,
-  ])
-  const hasSetInputs = (() => {
-    if (!isNewConversation)
-      return true
-
-    return isChatStarted
-  })()
-
-  const conversationName = currConversationInfo?.name || t('share.chat.newChatDefaultName') as string
-  const [controlChatUpdateAllConversation, setControlChatUpdateAllConversation] = useState(0)
-
-  // onData change thought (the produce obj). https://github.com/immerjs/immer/issues/576
-  useEffect(() => {
-    setAutoFreeze(false)
-    return () => {
-      setAutoFreeze(true)
-    }
-  }, [])
-
-  useEffect(() => {
-    (async () => {
-      if (controlChatUpdateAllConversation && !isNewConversation) {
-        const { data: allConversations } = await fetchAllConversations() as { data: ConversationItem[]; has_more: boolean }
-        const item = allConversations.find(item => item.id === currConversationId)
-        setAllConversationList(allConversations)
-        if (item) {
-          setExistConversationInfo({
-            ...existConversationInfo,
-            name: item?.name || '',
-          } as any)
-        }
-      }
-    })()
-  }, [controlChatUpdateAllConversation])
-
-  const handleConversationSwitch = () => {
-    if (!inited)
-      return
-    if (!appId) {
-      // wait for appId
-      setTimeout(handleConversationSwitch, 100)
-      return
-    }
-
-    // update inputs of current conversation
-    let notSyncToStateIntroduction = ''
-    let notSyncToStateInputs: Record<string, any> | undefined | null = {}
-    if (!isNewConversation) {
-      const item = allConversationList.find(item => item.id === currConversationId)
-      notSyncToStateInputs = item?.inputs || {}
-      setCurrInputs(notSyncToStateInputs)
-      notSyncToStateIntroduction = item?.introduction || ''
-      setExistConversationInfo({
-        name: item?.name || '',
-        introduction: notSyncToStateIntroduction,
-      })
-    }
-    else {
-      notSyncToStateInputs = newConversationInputs
-      setCurrInputs(notSyncToStateInputs)
-    }
-
-    // update chat list of current conversation
-    if (!isNewConversation && !conversationIdChangeBecauseOfNew) {
-      fetchChatList(currConversationId, isInstalledApp, installedAppInfo?.id).then((res: any) => {
-        const { data } = res
-        const newChatList: IChatItem[] = generateNewChatListWithOpenstatement(notSyncToStateIntroduction, notSyncToStateInputs)
-
-        data.forEach((item: any) => {
-          newChatList.push({
-            id: `question-${item.id}`,
-            content: item.query,
-            isAnswer: false,
-            message_files: item.message_files?.filter((file: any) => file.belongs_to === 'user') || [],
-          })
-          newChatList.push({
-            id: item.id,
-            content: item.answer,
-            agent_thoughts: addFileInfos(item.agent_thoughts ? sortAgentSorts(item.agent_thoughts) : item.agent_thoughts, item.message_files),
-            feedback: item.feedback,
-            isAnswer: true,
-            citation: item.retriever_resources,
-            message_files: item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || [],
-          })
-        })
-        setChatList(newChatList)
-      })
-    }
-
-    if (isNewConversation && isChatStarted)
-      setChatList(generateNewChatListWithOpenstatement())
-
-    setControlFocus(Date.now())
-  }
-  useEffect(handleConversationSwitch, [currConversationId, inited])
-
-  /*
-  * chat info. chat is under conversation.
-  */
-  useEffect(() => {
-    // scroll to bottom
-    if (chatListDomRef.current)
-      chatListDomRef.current.scrollTop = chatListDomRef.current.scrollHeight
-  }, [chatList, currConversationId])
-  // user can not edit inputs if user had send message
-  const canEditInpus = !chatList.some(item => item.isAnswer === false) && isNewConversation
-
-  const handleConversationIdChange = useCallback((id: string) => {
-    if (id === '-1') {
-      createNewChat()
-      setConversationIdChangeBecauseOfNew(true)
-    }
-    else {
-      setConversationIdChangeBecauseOfNew(false)
-    }
-    // trigger handleConversationSwitch
-    setCurrConversationId(id, appId)
-    setIsShowSuggestion(false)
-    hideSidebar()
-  }, [
-    appId,
-    createNewChat,
-    hideSidebar,
-    setCurrConversationId,
-    setIsShowSuggestion,
-    setConversationIdChangeBecauseOfNew,
-  ])
-
-  // sometime introduction is not applied to state
-  const generateNewChatListWithOpenstatement = (introduction?: string, inputs?: Record<string, any> | null) => {
-    let caculatedIntroduction = introduction || conversationIntroduction || ''
-    const caculatedPromptVariables = inputs || currInputs || null
-    if (caculatedIntroduction && caculatedPromptVariables)
-      caculatedIntroduction = replaceStringWithValues(caculatedIntroduction, promptConfig?.prompt_variables || [], caculatedPromptVariables)
-
-    const openstatement = {
-      id: `${Date.now()}`,
-      content: caculatedIntroduction,
-      isAnswer: true,
-      feedbackDisabled: true,
-      isOpeningStatement: true,
-      suggestedQuestions: openingSuggestedQuestions,
-    }
-    if (caculatedIntroduction)
-      return [openstatement]
-
-    return []
-  }
-
-  const fetchAllConversations = () => {
-    return fetchConversations(isInstalledApp, installedAppInfo?.id, undefined, undefined, 100)
-  }
-
-  const fetchInitData = async () => {
-    if (!isInstalledApp)
-      await checkOrSetAccessToken()
-
-    return Promise.all([isInstalledApp
-      ? {
-        app_id: installedAppInfo?.id,
-        site: {
-          title: installedAppInfo?.app.name,
-          icon: installedAppInfo?.app.icon,
-          icon_background: installedAppInfo?.app.icon_background,
-          prompt_public: false,
-          copyright: '',
-        },
-        plan: 'basic',
-      }
-      : fetchAppInfo(), fetchAllConversations(), fetchAppParams(isInstalledApp, installedAppInfo?.id), fetchAppMeta(isInstalledApp, installedAppInfo?.id)])
-  }
-
-  const { data: fileUploadConfigResponse } = useSWR(isInstalledApp ? { url: '/files/upload' } : null, fetchFileUploadConfig)
-
-  // init
-  useEffect(() => {
-    (async () => {
-      try {
-        const [appData, conversationData, appParams, appMeta]: any = await fetchInitData()
-        setAppMeta(appMeta)
-        const { app_id: appId, site: siteInfo, plan, can_replace_logo, custom_config }: any = appData
-        setAppId(appId)
-        setPlan(plan)
-        setCanReplaceLogo(can_replace_logo)
-        setCustomConfig(custom_config)
-        const tempIsPublicVersion = siteInfo.prompt_public
-        setIsPublicVersion(tempIsPublicVersion)
-        const prompt_template = ''
-        // handle current conversation id
-        const { data: allConversations } = conversationData as { data: ConversationItem[]; has_more: boolean }
-        const _conversationId = getConversationIdFromStorage(appId)
-        const isNotNewConversation = allConversations.some(item => item.id === _conversationId)
-        setAllConversationList(allConversations)
-        // fetch new conversation info
-        const { user_input_form, opening_statement: introduction, suggested_questions, suggested_questions_after_answer, speech_to_text, text_to_speech, retriever_resource, file_upload, sensitive_word_avoidance }: any = appParams
-        setVisionConfig({
-          ...file_upload.image,
-          image_file_size_limit: appParams?.system_parameters?.image_file_size_limit,
-        })
-        const prompt_variables = userInputsFormToPromptVariables(user_input_form)
-        if (siteInfo.default_language)
-          changeLanguage(siteInfo.default_language)
-
-        setNewConversationInfo({
-          name: t('share.chat.newChatDefaultName'),
-          introduction,
-        })
-        setOpeningSuggestedQuestions(suggested_questions || [])
-
-        setSiteInfo(siteInfo as SiteInfo)
-        setPromptConfig({
-          prompt_template,
-          prompt_variables,
-        } as PromptConfig)
-        setSuggestedQuestionsAfterAnswerConfig(suggested_questions_after_answer)
-        setSpeechToTextConfig(speech_to_text)
-        setTextToSpeechConfig(text_to_speech)
-        setCitationConfig(retriever_resource)
-
-        // setConversationList(conversations as ConversationItem[])
-
-        if (isNotNewConversation)
-          setCurrConversationId(_conversationId, appId, false)
-
-        setInited(true)
-      }
-      catch (e: any) {
-        if (e.status === 404) {
-          setAppUnavailable(true)
-        }
-        else {
-          setIsUnknwonReason(true)
-          setAppUnavailable(true)
-        }
-      }
-    })()
-  }, [])
-
-  const logError = useCallback((message: string) => {
-    notify({ type: 'error', message })
-  }, [notify])
-
-  const checkCanSend = useCallback(() => {
-    if (currConversationId !== '-1')
-      return true
-
-    const prompt_variables = promptConfig?.prompt_variables
-    const inputs = currInputs
-    if (!inputs || !prompt_variables || prompt_variables?.length === 0)
-      return true
-
-    let hasEmptyInput = ''
-    const requiredVars = prompt_variables?.filter(({ key, name, required }) => {
-      const res = (!key || !key.trim()) || (!name || !name.trim()) || (required || required === undefined || required === null)
-      return res
-    }) || [] // compatible with old version
-    requiredVars.forEach(({ key, name }) => {
-      if (hasEmptyInput)
-        return
-
-      if (!inputs?.[key])
-        hasEmptyInput = name
-    })
-
-    if (hasEmptyInput) {
-      logError(t('appDebug.errorMessage.valueOfVarRequired', { key: hasEmptyInput }))
-      return false
-    }
-    return !hasEmptyInput
-  }, [currConversationId, currInputs, promptConfig, t, logError])
-
-  const [controlFocus, setControlFocus] = useState(0)
-  const doShowSuggestion = isShowSuggestion && !isResponding
-  const [openingSuggestedQuestions, setOpeningSuggestedQuestions] = useState<string[]>([])
-  const [messageTaskId, setMessageTaskId] = useState('')
-  const [hasStopResponded, setHasStopResponded, getHasStopResponded] = useGetState(false)
-  const [isRespondingConIsCurrCon, setIsRespondingConCurrCon, getIsRespondingConIsCurrCon] = useGetState(true)
-  const [userQuery, setUserQuery] = useState('')
-  const [visionConfig, setVisionConfig] = useState<VisionSettings>({
-    enabled: false,
-    number_limits: 2,
-    detail: Resolution.low,
-    transfer_methods: [TransferMethod.local_file],
-  })
-
-  const updateCurrentQA = ({
-    responseItem,
-    questionId,
-    placeholderAnswerId,
-    questionItem,
-  }: {
-    responseItem: IChatItem
-    questionId: string
-    placeholderAnswerId: string
-    questionItem: IChatItem
-  }) => {
-    // closesure new list is outdated.
-    const newListWithAnswer = produce(
-      getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
-      (draft) => {
-        if (!draft.find(item => item.id === questionId))
-          draft.push({ ...questionItem })
-
-        draft.push({ ...responseItem })
-      })
-    setChatList(newListWithAnswer)
-  }
-
-  const handleSend = async (message: string, files?: VisionFile[]) => {
-    if (isResponding) {
-      notify({ type: 'info', message: t('appDebug.errorMessage.waitForResponse') })
-      return
-    }
-
-    if (files?.find(item => item.transfer_method === TransferMethod.local_file && !item.upload_file_id)) {
-      notify({ type: 'info', message: t('appDebug.errorMessage.waitForImgUpload') })
-      return false
-    }
-
-    const data: Record<string, any> = {
-      inputs: currInputs,
-      query: message,
-      conversation_id: isNewConversation ? null : currConversationId,
-    }
-
-    if (visionConfig?.enabled && files && files?.length > 0) {
-      data.files = files.map((item) => {
-        if (item.transfer_method === TransferMethod.local_file) {
-          return {
-            ...item,
-            url: '',
-          }
-        }
-        return item
-      })
-    }
-
-    // qustion
-    const questionId = `question-${Date.now()}`
-    const questionItem = {
-      id: questionId,
-      content: message,
-      isAnswer: false,
-      message_files: files,
-
-    }
-
-    const placeholderAnswerId = `answer-placeholder-${Date.now()}`
-    const placeholderAnswerItem = {
-      id: placeholderAnswerId,
-      content: '',
-      isAnswer: true,
-    }
-
-    const newList = [...getChatList(), questionItem, placeholderAnswerItem]
-    setChatList(newList)
-
-    let isAgentMode = false
-
-    // answer
-    const responseItem: IChatItem = {
-      id: `${Date.now()}`,
-      content: '',
-      agent_thoughts: [],
-      message_files: [],
-      isAnswer: true,
-    }
-    let hasSetResponseId = false
-
-    const prevTempNewConversationId = getCurrConversationId() || '-1'
-    let tempNewConversationId = prevTempNewConversationId
-
-    setHasStopResponded(false)
-    setRespondingTrue()
-    setIsShowSuggestion(false)
-    setIsRespondingConCurrCon(true)
-    sendChatMessage(data, {
-      getAbortController: (abortController) => {
-        setAbortController(abortController)
-      },
-      onData: (message: string, isFirstMessage: boolean, { conversationId: newConversationId, messageId, taskId }: any) => {
-        if (!isAgentMode) {
-          responseItem.content = responseItem.content + message
-        }
-        else {
-          const lastThought = responseItem.agent_thoughts?.[responseItem.agent_thoughts?.length - 1]
-          if (lastThought)
-            lastThought.thought = lastThought.thought + message // need immer setAutoFreeze
-        }
-        if (messageId && !hasSetResponseId) {
-          responseItem.id = messageId
-          hasSetResponseId = true
-        }
-
-        if (isFirstMessage && newConversationId)
-          tempNewConversationId = newConversationId
-
-        setMessageTaskId(taskId)
-        // has switched to other conversation
-        if (prevTempNewConversationId !== getCurrConversationId()) {
-          setIsRespondingConCurrCon(false)
-          return
-        }
-        updateCurrentQA({
-          responseItem,
-          questionId,
-          placeholderAnswerId,
-          questionItem,
-        })
-      },
-      async onCompleted(hasError?: boolean) {
-        if (hasError)
-          return
-
-        if (getConversationIdChangeBecauseOfNew()) {
-          const { data: allConversations }: any = await fetchAllConversations()
-          const newItem: any = await generationConversationName(isInstalledApp, installedAppInfo?.id, allConversations[0].id)
-
-          const newAllConversations = produce(allConversations, (draft: any) => {
-            draft[0].name = newItem.name
-          })
-          setAllConversationList(newAllConversations as any)
-          noticeUpdateList()
-        }
-        setConversationIdChangeBecauseOfNew(false)
-        resetNewConversationInputs()
-        setChatNotStarted()
-        setCurrConversationId(tempNewConversationId, appId, true)
-        if (getIsRespondingConIsCurrCon() && suggestedQuestionsAfterAnswerConfig?.enabled && !getHasStopResponded()) {
-          const { data }: any = await fetchSuggestedQuestions(responseItem.id, isInstalledApp, installedAppInfo?.id)
-          setSuggestQuestions(data)
-          setIsShowSuggestion(true)
-        }
-        setRespondingFalse()
-      },
-      onFile(file) {
-        const lastThought = responseItem.agent_thoughts?.[responseItem.agent_thoughts?.length - 1]
-        if (lastThought)
-          lastThought.message_files = [...(lastThought as any).message_files, { ...file }]
-
-        updateCurrentQA({
-          responseItem,
-          questionId,
-          placeholderAnswerId,
-          questionItem,
-        })
-      },
-      onThought(thought) {
-        isAgentMode = true
-        const response = responseItem as any
-        if (thought.message_id && !hasSetResponseId) {
-          response.id = thought.message_id
-          hasSetResponseId = true
-        }
-        // responseItem.id = thought.message_id;
-        if (response.agent_thoughts.length === 0) {
-          response.agent_thoughts.push(thought)
-        }
-        else {
-          const lastThought = response.agent_thoughts[response.agent_thoughts.length - 1]
-          // thought changed but still the same thought, so update.
-          if (lastThought.id === thought.id) {
-            thought.thought = lastThought.thought
-            thought.message_files = lastThought.message_files
-            responseItem.agent_thoughts![response.agent_thoughts.length - 1] = thought
-          }
-          else {
-            responseItem.agent_thoughts!.push(thought)
-          }
-        }
-        // has switched to other conversation
-        if (prevTempNewConversationId !== getCurrConversationId()) {
-          setIsRespondingConCurrCon(false)
-          return false
-        }
-
-        updateCurrentQA({
-          responseItem,
-          questionId,
-          placeholderAnswerId,
-          questionItem,
-        })
-      },
-      onMessageEnd: (messageEnd) => {
-        if (messageEnd.metadata?.annotation_reply) {
-          responseItem.id = messageEnd.id
-          responseItem.annotation = ({
-            id: messageEnd.metadata.annotation_reply.id,
-            authorName: messageEnd.metadata.annotation_reply.account.name,
-          } as AnnotationType)
-          const newListWithAnswer = produce(
-            getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
-            (draft) => {
-              if (!draft.find(item => item.id === questionId))
-                draft.push({ ...questionItem })
-
-              draft.push({
-                ...responseItem,
-              })
-            })
-          setChatList(newListWithAnswer)
-          return
-        }
-        // not support show citation
-        // responseItem.citation = messageEnd.retriever_resources
-        if (!isInstalledApp)
-          return
-        const newListWithAnswer = produce(
-          getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
-          (draft) => {
-            if (!draft.find(item => item.id === questionId))
-              draft.push({ ...questionItem })
-
-            draft.push({ ...responseItem })
-          })
-        setChatList(newListWithAnswer)
-      },
-      onMessageReplace: (messageReplace) => {
-        if (isInstalledApp) {
-          responseItem.content = messageReplace.answer
-        }
-        else {
-          setChatList(produce(
-            getChatList(),
-            (draft) => {
-              const current = draft.find(item => item.id === messageReplace.id)
-
-              if (current)
-                current.content = messageReplace.answer
-            },
-          ))
-        }
-      },
-      onError() {
-        setRespondingFalse()
-        // role back placeholder answer
-        setChatList(produce(getChatList(), (draft) => {
-          draft.splice(draft.findIndex(item => item.id === placeholderAnswerId), 1)
-        }))
-      },
-    }, isInstalledApp, installedAppInfo?.id)
-  }
-
-  const handleFeedback = useCallback(async (messageId: string, feedback: Feedbacktype) => {
-    await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating } }, isInstalledApp, installedAppInfo?.id)
-    const newChatList = chatList.map((item) => {
-      if (item.id === messageId) {
-        return {
-          ...item,
-          feedback,
-        }
-      }
-      return item
-    })
-    setChatList(newChatList)
-    notify({ type: 'success', message: t('common.api.success') })
-  }, [isInstalledApp, installedAppInfo?.id, chatList, t, notify, setChatList])
-
-  const handleListChanged = useCallback((list: ConversationItem[]) => {
-    setConversationList(list)
-    setControlChatUpdateAllConversation(Date.now())
-  }, [setConversationList, setControlChatUpdateAllConversation])
-  const handlePinnedListChanged = useCallback((list: ConversationItem[]) => {
-    setPinnedConversationList(list)
-    setControlChatUpdateAllConversation(Date.now())
-  }, [setPinnedConversationList, setControlChatUpdateAllConversation])
-  const handleStartChatOnSidebar = useCallback(() => {
-    handleConversationIdChange('-1')
-  }, [handleConversationIdChange])
-
-  const renderSidebar = () => {
-    if (!appId || !siteInfo || !promptConfig)
-      return null
-    return (
-      <Sidebar
-        list={conversationList}
-        onListChanged={handleListChanged}
-        isClearConversationList={isClearConversationList}
-        pinnedList={pinnedConversationList}
-        onPinnedListChanged={handlePinnedListChanged}
-        isClearPinnedConversationList={isClearPinnedConversationList}
-        onMoreLoaded={onMoreLoaded}
-        onPinnedMoreLoaded={onPinnedMoreLoaded}
-        isNoMore={!hasMore}
-        isPinnedNoMore={!hasPinnedMore}
-        onCurrentIdChange={handleConversationIdChange}
-        currentId={currConversationId}
-        copyRight={siteInfo.copyright || siteInfo.title}
-        isInstalledApp={isInstalledApp}
-        installedAppId={installedAppInfo?.id}
-        siteInfo={siteInfo}
-        onPin={handlePin}
-        onUnpin={handleUnpin}
-        controlUpdateList={controlUpdateConversationList}
-        onDelete={handleDelete}
-        onStartChat={handleStartChatOnSidebar}
-      />
-    )
-  }
-
-  const handleAbortResponding = useCallback(async () => {
-    await stopChatMessageResponding(appId, messageTaskId, isInstalledApp, installedAppInfo?.id)
-    setHasStopResponded(true)
-    setRespondingFalse()
-  }, [appId, messageTaskId, isInstalledApp, installedAppInfo?.id])
-
-  if (appUnavailable)
-    return <AppUnavailable isUnknownReason={isUnknownReason} />
-
-  if (!appId || !siteInfo || !promptConfig) {
-    return <div className='flex h-screen w-full'>
-      <Loading type='app' />
-    </div>
-  }
-
-  return (
-    <div className='bg-gray-100 h-full flex flex-col'>
-      {!isInstalledApp && (
-        <Header
-          title={siteInfo.title}
-          icon={siteInfo.icon || ''}
-          icon_background={siteInfo.icon_background || ''}
-          isMobile={isMobile}
-          onShowSideBar={showSidebar}
-          onCreateNewChat={handleStartChatOnSidebar}
-        />
-      )}
-
-      <div
-        className={cn(
-          'flex rounded-t-2xl bg-white overflow-hidden h-full w-full',
-          isInstalledApp && 'rounded-b-2xl',
-        )}
-        style={isInstalledApp
-          ? {
-            boxShadow: '0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03)',
-          }
-          : {}}
-      >
-        {/* sidebar */}
-        {!isMobile && renderSidebar()}
-        {isMobile && isShowSidebar && (
-          <div className='fixed inset-0 z-50'
-            style={{ backgroundColor: 'rgba(35, 56, 118, 0.2)' }}
-            onClick={hideSidebar}
-          >
-            <div className='inline-block' onClick={e => e.stopPropagation()}>
-              {renderSidebar()}
-            </div>
-          </div>
-        )}
-        {/* main */}
-        <div className={cn(
-          'h-full flex-grow flex flex-col overflow-y-auto',
-        )
-        }>
-          <ConfigSence
-            conversationName={conversationName}
-            hasSetInputs={hasSetInputs}
-            isPublicVersion={isPublicVersion}
-            siteInfo={siteInfo}
-            promptConfig={promptConfig}
-            onStartChat={handleStartChat}
-            canEidtInpus={canEditInpus}
-            savedInputs={currInputs as Record<string, any>}
-            onInputsChange={setCurrInputs}
-            plan={plan}
-            canReplaceLogo={canReplaceLogo}
-            customConfig={customConfig}
-          ></ConfigSence>
-
-          {
-            hasSetInputs && (
-              <div className={cn(doShowSuggestion ? 'pb-[140px]' : (isResponding ? 'pb-[113px]' : 'pb-[76px]'), 'relative grow h-[200px] pc:w-[794px] max-w-full mobile:w-full mx-auto mb-3.5 overflow-hidden')}>
-                <div className='h-full overflow-y-auto' ref={chatListDomRef}>
-                  <Chat
-                    chatList={chatList}
-                    query={userQuery}
-                    onQueryChange={setUserQuery}
-                    onSend={handleSend}
-                    isHideFeedbackEdit
-                    onFeedback={handleFeedback}
-                    isResponding={isResponding}
-                    canStopResponding={!!messageTaskId && isRespondingConIsCurrCon}
-                    abortResponding={handleAbortResponding}
-                    checkCanSend={checkCanSend}
-                    controlFocus={controlFocus}
-                    isShowSuggestion={doShowSuggestion}
-                    suggestionList={suggestedQuestions}
-                    isShowSpeechToText={speechToTextConfig?.enabled}
-                    isShowTextToSpeech={textToSpeechConfig?.enabled}
-                    isShowCitation={citationConfig?.enabled}
-                    visionConfig={{
-                      ...visionConfig,
-                      image_file_size_limit: fileUploadConfigResponse ? fileUploadConfigResponse.image_file_size_limit : visionConfig.image_file_size_limit,
-                    }}
-                    allToolIcons={appMeta?.tool_icons || {}}
-                    customDisclaimer={siteInfo.custom_disclaimer}
-                  />
-                </div>
-              </div>)
-          }
-
-          {isShowConfirm && (
-            <Confirm
-              title={t('share.chat.deleteConversation.title')}
-              content={t('share.chat.deleteConversation.content')}
-              isShow={isShowConfirm}
-              onClose={hideConfirm}
-              onConfirm={didDelete}
-              onCancel={hideConfirm}
-            />
-          )}
-        </div>
-      </div>
-    </div>
-  )
-}
-export default React.memo(Main)

+ 0 - 28
web/app/components/share/chat/sidebar/app-info/index.tsx

@@ -1,28 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React from 'react'
-import cn from 'classnames'
-import { appDefaultIconBackground } from '@/config/index'
-import AppIcon from '@/app/components/base/app-icon'
-
-export type IAppInfoProps = {
-  className?: string
-  icon: string
-  icon_background?: string
-  name: string
-}
-
-const AppInfo: FC<IAppInfoProps> = ({
-  className,
-  icon,
-  icon_background,
-  name,
-}) => {
-  return (
-    <div className={cn(className, 'flex items-center space-x-3')}>
-      <AppIcon size="small" icon={icon} background={icon_background || appDefaultIconBackground} />
-      <div className='w-0 grow text-sm font-semibold text-gray-800 overflow-hidden  text-ellipsis whitespace-nowrap'>{name}</div>
-    </div>
-  )
-}
-export default React.memo(AppInfo)

+ 0 - 3
web/app/components/share/chat/sidebar/card.module.css

@@ -1,3 +0,0 @@
-.card:hover {
-  background: linear-gradient(0deg, rgba(235, 245, 255, 0.4), rgba(235, 245, 255, 0.4)), #FFFFFF;
-}

+ 0 - 19
web/app/components/share/chat/sidebar/card.tsx

@@ -1,19 +0,0 @@
-import React from 'react'
-import { useTranslation } from 'react-i18next'
-import s from './card.module.css'
-
-type PropType = {
-  children: React.ReactNode
-  text?: string
-}
-function Card({ children, text }: PropType) {
-  const { t } = useTranslation()
-  return (
-    <div className={`${s.card} box-border w-full flex flex-col items-start px-4 py-3 rounded-lg border-solid border border-gray-200  cursor-pointer hover:border-primary-300`}>
-      <div className='text-gray-400 font-medium text-xs mb-2'>{text ?? t('share.chat.powerBy')}</div>
-      {children}
-    </div>
-  )
-}
-
-export default Card

+ 0 - 167
web/app/components/share/chat/sidebar/index.tsx

@@ -1,167 +0,0 @@
-import React, { useCallback, useEffect, useState } from 'react'
-import type { FC } from 'react'
-import { useTranslation } from 'react-i18next'
-import {
-  PencilSquareIcon,
-} from '@heroicons/react/24/outline'
-import cn from 'classnames'
-import Button from '../../../base/button'
-import List from './list'
-import AppInfo from '@/app/components/share/chat/sidebar/app-info'
-// import Card from './card'
-import type { ConversationItem, SiteInfo } from '@/models/share'
-import { fetchConversations } from '@/service/share'
-
-export type ISidebarProps = {
-  copyRight: string
-  currentId: string
-  onCurrentIdChange: (id: string) => void
-  list: ConversationItem[]
-  onListChanged: (newList: ConversationItem[]) => void
-  isClearConversationList: boolean
-  pinnedList: ConversationItem[]
-  onPinnedListChanged: (newList: ConversationItem[]) => void
-  isClearPinnedConversationList: boolean
-  isInstalledApp: boolean
-  installedAppId?: string
-  siteInfo: SiteInfo
-  onMoreLoaded: (res: { data: ConversationItem[]; has_more: boolean }) => void
-  onPinnedMoreLoaded: (res: { data: ConversationItem[]; has_more: boolean }) => void
-  isNoMore: boolean
-  isPinnedNoMore: boolean
-  onPin: (id: string) => void
-  onUnpin: (id: string) => void
-  controlUpdateList: number
-  onDelete: (id: string) => void
-  onStartChat: (inputs: Record<string, any>) => void
-}
-
-const Sidebar: FC<ISidebarProps> = ({
-  copyRight,
-  currentId,
-  onCurrentIdChange,
-  list,
-  onListChanged,
-  isClearConversationList,
-  pinnedList,
-  onPinnedListChanged,
-  isClearPinnedConversationList,
-  isInstalledApp,
-  installedAppId,
-  siteInfo,
-  onMoreLoaded,
-  onPinnedMoreLoaded,
-  isNoMore,
-  isPinnedNoMore,
-  onPin,
-  onUnpin,
-  controlUpdateList,
-  onDelete,
-  onStartChat,
-}) => {
-  const { t } = useTranslation()
-  const [hasPinned, setHasPinned] = useState(false)
-
-  const checkHasPinned = async () => {
-    const res = await fetchConversations(isInstalledApp, installedAppId, undefined, true) as any
-    setHasPinned(res.data.length > 0)
-  }
-
-  useEffect(() => {
-    checkHasPinned()
-  }, [])
-
-  useEffect(() => {
-    if (controlUpdateList !== 0)
-      checkHasPinned()
-  }, [controlUpdateList])
-
-  const handleUnpin = useCallback((id: string) => {
-    onUnpin(id)
-  }, [onUnpin])
-  const handlePin = useCallback((id: string) => {
-    onPin(id)
-  }, [onPin])
-
-  const maxListHeight = (isInstalledApp) ? 'max-h-[30vh]' : 'max-h-[40vh]'
-
-  return (
-    <div
-      className={
-        cn(
-          (isInstalledApp) ? 'tablet:h-[calc(100vh_-_74px)]' : '',
-          'shrink-0 flex flex-col bg-white pc:w-[244px] tablet:w-[192px] mobile:w-[240px]  border-r border-gray-200 mobile:h-screen',
-        )
-      }
-    >
-      {isInstalledApp && (
-        <AppInfo
-          className='my-4 px-4'
-          name={siteInfo.title || ''}
-          icon={siteInfo.icon || ''}
-          icon_background={siteInfo.icon_background}
-        />
-      )}
-      <div className="flex flex-shrink-0 p-4 !pb-0">
-        <Button
-          onClick={() => onStartChat({})}
-          variant='secondary-accent'
-          className="group w-full flex-shrink-0 justify-start">
-          <PencilSquareIcon className="mr-2 h-4 w-4" /> {t('share.chat.newChat')}
-        </Button>
-      </div>
-      <div className={'flex-grow flex flex-col h-0 overflow-y-auto overflow-x-hidden'}>
-        {/* pinned list */}
-        {hasPinned && (
-          <div className={cn('mt-4 px-4', list.length === 0 && 'flex flex-col flex-grow')}>
-            <div className='mb-1.5 leading-[18px] text-xs text-gray-500 font-medium uppercase'>{t('share.chat.pinnedTitle')}</div>
-            <List
-              className={cn(list.length > 0 ? maxListHeight : 'flex-grow')}
-              currentId={currentId}
-              onCurrentIdChange={onCurrentIdChange}
-              list={pinnedList}
-              onListChanged={onPinnedListChanged}
-              isClearConversationList={isClearPinnedConversationList}
-              isInstalledApp={isInstalledApp}
-              installedAppId={installedAppId}
-              onMoreLoaded={onPinnedMoreLoaded}
-              isNoMore={isPinnedNoMore}
-              isPinned={true}
-              onPinChanged={handleUnpin}
-              controlUpdate={controlUpdateList + 1}
-              onDelete={onDelete}
-            />
-          </div>
-        )}
-        {/* unpinned list */}
-        <div className={cn('grow flex flex-col mt-4 px-4', !hasPinned && 'flex flex-col flex-grow')}>
-          {(hasPinned && list.length > 0) && (
-            <div className='mb-1.5 leading-[18px] text-xs text-gray-500 font-medium uppercase'>{t('share.chat.unpinnedTitle')}</div>
-          )}
-          <List
-            className={cn('flex-grow h-0')}
-            currentId={currentId}
-            onCurrentIdChange={onCurrentIdChange}
-            list={list}
-            onListChanged={onListChanged}
-            isClearConversationList={isClearConversationList}
-            isInstalledApp={isInstalledApp}
-            installedAppId={installedAppId}
-            onMoreLoaded={onMoreLoaded}
-            isNoMore={isNoMore}
-            isPinned={false}
-            onPinChanged={handlePin}
-            controlUpdate={controlUpdateList + 1}
-            onDelete={onDelete}
-          />
-        </div>
-
-      </div>
-      <div className="flex flex-shrink-0 pr-4 pb-4 pl-4">
-        <div className="text-gray-400 font-normal text-xs">© {copyRight} {(new Date()).getFullYear()}</div>
-      </div>
-    </div>
-  )
-}
-
-export default React.memo(Sidebar)

+ 0 - 143
web/app/components/share/chat/sidebar/list/index.tsx

@@ -1,143 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React, { useRef, useState } from 'react'
-
-import { useBoolean, useInfiniteScroll } from 'ahooks'
-import cn from 'classnames'
-import { useTranslation } from 'react-i18next'
-import RenameModal from '../rename-modal'
-import Item from './item'
-import type { ConversationItem } from '@/models/share'
-import { fetchConversations, renameConversation } from '@/service/share'
-import Toast from '@/app/components/base/toast'
-
-export type IListProps = {
-  className: string
-  currentId: string
-  onCurrentIdChange: (id: string) => void
-  list: ConversationItem[]
-  onListChanged?: (newList: ConversationItem[]) => void
-  isClearConversationList: boolean
-  isInstalledApp: boolean
-  installedAppId?: string
-  onMoreLoaded: (res: { data: ConversationItem[]; has_more: boolean }) => void
-  isNoMore: boolean
-  isPinned: boolean
-  onPinChanged: (id: string) => void
-  controlUpdate: number
-  onDelete: (id: string) => void
-}
-
-const List: FC<IListProps> = ({
-  className,
-  currentId,
-  onCurrentIdChange,
-  list,
-  onListChanged,
-  isClearConversationList,
-  isInstalledApp,
-  installedAppId,
-  onMoreLoaded,
-  isNoMore,
-  isPinned,
-  onPinChanged,
-  controlUpdate,
-  onDelete,
-}) => {
-  const { t } = useTranslation()
-  const listRef = useRef<HTMLDivElement>(null)
-
-  useInfiniteScroll(
-    async () => {
-      if (!isNoMore) {
-        let lastId = !isClearConversationList ? list[list.length - 1]?.id : undefined
-        if (lastId === '-1')
-          lastId = undefined
-        const res = await fetchConversations(isInstalledApp, installedAppId, lastId, isPinned) as any
-        const { data: conversations, has_more }: any = res
-        onMoreLoaded({ data: conversations, has_more })
-      }
-      return { list: [] }
-    },
-    {
-      target: listRef,
-      isNoMore: () => {
-        return isNoMore
-      },
-      reloadDeps: [isNoMore, controlUpdate],
-    },
-  )
-  const [isShowRename, { setTrue: setShowRename, setFalse: setHideRename }] = useBoolean(false)
-  const [isSaving, { setTrue: setIsSaving, setFalse: setNotSaving }] = useBoolean(false)
-  const [currentConversation, setCurrentConversation] = useState<ConversationItem | null>(null)
-  const showRename = (item: ConversationItem) => {
-    setCurrentConversation(item)
-    setShowRename()
-  }
-  const handleRename = async (newName: string) => {
-    if (!newName.trim() || !currentConversation) {
-      Toast.notify({
-        type: 'error',
-        message: t('common.chat.conversationNameCanNotEmpty'),
-      })
-      return
-    }
-
-    setIsSaving()
-    const currId = currentConversation.id
-    try {
-      await renameConversation(isInstalledApp, installedAppId, currId, newName)
-
-      Toast.notify({
-        type: 'success',
-        message: t('common.actionMsg.modifiedSuccessfully'),
-      })
-      onListChanged?.(list.map((item) => {
-        if (item.id === currId) {
-          return {
-            ...item,
-            name: newName,
-          }
-        }
-        return item
-      }))
-      setHideRename()
-    }
-    finally {
-      setNotSaving()
-    }
-  }
-  return (
-    <nav
-      ref={listRef}
-      className={cn(className, 'shrink-0 space-y-1 bg-white overflow-y-auto overflow-x-hidden')}
-    >
-      {list.map((item) => {
-        const isCurrent = item.id === currentId
-        return (
-          <Item
-            key={item.id}
-            item={item}
-            isCurrent={isCurrent}
-            onClick={onCurrentIdChange}
-            isPinned={isPinned}
-            togglePin={onPinChanged}
-            onDelete={onDelete}
-            onRenameConversation={showRename}
-          />
-        )
-      })}
-      {isShowRename && (
-        <RenameModal
-          isShow={isShowRename}
-          onClose={setHideRename}
-          saveLoading={isSaving}
-          name={currentConversation?.name || ''}
-          onSave={handleRename}
-        />
-      )}
-    </nav>
-  )
-}
-
-export default React.memo(List)

+ 0 - 77
web/app/components/share/chat/sidebar/list/item.tsx

@@ -1,77 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React, { useRef } from 'react'
-import cn from 'classnames'
-import { ChatBubbleOvalLeftEllipsisIcon as ChatBubbleOvalLeftEllipsisSolidIcon } from '@heroicons/react/24/solid'
-import {
-  ChatBubbleOvalLeftEllipsisIcon,
-} from '@heroicons/react/24/outline'
-import { useHover } from 'ahooks'
-import ItemOperation from '@/app/components/explore/item-operation'
-import type { ConversationItem } from '@/models/share'
-
-export type IItemProps = {
-  onClick: (id: string) => void
-  item: ConversationItem
-  isCurrent: boolean
-  isPinned: boolean
-  togglePin: (id: string) => void
-  onDelete: (id: string) => void
-  onRenameConversation: (item: ConversationItem) => void
-}
-
-const Item: FC<IItemProps> = ({
-  isCurrent,
-  item,
-  onClick,
-  isPinned,
-  togglePin,
-  onDelete,
-  onRenameConversation,
-}) => {
-  const ItemIcon = isCurrent ? ChatBubbleOvalLeftEllipsisSolidIcon : ChatBubbleOvalLeftEllipsisIcon
-  const ref = useRef(null)
-  const isHovering = useHover(ref)
-
-  return (
-    <div
-      ref={ref}
-      onClick={() => onClick(item.id)}
-      key={item.id}
-      className={cn(
-        isCurrent
-          ? 'bg-primary-50 text-primary-600'
-          : 'text-gray-700 hover:bg-gray-200 hover:text-gray-700',
-        'group flex justify-between items-center rounded-md px-2 py-2 text-sm font-medium cursor-pointer',
-      )}
-    >
-      <div className='flex items-center w-0 grow'>
-        <ItemIcon
-          className={cn(
-            isCurrent
-              ? 'text-primary-600'
-              : 'text-gray-400 group-hover:text-gray-500',
-            'mr-3 h-5 w-5 flex-shrink-0',
-          )}
-          aria-hidden="true"
-        />
-        <span>{item.name}</span>
-      </div>
-
-      {item.id !== '-1' && (
-        <div className='shrink-0 h-6' onClick={e => e.stopPropagation()}>
-          <ItemOperation
-            isPinned={isPinned}
-            isItemHovering={isHovering}
-            togglePin={() => togglePin(item.id)}
-            isShowDelete
-            isShowRenameConversation
-            onRenameConversation={() => onRenameConversation(item)}
-            onDelete={() => onDelete(item.id)}
-          />
-        </div>
-      )}
-    </div>
-  )
-}
-export default React.memo(Item)

+ 0 - 77
web/app/components/share/chat/value-panel/index.tsx

@@ -1,77 +0,0 @@
-'use client'
-import type { FC, ReactNode } from 'react'
-import React from 'react'
-import cn from 'classnames'
-import { useTranslation } from 'react-i18next'
-import s from './style.module.css'
-import { StarIcon } from '@/app/components/share/chat/welcome/massive-component'
-import Button from '@/app/components/base/button'
-
-export type ITemplateVarPanelProps = {
-  className?: string
-  header: ReactNode
-  children?: ReactNode | null
-  isFold: boolean
-}
-
-const TemplateVarPanel: FC<ITemplateVarPanelProps> = ({
-  className,
-  header,
-  children,
-  isFold,
-}) => {
-  return (
-    <div className={cn(isFold ? 'border border-indigo-100' : s.boxShodow, className, 'rounded-xl ')}>
-      {/* header */}
-      <div
-        className={cn(isFold && 'rounded-b-xl', 'rounded-t-xl px-6 py-4 bg-indigo-25 text-xs')}
-      >
-        {header}
-      </div>
-      {/* body */}
-      {!isFold && children && (
-        <div className='rounded-b-xl p-6'>
-          {children}
-        </div>
-      )}
-    </div>
-  )
-}
-
-export const PanelTitle: FC<{ title: string; className?: string }> = ({
-  title,
-  className,
-}) => {
-  return (
-    <div className={cn(className, 'flex items-center space-x-1 text-indigo-600')}>
-      <StarIcon />
-      <span className='text-xs'>{title}</span>
-    </div>
-  )
-}
-
-export const VarOpBtnGroup: FC<{ className?: string; onConfirm: () => void; onCancel: () => void }> = ({
-  className,
-  onConfirm,
-  onCancel,
-}) => {
-  const { t } = useTranslation()
-
-  return (
-    <div className={cn(className, 'flex mt-3 space-x-2 mobile:ml-0 tablet:ml-[128px] text-sm')}>
-      <Button
-        variant='primary'
-        onClick={onConfirm}
-      >
-        {t('common.operation.save')}
-      </Button>
-      <Button
-        onClick={onCancel}
-      >
-        {t('common.operation.cancel')}
-      </Button>
-    </div >
-  )
-}
-
-export default React.memo(TemplateVarPanel)

+ 0 - 3
web/app/components/share/chat/value-panel/style.module.css

@@ -1,3 +0,0 @@
-.boxShodow {
-  box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
-}

+ 0 - 388
web/app/components/share/chat/welcome/index.tsx

@@ -1,388 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React, { useEffect, useState } from 'react'
-import { useTranslation } from 'react-i18next'
-import { useContext } from 'use-context-selector'
-import TemplateVarPanel, { PanelTitle, VarOpBtnGroup } from '../value-panel'
-import s from './style.module.css'
-import { AppInfo, ChatBtn, EditBtn, FootLogo, PromptTemplate } from './massive-component'
-import type { SiteInfo } from '@/models/share'
-import type { PromptConfig } from '@/models/debug'
-import { ToastContext } from '@/app/components/base/toast'
-import Select from '@/app/components/base/select'
-import { DEFAULT_VALUE_MAX_LEN } from '@/config'
-
-// regex to match the {{}} and replace it with a span
-const regex = /\{\{([^}]+)\}\}/g
-
-export type IWelcomeProps = {
-  conversationName: string
-  hasSetInputs: boolean
-  isPublicVersion: boolean
-  siteInfo: SiteInfo
-  promptConfig: PromptConfig
-  onStartChat: (inputs: Record<string, any>) => void
-  canEidtInpus: boolean
-  savedInputs: Record<string, any>
-  onInputsChange: (inputs: Record<string, any>) => void
-  plan?: string
-  canReplaceLogo?: boolean
-  customConfig?: {
-    remove_webapp_brand?: boolean
-    replace_webapp_logo?: string
-  }
-}
-
-const Welcome: FC<IWelcomeProps> = ({
-  conversationName,
-  hasSetInputs,
-  isPublicVersion,
-  siteInfo,
-  promptConfig,
-  onStartChat,
-  canEidtInpus,
-  savedInputs,
-  onInputsChange,
-  customConfig,
-}) => {
-  const { t } = useTranslation()
-  const hasVar = promptConfig.prompt_variables.length > 0
-  const [isFold, setIsFold] = useState<boolean>(true)
-  const [inputs, setInputs] = useState<Record<string, any>>((() => {
-    if (hasSetInputs)
-      return savedInputs
-
-    const res: Record<string, any> = {}
-    if (promptConfig) {
-      promptConfig.prompt_variables.forEach((item) => {
-        res[item.key] = ''
-      })
-    }
-    // debugger
-    return res
-  })())
-  useEffect(() => {
-    if (!savedInputs) {
-      const res: Record<string, any> = {}
-      if (promptConfig) {
-        promptConfig.prompt_variables.forEach((item) => {
-          res[item.key] = ''
-        })
-      }
-      setInputs(res)
-    }
-    else {
-      setInputs(savedInputs)
-    }
-  }, [savedInputs])
-
-  const highLightPromoptTemplate = (() => {
-    if (!promptConfig)
-      return ''
-    const res = promptConfig.prompt_template.replace(regex, (match, p1) => {
-      return `<span class='text-gray-800 font-bold'>${inputs?.[p1] ? inputs?.[p1] : match}</span>`
-    })
-    return res
-  })()
-
-  const { notify } = useContext(ToastContext)
-  const logError = (message: string) => {
-    notify({ type: 'error', message, duration: 3000 })
-  }
-
-  const renderHeader = () => {
-    return (
-      <div className='absolute top-0 left-0 right-0 flex items-center justify-between border-b border-gray-100 mobile:h-12 tablet:h-16 px-8 bg-white'>
-        <div className='text-gray-900'>{conversationName}</div>
-      </div>
-    )
-  }
-
-  const renderInputs = () => {
-    return (
-      <div className='space-y-3'>
-        {promptConfig.prompt_variables.map(item => (
-          <div className='tablet:flex items-start mobile:space-y-2 tablet:space-y-0 mobile:text-xs tablet:text-sm' key={item.key}>
-            <label className={`flex-shrink-0 flex items-center tablet:leading-9 mobile:text-gray-700 tablet:text-gray-900 mobile:font-medium pc:font-normal ${s.formLabel}`}>{item.name}</label>
-            {item.type === 'select'
-              && (
-                <Select
-                  className='w-full'
-                  defaultValue={inputs?.[item.key]}
-                  onSelect={(i) => { setInputs({ ...inputs, [item.key]: i.value }) }}
-                  items={(item.options || []).map(i => ({ name: i, value: i }))}
-                  allowSearch={false}
-                  bgClassName='bg-gray-50'
-                />
-              )}
-            {item.type === 'string' && (
-              <input
-                placeholder={`${item.name}${!item.required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
-                value={inputs?.[item.key] || ''}
-                onChange={(e) => { setInputs({ ...inputs, [item.key]: e.target.value }) }}
-                className={'w-full flex-grow py-2 pl-3 pr-3 box-border rounded-lg bg-gray-50'}
-                maxLength={item.max_length || DEFAULT_VALUE_MAX_LEN}
-              />
-            )}
-            {item.type === 'paragraph' && (
-              <textarea
-                className="w-full h-[104px] flex-grow py-2 pl-3 pr-3 box-border rounded-lg bg-gray-50"
-                placeholder={`${item.name}${!item.required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
-                value={inputs?.[item.key] || ''}
-                onChange={(e) => { setInputs({ ...inputs, [item.key]: e.target.value }) }}
-              />
-            )}
-            {item.type === 'number' && (
-              <input
-                type='number'
-                placeholder={`${item.name}${!item.required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
-                value={inputs?.[item.key] || ''}
-                onChange={(e) => { setInputs({ ...inputs, [item.key]: e.target.value }) }}
-                className={'w-full flex-grow py-2 pl-3 pr-3 box-border rounded-lg bg-gray-50'}
-              />
-            )}
-          </div>
-        ))}
-      </div>
-    )
-  }
-
-  const canChat = () => {
-    const prompt_variables = promptConfig?.prompt_variables
-    if (!inputs || !prompt_variables || prompt_variables?.length === 0)
-      return true
-
-    let hasEmptyInput = ''
-    const requiredVars = prompt_variables?.filter(({ key, name, required }) => {
-      const res = (!key || !key.trim()) || (!name || !name.trim()) || (required || required === undefined || required === null)
-      return res
-    }) || [] // compatible with old version
-    requiredVars.forEach(({ key, name }) => {
-      if (hasEmptyInput)
-        return
-
-      if (!inputs?.[key])
-        hasEmptyInput = name
-    })
-
-    if (hasEmptyInput) {
-      logError(t('appDebug.errorMessage.valueOfVarRequired', { key: hasEmptyInput }))
-      return false
-    }
-    return !hasEmptyInput
-  }
-
-  const handleChat = () => {
-    if (!canChat())
-      return
-
-    onStartChat(inputs)
-  }
-
-  const renderNoVarPanel = () => {
-    if (isPublicVersion) {
-      return (
-        <div>
-          <AppInfo siteInfo={siteInfo} />
-          <TemplateVarPanel
-            isFold={false}
-            header={
-              <>
-                <PanelTitle
-                  title={t('share.chat.publicPromptConfigTitle')}
-                  className='mb-1'
-                />
-                <PromptTemplate html={highLightPromoptTemplate} />
-              </>
-            }
-          >
-            <ChatBtn onClick={handleChat} />
-          </TemplateVarPanel>
-        </div>
-      )
-    }
-    // private version
-    return (
-      <TemplateVarPanel
-        isFold={false}
-        header={
-          <AppInfo siteInfo={siteInfo} />
-        }
-      >
-        <ChatBtn onClick={handleChat} />
-      </TemplateVarPanel>
-    )
-  }
-
-  const renderVarPanel = () => {
-    return (
-      <TemplateVarPanel
-        isFold={false}
-        header={
-          <AppInfo siteInfo={siteInfo} />
-        }
-      >
-        {renderInputs()}
-        <ChatBtn
-          className='mt-3 mobile:ml-0 tablet:ml-[128px]'
-          onClick={handleChat}
-        />
-      </TemplateVarPanel>
-    )
-  }
-
-  const renderVarOpBtnGroup = () => {
-    return (
-      <VarOpBtnGroup
-        onConfirm={() => {
-          if (!canChat())
-            return
-
-          onInputsChange(inputs)
-          setIsFold(true)
-        }}
-        onCancel={() => {
-          setInputs(savedInputs)
-          setIsFold(true)
-        }}
-      />
-    )
-  }
-
-  const renderHasSetInputsPublic = () => {
-    if (!canEidtInpus) {
-      return (
-        <TemplateVarPanel
-          isFold={false}
-          header={
-            <>
-              <PanelTitle
-                title={t('share.chat.publicPromptConfigTitle')}
-                className='mb-1'
-              />
-              <PromptTemplate html={highLightPromoptTemplate} />
-            </>
-          }
-        />
-      )
-    }
-
-    return (
-      <TemplateVarPanel
-        isFold={isFold}
-        header={
-          <>
-            <PanelTitle
-              title={t('share.chat.publicPromptConfigTitle')}
-              className='mb-1'
-            />
-            <PromptTemplate html={highLightPromoptTemplate} />
-            {isFold && (
-              <div className='flex items-center justify-between mt-3 border-t border-indigo-100 pt-4 text-xs text-indigo-600'>
-                <span className='text-gray-700'>{t('share.chat.configStatusDes')}</span>
-                <EditBtn onClick={() => setIsFold(false)} />
-              </div>
-            )}
-          </>
-        }
-      >
-        {renderInputs()}
-        {renderVarOpBtnGroup()}
-      </TemplateVarPanel>
-    )
-  }
-
-  const renderHasSetInputsPrivate = () => {
-    if (!canEidtInpus || !hasVar)
-      return null
-
-    return (
-      <TemplateVarPanel
-        isFold={isFold}
-        header={
-          <div className='flex items-center justify-between text-indigo-600'>
-            <PanelTitle
-              title={!isFold ? t('share.chat.privatePromptConfigTitle') : t('share.chat.configStatusDes')}
-            />
-            {isFold && (
-              <EditBtn onClick={() => setIsFold(false)} />
-            )}
-          </div>
-        }
-      >
-        {renderInputs()}
-        {renderVarOpBtnGroup()}
-      </TemplateVarPanel>
-    )
-  }
-
-  const renderHasSetInputs = () => {
-    if ((!isPublicVersion && !canEidtInpus) || !hasVar)
-      return null
-
-    return (
-      <div
-        className='pt-[88px] mb-5'
-      >
-        {isPublicVersion ? renderHasSetInputsPublic() : renderHasSetInputsPrivate()}
-      </div>)
-  }
-
-  return (
-    <div className='relative mobile:min-h-[48px] tablet:min-h-[64px]'>
-      {hasSetInputs && renderHeader()}
-      <div className='mx-auto pc:w-[794px] max-w-full mobile:w-full px-3.5'>
-        {/*  Has't set inputs  */}
-        {
-          !hasSetInputs && (
-            <div className='mobile:pt-[72px] tablet:pt-[128px] pc:pt-[200px]'>
-              {hasVar
-                ? (
-                  renderVarPanel()
-                )
-                : (
-                  renderNoVarPanel()
-                )}
-            </div>
-          )
-        }
-
-        {/* Has set inputs */}
-        {hasSetInputs && renderHasSetInputs()}
-
-        {/* foot */}
-        {!hasSetInputs && (
-          <div className='mt-4 flex justify-between items-center h-8 text-xs text-gray-400'>
-
-            {siteInfo.privacy_policy
-              ? <div>{t('share.chat.privacyPolicyLeft')}
-                <a
-                  className='text-gray-500 px-1'
-                  href={siteInfo.privacy_policy}
-                  target='_blank' rel='noopener noreferrer'>{t('share.chat.privacyPolicyMiddle')}</a>
-                {t('share.chat.privacyPolicyRight')}
-              </div>
-              : <div>
-              </div>}
-            {
-              customConfig?.remove_webapp_brand
-                ? null
-                : (
-                  <a className='flex items-center pr-3 space-x-3' href="https://dify.ai/" target="_blank">
-                    <span className='uppercase'>{t('share.chat.powerBy')}</span>
-                    {
-                      customConfig?.replace_webapp_logo
-                        ? <img src={customConfig?.replace_webapp_logo} alt='logo' className='block w-auto h-5' />
-                        : <FootLogo />
-                    }
-                  </a>
-                )
-            }
-          </div>
-        )}
-      </div>
-    </div >
-  )
-}
-
-export default React.memo(Welcome)

+ 0 - 74
web/app/components/share/chat/welcome/massive-component.tsx

@@ -1,74 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React from 'react'
-import cn from 'classnames'
-import { useTranslation } from 'react-i18next'
-import {
-  PencilIcon,
-} from '@heroicons/react/24/solid'
-import s from './style.module.css'
-import type { SiteInfo } from '@/models/share'
-import Button from '@/app/components/base/button'
-import LogoSite from '@/app/components/base/logo/logo-site'
-
-export const AppInfo: FC<{ siteInfo: SiteInfo }> = ({ siteInfo }) => {
-  return (
-    <div>
-      <div className='flex items-center py-2 text-xl font-medium text-gray-700 rounded-md'>{siteInfo.icon && <span className='mr-2'><em-emoji id={siteInfo.icon} /></span>}{siteInfo.title}</div>
-      <p className='text-sm text-gray-500'>{siteInfo.description}</p>
-    </div>
-  )
-}
-
-export const PromptTemplate: FC<{ html: string }> = ({ html }) => {
-  return (
-    <div
-      className={'box-border text-sm text-gray-700'}
-      dangerouslySetInnerHTML={{ __html: html }}
-    />
-  )
-}
-
-export const StarIcon = () => (
-  <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
-    <path d="M2.75 1C2.75 0.723858 2.52614 0.5 2.25 0.5C1.97386 0.5 1.75 0.723858 1.75 1V1.75H1C0.723858 1.75 0.5 1.97386 0.5 2.25C0.5 2.52614 0.723858 2.75 1 2.75H1.75V3.5C1.75 3.77614 1.97386 4 2.25 4C2.52614 4 2.75 3.77614 2.75 3.5V2.75H3.5C3.77614 2.75 4 2.52614 4 2.25C4 1.97386 3.77614 1.75 3.5 1.75H2.75V1Z" fill="#444CE7" />
-    <path d="M2.75 8.5C2.75 8.22386 2.52614 8 2.25 8C1.97386 8 1.75 8.22386 1.75 8.5V9.25H1C0.723858 9.25 0.5 9.47386 0.5 9.75C0.5 10.0261 0.723858 10.25 1 10.25H1.75V11C1.75 11.2761 1.97386 11.5 2.25 11.5C2.52614 11.5 2.75 11.2761 2.75 11V10.25H3.5C3.77614 10.25 4 10.0261 4 9.75C4 9.47386 3.77614 9.25 3.5 9.25H2.75V8.5Z" fill="#444CE7" />
-    <path d="M6.96667 1.32051C6.8924 1.12741 6.70689 1 6.5 1C6.29311 1 6.10759 1.12741 6.03333 1.32051L5.16624 3.57494C5.01604 3.96546 4.96884 4.078 4.90428 4.1688C4.8395 4.2599 4.7599 4.3395 4.6688 4.40428C4.578 4.46884 4.46546 4.51604 4.07494 4.66624L1.82051 5.53333C1.62741 5.60759 1.5 5.79311 1.5 6C1.5 6.20689 1.62741 6.39241 1.82051 6.46667L4.07494 7.33376C4.46546 7.48396 4.578 7.53116 4.6688 7.59572C4.7599 7.6605 4.8395 7.7401 4.90428 7.8312C4.96884 7.922 5.01604 8.03454 5.16624 8.42506L6.03333 10.6795C6.1076 10.8726 6.29311 11 6.5 11C6.70689 11 6.89241 10.8726 6.96667 10.6795L7.83376 8.42506C7.98396 8.03454 8.03116 7.922 8.09572 7.8312C8.1605 7.7401 8.2401 7.6605 8.3312 7.59572C8.422 7.53116 8.53454 7.48396 8.92506 7.33376L11.1795 6.46667C11.3726 6.39241 11.5 6.20689 11.5 6C11.5 5.79311 11.3726 5.60759 11.1795 5.53333L8.92506 4.66624C8.53454 4.51604 8.422 4.46884 8.3312 4.40428C8.2401 4.3395 8.1605 4.2599 8.09572 4.1688C8.03116 4.078 7.98396 3.96546 7.83376 3.57494L6.96667 1.32051Z" fill="#444CE7" />
-  </svg>
-)
-
-export const ChatBtn: FC<{ onClick: () => void; className?: string }> = ({
-  className,
-  onClick,
-}) => {
-  const { t } = useTranslation()
-  return (
-    <Button
-      variant='primary'
-      className={cn(className, `px-4 ${s.customBtn} gap-2`)}
-      onClick={onClick}>
-      <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-        <path fillRule="evenodd" clipRule="evenodd" d="M18 10.5C18 14.366 14.418 17.5 10 17.5C8.58005 17.506 7.17955 17.1698 5.917 16.52L2 17.5L3.338 14.377C2.493 13.267 2 11.934 2 10.5C2 6.634 5.582 3.5 10 3.5C14.418 3.5 18 6.634 18 10.5ZM7 9.5H5V11.5H7V9.5ZM15 9.5H13V11.5H15V9.5ZM9 9.5H11V11.5H9V9.5Z" fill="white" />
-      </svg>
-      {t('share.chat.startChat')}
-    </Button>
-  )
-}
-
-export const EditBtn = ({ className, onClick }: { className?: string; onClick: () => void }) => {
-  const { t } = useTranslation()
-
-  return (
-    <div
-      className={cn('px-2 flex space-x-1 items-center rounded-md  cursor-pointer', className)}
-      onClick={onClick}
-    >
-      <PencilIcon className='w-3 h-3' />
-      <span>{t('common.operation.edit')}</span>
-    </div>
-  )
-}
-
-export const FootLogo = () => (
-  <LogoSite className='!h-5' />
-)

+ 0 - 22
web/app/components/share/chat/welcome/style.module.css

@@ -1,22 +0,0 @@
-.boxShodow {
-  box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
-}
-
-.bgGrayColor {
-  background-color: #F9FAFB;
-}
-
-.headerBg {
-  height: 3.5rem;
-  padding-left: 1.5rem;
-  padding-right: 1.5rem;
-}
-
-.formLabel {
-  width: 120px;
-  margin-right: 8px;
-}
-
-.customBtn {
-  width: auto;
-}

+ 0 - 13
web/app/components/share/chatbot/config-scence/index.tsx

@@ -1,13 +0,0 @@
-import type { FC } from 'react'
-import React from 'react'
-import type { IWelcomeProps } from '../welcome'
-import Welcome from '../welcome'
-
-const ConfigScene: FC<IWelcomeProps> = (props) => {
-  return (
-    <div className='mb-5 antialiased font-sans shrink-0'>
-      <Welcome {...props} />
-    </div>
-  )
-}
-export default React.memo(ConfigScene)

+ 0 - 72
web/app/components/share/chatbot/hooks/use-conversation.ts

@@ -1,72 +0,0 @@
-import { useState } from 'react'
-import produce from 'immer'
-import { useGetState } from 'ahooks'
-import type { ConversationItem } from '@/models/share'
-
-const storageConversationIdKey = 'conversationIdInfo'
-
-type ConversationInfoType = Omit<ConversationItem, 'inputs' | 'id'>
-function useConversation() {
-  const [conversationList, setConversationList] = useState<ConversationItem[]>([])
-  const [pinnedConversationList, setPinnedConversationList] = useState<ConversationItem[]>([])
-  const [currConversationId, doSetCurrConversationId, getCurrConversationId] = useGetState<string>('-1')
-  // when set conversation id, we do not have set appId
-  const setCurrConversationId = (id: string, appId: string, isSetToLocalStroge = true, newConversationName = '') => {
-    doSetCurrConversationId(id)
-    if (isSetToLocalStroge && id !== '-1') {
-      // conversationIdInfo: {[appId1]: conversationId1, [appId2]: conversationId2}
-      const conversationIdInfo = globalThis.localStorage?.getItem(storageConversationIdKey) ? JSON.parse(globalThis.localStorage?.getItem(storageConversationIdKey) || '') : {}
-      conversationIdInfo[appId] = id
-      globalThis.localStorage?.setItem(storageConversationIdKey, JSON.stringify(conversationIdInfo))
-    }
-  }
-
-  const getConversationIdFromStorage = (appId: string) => {
-    const conversationIdInfo = globalThis.localStorage?.getItem(storageConversationIdKey) ? JSON.parse(globalThis.localStorage?.getItem(storageConversationIdKey) || '') : {}
-    const id = conversationIdInfo[appId]
-    return id
-  }
-
-  const isNewConversation = currConversationId === '-1'
-  // input can be updated by user
-  const [newConversationInputs, setNewConversationInputs] = useState<Record<string, any> | null>(null)
-  const resetNewConversationInputs = () => {
-    if (!newConversationInputs)
-      return
-    setNewConversationInputs(produce(newConversationInputs, (draft) => {
-      Object.keys(draft).forEach((key) => {
-        draft[key] = ''
-      })
-    }))
-  }
-  const [existConversationInputs, setExistConversationInputs] = useState<Record<string, any> | null>(null)
-  const currInputs = isNewConversation ? newConversationInputs : existConversationInputs
-  const setCurrInputs = isNewConversation ? setNewConversationInputs : setExistConversationInputs
-
-  // info is muted
-  const [newConversationInfo, setNewConversationInfo] = useState<ConversationInfoType | null>(null)
-  const [existConversationInfo, setExistConversationInfo] = useState<ConversationInfoType | null>(null)
-  const currConversationInfo = isNewConversation ? newConversationInfo : existConversationInfo
-
-  return {
-    conversationList,
-    setConversationList,
-    pinnedConversationList,
-    setPinnedConversationList,
-    currConversationId,
-    getCurrConversationId,
-    setCurrConversationId,
-    getConversationIdFromStorage,
-    isNewConversation,
-    currInputs,
-    newConversationInputs,
-    existConversationInputs,
-    resetNewConversationInputs,
-    setCurrInputs,
-    currConversationInfo,
-    setNewConversationInfo,
-    setExistConversationInfo,
-  }
-}
-
-export default useConversation

+ 0 - 824
web/app/components/share/chatbot/index.tsx

@@ -1,824 +0,0 @@
-/* eslint-disable @typescript-eslint/no-use-before-define */
-'use client'
-import type { FC } from 'react'
-import React, { useEffect, useRef, useState } from 'react'
-import cn from 'classnames'
-import { useTranslation } from 'react-i18next'
-import { useContext } from 'use-context-selector'
-import produce, { setAutoFreeze } from 'immer'
-import { useBoolean, useGetState } from 'ahooks'
-import { checkOrSetAccessToken } from '../utils'
-import AppUnavailable from '../../base/app-unavailable'
-import useConversation from './hooks/use-conversation'
-import { ToastContext } from '@/app/components/base/toast'
-import ConfigScene from '@/app/components/share/chatbot/config-scence'
-import Header from '@/app/components/share/header'
-import {
-  fetchAppInfo,
-  fetchAppMeta,
-  fetchAppParams,
-  fetchChatList,
-  fetchConversations,
-  fetchSuggestedQuestions,
-  generationConversationName,
-  sendChatMessage,
-  stopChatMessageResponding,
-  updateFeedback,
-} from '@/service/share'
-import { addFileInfos, sortAgentSorts } from '@/app/components/tools/utils'
-import type { AppMeta, ConversationItem, SiteInfo } from '@/models/share'
-import type { PromptConfig, SuggestedQuestionsAfterAnswerConfig } from '@/models/debug'
-import type { Feedbacktype, IChatItem } from '@/app/components/app/chat/type'
-import Chat from '@/app/components/app/chat'
-import { changeLanguage } from '@/i18n/i18next-config'
-import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
-import Loading from '@/app/components/base/loading'
-import { replaceStringWithValues } from '@/app/components/app/configuration/prompt-value-panel'
-import { userInputsFormToPromptVariables } from '@/utils/model-config'
-import type { InstalledApp } from '@/models/explore'
-import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
-import LogoHeader from '@/app/components/base/logo/logo-embeded-chat-header'
-import LogoAvatar from '@/app/components/base/logo/logo-embeded-chat-avatar'
-import type { VisionFile, VisionSettings } from '@/types/app'
-import { Resolution, TransferMethod } from '@/types/app'
-import type { Annotation as AnnotationType } from '@/models/log'
-
-export type IMainProps = {
-  isInstalledApp?: boolean
-  installedAppInfo?: InstalledApp
-}
-
-const Main: FC<IMainProps> = ({
-  isInstalledApp = false,
-  installedAppInfo,
-}) => {
-  const { t } = useTranslation()
-  const media = useBreakpoints()
-  const isMobile = media === MediaType.mobile
-
-  /*
-  * app info
-  */
-  const [appUnavailable, setAppUnavailable] = useState<boolean>(false)
-  const [isUnknownReason, setIsUnknwonReason] = useState<boolean>(false)
-  const [appId, setAppId] = useState<string>('')
-  const [isPublicVersion, setIsPublicVersion] = useState<boolean>(true)
-  const [siteInfo, setSiteInfo] = useState<SiteInfo | null>()
-  const [promptConfig, setPromptConfig] = useState<PromptConfig | null>(null)
-  const [inited, setInited] = useState<boolean>(false)
-  const [plan, setPlan] = useState<string>('basic') // basic/plus/pro
-  const [canReplaceLogo, setCanReplaceLogo] = useState<boolean>(false)
-  const [customConfig, setCustomConfig] = useState<any>(null)
-  const [appMeta, setAppMeta] = useState<AppMeta | null>(null)
-
-  // Can Use metadata(https://beta.nextjs.org/docs/api-reference/metadata) to set title. But it only works in server side client.
-  useEffect(() => {
-    if (siteInfo?.title) {
-      if (canReplaceLogo)
-        document.title = `${siteInfo.title}`
-      else
-        document.title = `${siteInfo.title} - Powered by Dify`
-    }
-  }, [siteInfo?.title, canReplaceLogo])
-
-  // onData change thought (the produce obj). https://github.com/immerjs/immer/issues/576
-  useEffect(() => {
-    setAutoFreeze(false)
-    return () => {
-      setAutoFreeze(true)
-    }
-  }, [])
-
-  /*
-  * conversation info
-  */
-  const [allConversationList, setAllConversationList] = useState<ConversationItem[]>([])
-  const [isClearConversationList, { setTrue: clearConversationListTrue, setFalse: clearConversationListFalse }] = useBoolean(false)
-  const [isClearPinnedConversationList, { setTrue: clearPinnedConversationListTrue, setFalse: clearPinnedConversationListFalse }] = useBoolean(false)
-  const {
-    conversationList,
-    setConversationList,
-    pinnedConversationList,
-    setPinnedConversationList,
-    currConversationId,
-    getCurrConversationId,
-    setCurrConversationId,
-    getConversationIdFromStorage,
-    isNewConversation,
-    currConversationInfo,
-    currInputs,
-    newConversationInputs,
-    // existConversationInputs,
-    resetNewConversationInputs,
-    setCurrInputs,
-    setNewConversationInfo,
-    setExistConversationInfo,
-  } = useConversation()
-  const [hasMore, setHasMore] = useState<boolean>(true)
-  const [hasPinnedMore, setHasPinnedMore] = useState<boolean>(true)
-
-  const onMoreLoaded = ({ data: conversations, has_more }: any) => {
-    setHasMore(has_more)
-    if (isClearConversationList) {
-      setConversationList(conversations)
-      clearConversationListFalse()
-    }
-    else {
-      setConversationList([...conversationList, ...conversations])
-    }
-  }
-
-  const onPinnedMoreLoaded = ({ data: conversations, has_more }: any) => {
-    setHasPinnedMore(has_more)
-    if (isClearPinnedConversationList) {
-      setPinnedConversationList(conversations)
-      clearPinnedConversationListFalse()
-    }
-    else {
-      setPinnedConversationList([...pinnedConversationList, ...conversations])
-    }
-  }
-
-  const [controlUpdateConversationList, setControlUpdateConversationList] = useState(0)
-
-  const noticeUpdateList = () => {
-    setHasMore(true)
-    clearConversationListTrue()
-
-    setHasPinnedMore(true)
-    clearPinnedConversationListTrue()
-
-    setControlUpdateConversationList(Date.now())
-  }
-  const [suggestedQuestionsAfterAnswerConfig, setSuggestedQuestionsAfterAnswerConfig] = useState<SuggestedQuestionsAfterAnswerConfig | null>(null)
-  const [speechToTextConfig, setSpeechToTextConfig] = useState<SuggestedQuestionsAfterAnswerConfig | null>(null)
-  const [textToSpeechConfig, setTextToSpeechConfig] = useState<SuggestedQuestionsAfterAnswerConfig | null>(null)
-  const [citationConfig, setCitationConfig] = useState<SuggestedQuestionsAfterAnswerConfig | null>(null)
-
-  const [conversationIdChangeBecauseOfNew, setConversationIdChangeBecauseOfNew, getConversationIdChangeBecauseOfNew] = useGetState(false)
-  const [isChatStarted, { setTrue: setChatStarted, setFalse: setChatNotStarted }] = useBoolean(false)
-  const handleStartChat = (inputs: Record<string, any>) => {
-    createNewChat()
-    setConversationIdChangeBecauseOfNew(true)
-    setCurrInputs(inputs)
-    setChatStarted()
-    // parse variables in introduction
-    setChatList(generateNewChatListWithOpenstatement('', inputs))
-  }
-  const hasSetInputs = (() => {
-    if (!isNewConversation)
-      return true
-
-    return isChatStarted
-  })()
-
-  // const conversationName = currConversationInfo?.name || t('share.chat.newChatDefaultName') as string
-  const conversationIntroduction = currConversationInfo?.introduction || ''
-
-  const handleConversationSwitch = () => {
-    if (!inited)
-      return
-    if (!appId) {
-      // wait for appId
-      setTimeout(handleConversationSwitch, 100)
-      return
-    }
-
-    // update inputs of current conversation
-    let notSyncToStateIntroduction = ''
-    let notSyncToStateInputs: Record<string, any> | undefined | null = {}
-    if (!isNewConversation) {
-      const item = allConversationList.find(item => item.id === currConversationId)
-      notSyncToStateInputs = item?.inputs || {}
-      setCurrInputs(notSyncToStateInputs)
-      notSyncToStateIntroduction = item?.introduction || ''
-      setExistConversationInfo({
-        name: item?.name || '',
-        introduction: notSyncToStateIntroduction,
-      })
-    }
-    else {
-      notSyncToStateInputs = newConversationInputs
-      setCurrInputs(notSyncToStateInputs)
-    }
-
-    // update chat list of current conversation
-    if (!isNewConversation && !conversationIdChangeBecauseOfNew && !isResponding) {
-      fetchChatList(currConversationId, isInstalledApp, installedAppInfo?.id).then((res: any) => {
-        const { data } = res
-        const newChatList: IChatItem[] = generateNewChatListWithOpenstatement(notSyncToStateIntroduction, notSyncToStateInputs)
-
-        data.forEach((item: any) => {
-          newChatList.push({
-            id: `question-${item.id}`,
-            content: item.query,
-            isAnswer: false,
-            message_files: item.message_files?.filter((file: any) => file.belongs_to === 'user') || [],
-          })
-          newChatList.push({
-            id: item.id,
-            content: item.answer,
-            agent_thoughts: addFileInfos(item.agent_thoughts ? sortAgentSorts(item.agent_thoughts) : item.agent_thoughts, item.message_files),
-            feedback: item.feedback,
-            isAnswer: true,
-            citation: item.retriever_resources,
-            message_files: item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || [],
-          })
-        })
-        setChatList(newChatList)
-      })
-    }
-
-    if (isNewConversation && isChatStarted)
-      setChatList(generateNewChatListWithOpenstatement())
-
-    setControlFocus(Date.now())
-  }
-  useEffect(handleConversationSwitch, [currConversationId, inited])
-
-  /*
-  * chat info. chat is under conversation.
-  */
-  const [chatList, setChatList, getChatList] = useGetState<IChatItem[]>([])
-  const chatListDomRef = useRef<HTMLDivElement>(null)
-
-  useEffect(() => {
-    // scroll to bottom
-    if (chatListDomRef.current)
-      chatListDomRef.current.scrollTop = chatListDomRef.current.scrollHeight
-  }, [chatList, currConversationId])
-  // user can not edit inputs if user had send message
-  const canEditInputs = !chatList.some(item => item.isAnswer === false) && isNewConversation
-  const createNewChat = async () => {
-    // if new chat is already exist, do not create new chat
-    abortController?.abort()
-    setRespondingFalse()
-    if (conversationList.some(item => item.id === '-1'))
-      return
-
-    setConversationList(produce(conversationList, (draft) => {
-      draft.unshift({
-        id: '-1',
-        name: t('share.chat.newChatDefaultName'),
-        inputs: newConversationInputs,
-        introduction: conversationIntroduction,
-      })
-    }))
-  }
-
-  // sometime introduction is not applied to state
-  const generateNewChatListWithOpenstatement = (introduction?: string, inputs?: Record<string, any> | null) => {
-    let caculatedIntroduction = introduction || conversationIntroduction || ''
-    const caculatedPromptVariables = inputs || currInputs || null
-    if (caculatedIntroduction && caculatedPromptVariables)
-      caculatedIntroduction = replaceStringWithValues(caculatedIntroduction, promptConfig?.prompt_variables || [], caculatedPromptVariables)
-
-    const openstatement = {
-      id: `${Date.now()}`,
-      content: caculatedIntroduction,
-      isAnswer: true,
-      feedbackDisabled: true,
-      isOpeningStatement: isPublicVersion,
-    }
-    if (caculatedIntroduction)
-      return [openstatement]
-
-    return []
-  }
-
-  const fetchAllConversations = () => {
-    return fetchConversations(isInstalledApp, installedAppInfo?.id, undefined, undefined, 100)
-  }
-
-  const fetchInitData = async () => {
-    if (!isInstalledApp)
-      await checkOrSetAccessToken()
-
-    return Promise.all([isInstalledApp
-      ? {
-        app_id: installedAppInfo?.id,
-        site: {
-          title: installedAppInfo?.app.name,
-          prompt_public: false,
-          copyright: '',
-        },
-        plan: 'basic',
-      }
-      : fetchAppInfo(), fetchAllConversations(), fetchAppParams(isInstalledApp, installedAppInfo?.id), fetchAppMeta(isInstalledApp, installedAppInfo?.id)])
-  }
-
-  // init
-  useEffect(() => {
-    (async () => {
-      try {
-        const [appData, conversationData, appParams, appMeta]: any = await fetchInitData()
-        setAppMeta(appMeta)
-        const { app_id: appId, site: siteInfo, plan, can_replace_logo, custom_config }: any = appData
-        setAppId(appId)
-        setPlan(plan)
-        setCanReplaceLogo(can_replace_logo)
-        setCustomConfig(custom_config)
-        const tempIsPublicVersion = siteInfo.prompt_public
-        setIsPublicVersion(tempIsPublicVersion)
-        const prompt_template = ''
-        // handle current conversation id
-        const { data: allConversations } = conversationData as { data: ConversationItem[]; has_more: boolean }
-        const _conversationId = getConversationIdFromStorage(appId)
-        const isNotNewConversation = allConversations.some(item => item.id === _conversationId)
-        setAllConversationList(allConversations)
-        // fetch new conversation info
-        const { user_input_form, opening_statement: introduction, suggested_questions_after_answer, speech_to_text, text_to_speech, retriever_resource, file_upload, sensitive_word_avoidance }: any = appParams
-        setVisionConfig({
-          ...file_upload.image,
-          image_file_size_limit: appParams?.system_parameters?.image_file_size_limit,
-        })
-        const prompt_variables = userInputsFormToPromptVariables(user_input_form)
-        if (siteInfo.default_language)
-          changeLanguage(siteInfo.default_language)
-
-        setNewConversationInfo({
-          name: t('share.chat.newChatDefaultName'),
-          introduction,
-        })
-        setSiteInfo(siteInfo as SiteInfo)
-        setPromptConfig({
-          prompt_template,
-          prompt_variables,
-        } as PromptConfig)
-        setSuggestedQuestionsAfterAnswerConfig(suggested_questions_after_answer)
-        setSpeechToTextConfig(speech_to_text)
-        setTextToSpeechConfig(text_to_speech)
-        setCitationConfig(retriever_resource)
-
-        // setConversationList(conversations as ConversationItem[])
-
-        if (isNotNewConversation)
-          setCurrConversationId(_conversationId, appId, false)
-
-        setInited(true)
-      }
-      catch (e: any) {
-        if (e.status === 404) {
-          setAppUnavailable(true)
-        }
-        else {
-          setIsUnknwonReason(true)
-          setAppUnavailable(true)
-        }
-      }
-    })()
-  }, [])
-
-  const [isResponding, { setTrue: setRespondingTrue, setFalse: setRespondingFalse }] = useBoolean(false)
-  const [abortController, setAbortController] = useState<AbortController | null>(null)
-  const { notify } = useContext(ToastContext)
-  const logError = (message: string) => {
-    notify({ type: 'error', message })
-  }
-
-  const checkCanSend = () => {
-    if (currConversationId !== '-1')
-      return true
-
-    const prompt_variables = promptConfig?.prompt_variables
-    const inputs = currInputs
-    if (!inputs || !prompt_variables || prompt_variables?.length === 0)
-      return true
-
-    let hasEmptyInput = ''
-    const requiredVars = prompt_variables?.filter(({ key, name, required }) => {
-      const res = (!key || !key.trim()) || (!name || !name.trim()) || (required || required === undefined || required === null)
-      return res
-    }) || [] // compatible with old version
-    requiredVars.forEach(({ key, name }) => {
-      if (hasEmptyInput)
-        return
-
-      if (!inputs?.[key])
-        hasEmptyInput = name
-    })
-
-    if (hasEmptyInput) {
-      logError(t('appDebug.errorMessage.valueOfVarRequired', { key: hasEmptyInput }))
-      return false
-    }
-    return !hasEmptyInput
-  }
-
-  const [controlFocus, setControlFocus] = useState(0)
-  const [isShowSuggestion, setIsShowSuggestion] = useState(false)
-  const doShowSuggestion = isShowSuggestion && !isResponding
-  const [suggestQuestions, setSuggestQuestions] = useState<string[]>([])
-  const [messageTaskId, setMessageTaskId] = useState('')
-  const [hasStopResponded, setHasStopResponded, getHasStopResponded] = useGetState(false)
-  const [isRespondingConIsCurrCon, setIsRespondingConCurrCon, getIsRespondingConIsCurrCon] = useGetState(true)
-  const [shouldReload, setShouldReload] = useState(false)
-  const [userQuery, setUserQuery] = useState('')
-  const [visionConfig, setVisionConfig] = useState<VisionSettings>({
-    enabled: false,
-    number_limits: 2,
-    detail: Resolution.low,
-    transfer_methods: [TransferMethod.local_file],
-  })
-
-  const updateCurrentQA = ({
-    responseItem,
-    questionId,
-    placeholderAnswerId,
-    questionItem,
-  }: {
-    responseItem: IChatItem
-    questionId: string
-    placeholderAnswerId: string
-    questionItem: IChatItem
-  }) => {
-    // closesure new list is outdated.
-    const newListWithAnswer = produce(
-      getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
-      (draft) => {
-        if (!draft.find(item => item.id === questionId))
-          draft.push({ ...questionItem })
-
-        draft.push({ ...responseItem })
-      })
-    setChatList(newListWithAnswer)
-  }
-
-  const handleSend = async (message: string, files?: VisionFile[]) => {
-    if (isResponding) {
-      notify({ type: 'info', message: t('appDebug.errorMessage.waitForResponse') })
-      return
-    }
-
-    if (files?.find(item => item.transfer_method === TransferMethod.local_file && !item.upload_file_id)) {
-      notify({ type: 'info', message: t('appDebug.errorMessage.waitForImgUpload') })
-      return false
-    }
-    const data: Record<string, any> = {
-      inputs: currInputs,
-      query: message,
-      conversation_id: isNewConversation ? null : currConversationId,
-    }
-
-    if (visionConfig.enabled && files && files?.length > 0) {
-      data.files = files.map((item) => {
-        if (item.transfer_method === TransferMethod.local_file) {
-          return {
-            ...item,
-            url: '',
-          }
-        }
-        return item
-      })
-    }
-
-    // qustion
-    const questionId = `question-${Date.now()}`
-    const questionItem = {
-      id: questionId,
-      content: message,
-      isAnswer: false,
-      message_files: files,
-    }
-
-    const placeholderAnswerId = `answer-placeholder-${Date.now()}`
-    const placeholderAnswerItem = {
-      id: placeholderAnswerId,
-      content: '',
-      isAnswer: true,
-    }
-
-    const newList = [...getChatList(), questionItem, placeholderAnswerItem]
-    setChatList(newList)
-
-    let isAgentMode = false
-
-    // answer
-    const responseItem: IChatItem = {
-      id: `${Date.now()}`,
-      content: '',
-      agent_thoughts: [],
-      message_files: [],
-      isAnswer: true,
-    }
-    let hasSetResponseId = false
-
-    const prevTempNewConversationId = getCurrConversationId() || '-1'
-    let tempNewConversationId = prevTempNewConversationId
-
-    setHasStopResponded(false)
-    setRespondingTrue()
-    setIsShowSuggestion(false)
-    sendChatMessage(data, {
-      getAbortController: (abortController) => {
-        setAbortController(abortController)
-      },
-      onData: (message: string, isFirstMessage: boolean, { conversationId: newConversationId, messageId, taskId }: any) => {
-        if (!isAgentMode) {
-          responseItem.content = responseItem.content + message
-        }
-        else {
-          const lastThought = responseItem.agent_thoughts?.[responseItem.agent_thoughts?.length - 1]
-          if (lastThought)
-            lastThought.thought = lastThought.thought + message // need immer setAutoFreeze
-        }
-        if (messageId && !hasSetResponseId) {
-          responseItem.id = messageId
-          hasSetResponseId = true
-        }
-
-        if (isFirstMessage && newConversationId)
-          tempNewConversationId = newConversationId
-
-        setMessageTaskId(taskId)
-        // has switched to other conversation
-        if (prevTempNewConversationId !== getCurrConversationId()) {
-          setIsRespondingConCurrCon(false)
-          return
-        }
-        updateCurrentQA({
-          responseItem,
-          questionId,
-          placeholderAnswerId,
-          questionItem,
-        })
-      },
-      async onCompleted(hasError?: boolean) {
-        if (hasError)
-          return
-
-        if (getConversationIdChangeBecauseOfNew()) {
-          const { data: allConversations }: any = await fetchAllConversations()
-          const newItem: any = await generationConversationName(isInstalledApp, installedAppInfo?.id, allConversations[0].id)
-          const newAllConversations = produce(allConversations, (draft: any) => {
-            draft[0].name = newItem.name
-          })
-          setAllConversationList(newAllConversations as any)
-          noticeUpdateList()
-        }
-        setConversationIdChangeBecauseOfNew(false)
-        resetNewConversationInputs()
-        setChatNotStarted()
-        setCurrConversationId(tempNewConversationId, appId, true)
-        if (suggestedQuestionsAfterAnswerConfig?.enabled && !getHasStopResponded()) {
-          const { data }: any = await fetchSuggestedQuestions(responseItem.id, isInstalledApp, installedAppInfo?.id)
-          setSuggestQuestions(data)
-          setIsShowSuggestion(true)
-        }
-        setRespondingFalse()
-      },
-      onFile(file) {
-        const lastThought = responseItem.agent_thoughts?.[responseItem.agent_thoughts?.length - 1]
-        if (lastThought)
-          lastThought.message_files = [...(lastThought as any).message_files, { ...file }]
-
-        updateCurrentQA({
-          responseItem,
-          questionId,
-          placeholderAnswerId,
-          questionItem,
-        })
-      },
-      onThought(thought) {
-        isAgentMode = true
-        const response = responseItem as any
-        if (thought.message_id && !hasSetResponseId) {
-          response.id = thought.message_id
-          hasSetResponseId = true
-        }
-        // responseItem.id = thought.message_id;
-        if (response.agent_thoughts.length === 0) {
-          response.agent_thoughts.push(thought)
-        }
-        else {
-          const lastThought = response.agent_thoughts[response.agent_thoughts.length - 1]
-          // thought changed but still the same thought, so update.
-          if (lastThought.id === thought.id) {
-            thought.thought = lastThought.thought
-            thought.message_files = lastThought.message_files
-            responseItem.agent_thoughts![response.agent_thoughts.length - 1] = thought
-          }
-          else {
-            responseItem.agent_thoughts!.push(thought)
-          }
-        }
-        // has switched to other conversation
-        if (prevTempNewConversationId !== getCurrConversationId()) {
-          setIsRespondingConCurrCon(false)
-          return false
-        }
-
-        updateCurrentQA({
-          responseItem,
-          questionId,
-          placeholderAnswerId,
-          questionItem,
-        })
-      },
-      onMessageEnd: (messageEnd) => {
-        if (messageEnd.metadata?.annotation_reply) {
-          responseItem.id = messageEnd.id
-          responseItem.annotation = ({
-            id: messageEnd.metadata.annotation_reply.id,
-            authorName: messageEnd.metadata.annotation_reply.account.name,
-          } as AnnotationType)
-          const newListWithAnswer = produce(
-            getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
-            (draft) => {
-              if (!draft.find(item => item.id === questionId))
-                draft.push({ ...questionItem })
-
-              draft.push({
-                ...responseItem,
-              })
-            })
-          setChatList(newListWithAnswer)
-          return
-        }
-        // not support show citation
-        // responseItem.citation = messageEnd.retriever_resources
-        if (!isInstalledApp)
-          return
-        const newListWithAnswer = produce(
-          getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
-          (draft) => {
-            if (!draft.find(item => item.id === questionId))
-              draft.push({ ...questionItem })
-
-            draft.push({ ...responseItem })
-          })
-        setChatList(newListWithAnswer)
-      },
-      onMessageReplace: (messageReplace) => {
-        if (isInstalledApp) {
-          responseItem.content = messageReplace.answer
-        }
-        else {
-          setChatList(produce(
-            getChatList(),
-            (draft) => {
-              const current = draft.find(item => item.id === messageReplace.id)
-
-              if (current)
-                current.content = messageReplace.answer
-            },
-          ))
-        }
-      },
-      onError() {
-        setRespondingFalse()
-        // role back placeholder answer
-        setChatList(produce(getChatList(), (draft) => {
-          draft.splice(draft.findIndex(item => item.id === placeholderAnswerId), 1)
-        }))
-      },
-    }, isInstalledApp, installedAppInfo?.id)
-  }
-
-  const handleFeedback = async (messageId: string, feedback: Feedbacktype) => {
-    await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating } }, isInstalledApp, installedAppInfo?.id)
-    const newChatList = chatList.map((item) => {
-      if (item.id === messageId) {
-        return {
-          ...item,
-          feedback,
-        }
-      }
-      return item
-    })
-    setChatList(newChatList)
-    notify({ type: 'success', message: t('common.api.success') })
-  }
-
-  const handleReload = () => {
-    setCurrConversationId('-1', appId, false)
-    setChatNotStarted()
-    setShouldReload(false)
-    createNewChat()
-  }
-
-  const handleConversationIdChange = (id: string) => {
-    if (id === '-1') {
-      createNewChat()
-      setConversationIdChangeBecauseOfNew(true)
-    }
-    else {
-      setConversationIdChangeBecauseOfNew(false)
-    }
-    // trigger handleConversationSwitch
-    setCurrConversationId(id, appId)
-    setIsShowSuggestion(false)
-  }
-
-  const difyIcon = (
-    <LogoHeader />
-  )
-
-  if (appUnavailable)
-    return <AppUnavailable isUnknownReason={isUnknownReason} />
-
-  if (!appId || !siteInfo || !promptConfig) {
-    return <div className='flex h-screen w-full'>
-      <Loading type='app' />
-    </div>
-  }
-
-  return (
-    <div>
-      <Header
-        title={siteInfo.title}
-        icon=''
-        customerIcon={difyIcon}
-        icon_background={siteInfo.icon_background || ''}
-        isEmbedScene={true}
-        isMobile={isMobile}
-        onCreateNewChat={() => handleConversationIdChange('-1')}
-      />
-
-      <div className={'flex bg-white overflow-hidden'}>
-        <div className={cn(
-          isInstalledApp ? 'h-full' : 'h-[calc(100vh_-_3rem)]',
-          'flex-grow flex flex-col overflow-y-auto',
-        )
-        }>
-          <ConfigScene
-            // conversationName={conversationName}
-            hasSetInputs={hasSetInputs}
-            isPublicVersion={isPublicVersion}
-            siteInfo={siteInfo}
-            promptConfig={promptConfig}
-            onStartChat={handleStartChat}
-            canEditInputs={canEditInputs}
-            savedInputs={currInputs as Record<string, any>}
-            onInputsChange={setCurrInputs}
-            plan={plan}
-            canReplaceLogo={canReplaceLogo}
-            customConfig={customConfig}
-          ></ConfigScene>
-          {
-            shouldReload && (
-              <div className='flex items-center justify-between mb-5 px-4 py-2 bg-[#FEF0C7]'>
-                <div className='flex items-center text-xs font-medium text-[#DC6803]'>
-                  <AlertTriangle className='mr-2 w-4 h-4' />
-                  {t('share.chat.temporarySystemIssue')}
-                </div>
-                <div
-                  className='flex items-center px-3 h-7 bg-white shadow-xs rounded-md text-xs font-medium text-gray-700 cursor-pointer'
-                  onClick={handleReload}
-                >
-                  {t('share.chat.tryToSolve')}
-                </div>
-              </div>
-            )
-          }
-          {
-            hasSetInputs && (
-              <div className={cn(doShowSuggestion ? 'pb-[140px]' : (isResponding ? 'pb-[113px]' : 'pb-[76px]'), 'relative grow h-[200px] pc:w-[794px] max-w-full mobile:w-full mx-auto mb-3.5 overflow-hidden')}>
-                <div className='h-full overflow-y-auto' ref={chatListDomRef}>
-                  <Chat
-                    chatList={chatList}
-                    query={userQuery}
-                    onQueryChange={setUserQuery}
-                    onSend={handleSend}
-                    isHideFeedbackEdit
-                    onFeedback={handleFeedback}
-                    isResponding={isResponding}
-                    canStopResponding={!!messageTaskId && isRespondingConIsCurrCon}
-                    abortResponding={async () => {
-                      await stopChatMessageResponding(appId, messageTaskId, isInstalledApp, installedAppInfo?.id)
-                      setHasStopResponded(true)
-                      setRespondingFalse()
-                    }}
-                    checkCanSend={checkCanSend}
-                    controlFocus={controlFocus}
-                    isShowSuggestion={doShowSuggestion}
-                    suggestionList={suggestQuestions}
-                    displayScene='web'
-                    isShowSpeechToText={speechToTextConfig?.enabled}
-                    isShowTextToSpeech={textToSpeechConfig?.enabled}
-                    isShowCitation={citationConfig?.enabled && isInstalledApp}
-                    answerIcon={<LogoAvatar className='relative shrink-0' />}
-                    visionConfig={visionConfig}
-                    allToolIcons={appMeta?.tool_icons || {}}
-                    customDisclaimer={siteInfo.custom_disclaimer}
-                  />
-                </div>
-              </div>)
-          }
-
-          {/* {isShowConfirm && (
-            <Confirm
-              title={t('share.chat.deleteConversation.title')}
-              content={t('share.chat.deleteConversation.content')}
-              isShow={isShowConfirm}
-              onClose={hideConfirm}
-              onConfirm={didDelete}
-              onCancel={hideConfirm}
-            />
-          )} */}
-        </div>
-      </div>
-    </div>
-  )
-}
-export default React.memo(Main)

+ 0 - 28
web/app/components/share/chatbot/sidebar/app-info/index.tsx

@@ -1,28 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React from 'react'
-import cn from 'classnames'
-import { appDefaultIconBackground } from '@/config/index'
-import AppIcon from '@/app/components/base/app-icon'
-
-export type IAppInfoProps = {
-  className?: string
-  icon: string
-  icon_background?: string
-  name: string
-}
-
-const AppInfo: FC<IAppInfoProps> = ({
-  className,
-  icon,
-  icon_background,
-  name,
-}) => {
-  return (
-    <div className={cn(className, 'flex items-center space-x-3')}>
-      <AppIcon size="small" icon={icon} background={icon_background || appDefaultIconBackground} />
-      <div className='w-0 grow text-sm font-semibold text-gray-800 overflow-hidden  text-ellipsis whitespace-nowrap'>{name}</div>
-    </div>
-  )
-}
-export default React.memo(AppInfo)

+ 0 - 3
web/app/components/share/chatbot/sidebar/card.module.css

@@ -1,3 +0,0 @@
-.card:hover {
-  background: linear-gradient(0deg, rgba(235, 245, 255, 0.4), rgba(235, 245, 255, 0.4)), #FFFFFF;
-}

+ 0 - 19
web/app/components/share/chatbot/sidebar/card.tsx

@@ -1,19 +0,0 @@
-import React from 'react'
-import { useTranslation } from 'react-i18next'
-import s from './card.module.css'
-
-type PropType = {
-  children: React.ReactNode
-  text?: string
-}
-function Card({ children, text }: PropType) {
-  const { t } = useTranslation()
-  return (
-    <div className={`${s.card} box-border w-full flex flex-col items-start px-4 py-3 rounded-lg border-solid border border-gray-200  cursor-pointer hover:border-primary-300`}>
-      <div className='text-gray-400 font-medium text-xs mb-2'>{text ?? t('share.chat.powerBy')}</div>
-      {children}
-    </div>
-  )
-}
-
-export default Card

+ 0 - 152
web/app/components/share/chatbot/sidebar/index.tsx

@@ -1,152 +0,0 @@
-import React, { useEffect, useState } from 'react'
-import type { FC } from 'react'
-import { useTranslation } from 'react-i18next'
-import {
-  PencilSquareIcon,
-} from '@heroicons/react/24/outline'
-import cn from 'classnames'
-import Button from '../../../base/button'
-import List from './list'
-import AppInfo from '@/app/components/share/chat/sidebar/app-info'
-// import Card from './card'
-import type { ConversationItem, SiteInfo } from '@/models/share'
-import { fetchConversations } from '@/service/share'
-
-export type ISidebarProps = {
-  copyRight: string
-  currentId: string
-  onCurrentIdChange: (id: string) => void
-  list: ConversationItem[]
-  isClearConversationList: boolean
-  pinnedList: ConversationItem[]
-  isClearPinnedConversationList: boolean
-  isInstalledApp: boolean
-  installedAppId?: string
-  siteInfo: SiteInfo
-  onMoreLoaded: (res: { data: ConversationItem[]; has_more: boolean }) => void
-  onPinnedMoreLoaded: (res: { data: ConversationItem[]; has_more: boolean }) => void
-  isNoMore: boolean
-  isPinnedNoMore: boolean
-  onPin: (id: string) => void
-  onUnpin: (id: string) => void
-  controlUpdateList: number
-  onDelete: (id: string) => void
-}
-
-const Sidebar: FC<ISidebarProps> = ({
-  copyRight,
-  currentId,
-  onCurrentIdChange,
-  list,
-  isClearConversationList,
-  pinnedList,
-  isClearPinnedConversationList,
-  isInstalledApp,
-  installedAppId,
-  siteInfo,
-  onMoreLoaded,
-  onPinnedMoreLoaded,
-  isNoMore,
-  isPinnedNoMore,
-  onPin,
-  onUnpin,
-  controlUpdateList,
-  onDelete,
-}) => {
-  const { t } = useTranslation()
-  const [hasPinned, setHasPinned] = useState(false)
-
-  const checkHasPinned = async () => {
-    const { data }: any = await fetchConversations(isInstalledApp, installedAppId, undefined, true)
-    setHasPinned(data.length > 0)
-  }
-
-  useEffect(() => {
-    checkHasPinned()
-  }, [])
-
-  useEffect(() => {
-    if (controlUpdateList !== 0)
-      checkHasPinned()
-  }, [controlUpdateList])
-
-  const maxListHeight = isInstalledApp ? 'max-h-[30vh]' : 'max-h-[40vh]'
-
-  return (
-    <div
-      className={
-        cn(
-          isInstalledApp ? 'tablet:h-[calc(100vh_-_74px)]' : 'tablet:h-[calc(100vh_-_3rem)]',
-          'shrink-0 flex flex-col bg-white pc:w-[244px] tablet:w-[192px] mobile:w-[240px]  border-r border-gray-200 mobile:h-screen',
-        )
-      }
-    >
-      {isInstalledApp && (
-        <AppInfo
-          className='my-4 px-4'
-          name={siteInfo.title || ''}
-          icon={siteInfo.icon || ''}
-          icon_background={siteInfo.icon_background}
-        />
-      )}
-      <div className="flex flex-shrink-0 p-4 !pb-0">
-        <Button
-          onClick={() => { onCurrentIdChange('-1') }}
-          variant='secondary-accent'
-          className="group w-full flex-shrink-0">
-          <PencilSquareIcon className="mr-2 h-4 w-4" /> {t('share.chat.newChat')}
-        </Button>
-      </div>
-      <div className={'flex-grow flex flex-col h-0 overflow-y-auto overflow-x-hidden'}>
-        {/* pinned list */}
-        {hasPinned && (
-          <div className={cn('mt-4 px-4', list.length === 0 && 'flex flex-col flex-grow')}>
-            <div className='mb-1.5 leading-[18px] text-xs text-gray-500 font-medium uppercase'>{t('share.chat.pinnedTitle')}</div>
-            <List
-              className={cn(list.length > 0 ? maxListHeight : 'flex-grow')}
-              currentId={currentId}
-              onCurrentIdChange={onCurrentIdChange}
-              list={pinnedList}
-              isClearConversationList={isClearPinnedConversationList}
-              isInstalledApp={isInstalledApp}
-              installedAppId={installedAppId}
-              onMoreLoaded={onPinnedMoreLoaded}
-              isNoMore={isPinnedNoMore}
-              isPinned={true}
-              onPinChanged={id => onUnpin(id)}
-              controlUpdate={controlUpdateList + 1}
-              onDelete={onDelete}
-            />
-          </div>
-        )}
-        {/* unpinned list */}
-        <div className={cn('mt-4 px-4', !hasPinned && 'flex flex-col flex-grow')}>
-          {(hasPinned && list.length > 0) && (
-            <div className='mb-1.5 leading-[18px] text-xs text-gray-500 font-medium uppercase'>{t('share.chat.unpinnedTitle')}</div>
-          )}
-          <List
-            className={cn(hasPinned ? maxListHeight : 'flex-grow')}
-            currentId={currentId}
-            onCurrentIdChange={onCurrentIdChange}
-            list={list}
-            isClearConversationList={isClearConversationList}
-            isInstalledApp={isInstalledApp}
-            installedAppId={installedAppId}
-            onMoreLoaded={onMoreLoaded}
-            isNoMore={isNoMore}
-            isPinned={false}
-            onPinChanged={id => onPin(id)}
-            controlUpdate={controlUpdateList + 1}
-            onDelete={onDelete}
-          />
-        </div>
-
-      </div>
-      <div className="flex flex-shrink-0 pr-4 pb-4 pl-4">
-        <div className="text-gray-400 font-normal text-xs">© {copyRight} {(new Date()).getFullYear()}</div>
-      </div>
-    </div>
-  )
-}
-
-export default React.memo(Sidebar)

+ 0 - 115
web/app/components/share/chatbot/sidebar/list/index.tsx

@@ -1,115 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React, { useRef } from 'react'
-import {
-  ChatBubbleOvalLeftEllipsisIcon,
-} from '@heroicons/react/24/outline'
-import { useInfiniteScroll } from 'ahooks'
-import { ChatBubbleOvalLeftEllipsisIcon as ChatBubbleOvalLeftEllipsisSolidIcon } from '@heroicons/react/24/solid'
-import cn from 'classnames'
-import s from './style.module.css'
-import type { ConversationItem } from '@/models/share'
-import { fetchConversations } from '@/service/share'
-import ItemOperation from '@/app/components/explore/item-operation'
-
-export type IListProps = {
-  className: string
-  currentId: string
-  onCurrentIdChange: (id: string) => void
-  list: ConversationItem[]
-  isClearConversationList: boolean
-  isInstalledApp: boolean
-  installedAppId?: string
-  onMoreLoaded: (res: { data: ConversationItem[]; has_more: boolean }) => void
-  isNoMore: boolean
-  isPinned: boolean
-  onPinChanged: (id: string) => void
-  controlUpdate: number
-  onDelete: (id: string) => void
-}
-
-const List: FC<IListProps> = ({
-  className,
-  currentId,
-  onCurrentIdChange,
-  list,
-  isClearConversationList,
-  isInstalledApp,
-  installedAppId,
-  onMoreLoaded,
-  isNoMore,
-  isPinned,
-  onPinChanged,
-  controlUpdate,
-  onDelete,
-}) => {
-  const listRef = useRef<HTMLDivElement>(null)
-
-  useInfiniteScroll(
-    async () => {
-      if (!isNoMore) {
-        const lastId = !isClearConversationList ? list[list.length - 1]?.id : undefined
-        const { data: conversations, has_more }: any = await fetchConversations(isInstalledApp, installedAppId, lastId, isPinned)
-        onMoreLoaded({ data: conversations, has_more })
-      }
-      return { list: [] }
-    },
-    {
-      target: listRef,
-      isNoMore: () => {
-        return isNoMore
-      },
-      reloadDeps: [isNoMore, controlUpdate],
-    },
-  )
-  return (
-    <nav
-      ref={listRef}
-      className={cn(className, 'shrink-0 space-y-1 bg-white overflow-y-auto')}
-    >
-      {list.map((item) => {
-        const isCurrent = item.id === currentId
-        const ItemIcon
-            = isCurrent ? ChatBubbleOvalLeftEllipsisSolidIcon : ChatBubbleOvalLeftEllipsisIcon
-        return (
-          <div
-            onClick={() => onCurrentIdChange(item.id)}
-            key={item.id}
-            className={cn(s.item,
-              isCurrent
-                ? 'bg-primary-50 text-primary-600'
-                : 'text-gray-700 hover:bg-gray-200 hover:text-gray-700',
-              'group flex justify-between items-center rounded-md px-2 py-2 text-sm font-medium cursor-pointer',
-            )}
-          >
-            <div className='flex items-center w-0 grow'>
-              <ItemIcon
-                className={cn(
-                  isCurrent
-                    ? 'text-primary-600'
-                    : 'text-gray-400 group-hover:text-gray-500',
-                  'mr-3 h-5 w-5 flex-shrink-0',
-                )}
-                aria-hidden="true"
-              />
-              <span>{item.name}</span>
-            </div>
-
-            {item.id !== '-1' && (
-              <div className={cn(s.opBtn, 'shrink-0')} onClick={e => e.stopPropagation()}>
-                <ItemOperation
-                  isPinned={isPinned}
-                  togglePin={() => onPinChanged(item.id)}
-                  isShowDelete
-                  onDelete={() => onDelete(item.id)}
-                />
-              </div>
-            )}
-          </div>
-        )
-      })}
-    </nav>
-  )
-}
-
-export default React.memo(List)

+ 0 - 7
web/app/components/share/chatbot/sidebar/list/style.module.css

@@ -1,7 +0,0 @@
-.opBtn {
-  visibility: hidden;
-}
-
-.item:hover .opBtn {
-  visibility: visible;
-}

+ 0 - 77
web/app/components/share/chatbot/value-panel/index.tsx

@@ -1,77 +0,0 @@
-'use client'
-import type { FC, ReactNode } from 'react'
-import React from 'react'
-import cn from 'classnames'
-import { useTranslation } from 'react-i18next'
-import s from './style.module.css'
-import { StarIcon } from '@/app/components/share/chatbot/welcome/massive-component'
-import Button from '@/app/components/base/button'
-
-export type ITemplateVarPanelProps = {
-  className?: string
-  header: ReactNode
-  children?: ReactNode | null
-  isFold: boolean
-}
-
-const TemplateVarPanel: FC<ITemplateVarPanelProps> = ({
-  className,
-  header,
-  children,
-  isFold,
-}) => {
-  return (
-    <div className={cn(isFold ? 'border border-indigo-100' : s.boxShodow, className, 'rounded-xl ')}>
-      {/* header */}
-      <div
-        className={cn(isFold && 'rounded-b-xl', 'rounded-t-xl px-6 py-4 bg-indigo-25 text-xs')}
-      >
-        {header}
-      </div>
-      {/* body */}
-      {!isFold && children && (
-        <div className='rounded-b-xl p-6'>
-          {children}
-        </div>
-      )}
-    </div>
-  )
-}
-
-export const PanelTitle: FC<{ title: string; className?: string }> = ({
-  title,
-  className,
-}) => {
-  return (
-    <div className={cn(className, 'flex items-center space-x-1 text-indigo-600')}>
-      <StarIcon />
-      <span className='text-xs'>{title}</span>
-    </div>
-  )
-}
-
-export const VarOpBtnGroup: FC<{ className?: string; onConfirm: () => void; onCancel: () => void }> = ({
-  className,
-  onConfirm,
-  onCancel,
-}) => {
-  const { t } = useTranslation()
-
-  return (
-    <div className={cn(className, 'flex mt-3 space-x-2 mobile:ml-0 tablet:ml-[128px] text-sm')}>
-      <Button
-        variant='primary'
-        onClick={onConfirm}
-      >
-        {t('common.operation.save')}
-      </Button>
-      <Button
-        onClick={onCancel}
-      >
-        {t('common.operation.cancel')}
-      </Button>
-    </div >
-  )
-}
-
-export default React.memo(TemplateVarPanel)

+ 0 - 3
web/app/components/share/chatbot/value-panel/style.module.css

@@ -1,3 +0,0 @@
-.boxShodow {
-  box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
-}

+ 0 - 389
web/app/components/share/chatbot/welcome/index.tsx

@@ -1,389 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React, { useEffect, useState } from 'react'
-import { useTranslation } from 'react-i18next'
-import { useContext } from 'use-context-selector'
-import TemplateVarPanel, { PanelTitle, VarOpBtnGroup } from '../value-panel'
-import s from './style.module.css'
-import { AppInfo, ChatBtn, EditBtn, FootLogo, PromptTemplate } from './massive-component'
-import type { SiteInfo } from '@/models/share'
-import type { PromptConfig } from '@/models/debug'
-import { ToastContext } from '@/app/components/base/toast'
-import Select from '@/app/components/base/select'
-import { DEFAULT_VALUE_MAX_LEN } from '@/config'
-
-// regex to match the {{}} and replace it with a span
-const regex = /\{\{([^}]+)\}\}/g
-
-export type IWelcomeProps = {
-  // conversationName: string
-  hasSetInputs: boolean
-  isPublicVersion: boolean
-  siteInfo: SiteInfo
-  promptConfig: PromptConfig
-  onStartChat: (inputs: Record<string, any>) => void
-  canEditInputs: boolean
-  savedInputs: Record<string, any>
-  onInputsChange: (inputs: Record<string, any>) => void
-  plan: string
-  canReplaceLogo?: boolean
-  customConfig?: {
-    remove_webapp_brand?: boolean
-    replace_webapp_logo?: string
-  }
-}
-
-const Welcome: FC<IWelcomeProps> = ({
-  // conversationName,
-  hasSetInputs,
-  isPublicVersion,
-  siteInfo,
-  promptConfig,
-  onStartChat,
-  canEditInputs,
-  savedInputs,
-  onInputsChange,
-  customConfig,
-}) => {
-  const { t } = useTranslation()
-  const hasVar = promptConfig.prompt_variables.length > 0
-  const [isFold, setIsFold] = useState<boolean>(true)
-  const [inputs, setInputs] = useState<Record<string, any>>((() => {
-    if (hasSetInputs)
-      return savedInputs
-
-    const res: Record<string, any> = {}
-    if (promptConfig) {
-      promptConfig.prompt_variables.forEach((item) => {
-        res[item.key] = ''
-      })
-    }
-    // debugger
-    return res
-  })())
-  useEffect(() => {
-    if (!savedInputs) {
-      const res: Record<string, any> = {}
-      if (promptConfig) {
-        promptConfig.prompt_variables.forEach((item) => {
-          res[item.key] = ''
-        })
-      }
-      setInputs(res)
-    }
-    else {
-      setInputs(savedInputs)
-    }
-  }, [savedInputs])
-
-  const highLightPromoptTemplate = (() => {
-    if (!promptConfig)
-      return ''
-    const res = promptConfig.prompt_template.replace(regex, (match, p1) => {
-      return `<span class='text-gray-800 font-bold'>${inputs?.[p1] ? inputs?.[p1] : match}</span>`
-    })
-    return res
-  })()
-
-  const { notify } = useContext(ToastContext)
-  const logError = (message: string) => {
-    notify({ type: 'error', message, duration: 3000 })
-  }
-
-  // const renderHeader = () => {
-  //   return (
-  //     <div className='absolute top-0 left-0 right-0 flex items-center justify-between border-b border-gray-100 mobile:h-12 tablet:h-16 px-8 bg-white'>
-  //       <div className='text-gray-900'>{conversationName}</div>
-  //     </div>
-  //   )
-  // }
-
-  const renderInputs = () => {
-    return (
-      <div className='space-y-3'>
-        {promptConfig.prompt_variables.map(item => (
-          <div className='tablet:flex items-start  mobile:space-y-2 tablet:space-y-0 mobile:text-xs tablet:text-sm' key={item.key}>
-            <label className={`flex-shrink-0 flex items-center tablet:leading-9 mobile:text-gray-700 tablet:text-gray-900 mobile:font-medium pc:font-normal ${s.formLabel}`}>{item.name}</label>
-            {item.type === 'select'
-              && (
-                <Select
-                  className='w-full'
-                  defaultValue={inputs?.[item.key]}
-                  onSelect={(i) => { setInputs({ ...inputs, [item.key]: i.value }) }}
-                  items={(item.options || []).map(i => ({ name: i, value: i }))}
-                  allowSearch={false}
-                  bgClassName='bg-gray-50'
-                />
-              )
-            }
-            {item.type === 'string' && (
-              <input
-                placeholder={`${item.name}${!item.required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
-                value={inputs?.[item.key] || ''}
-                onChange={(e) => { setInputs({ ...inputs, [item.key]: e.target.value }) }}
-                className={'w-full flex-grow py-2 pl-3 pr-3 box-border rounded-lg bg-gray-50'}
-                maxLength={item.max_length || DEFAULT_VALUE_MAX_LEN}
-              />
-            )}
-            {item.type === 'paragraph' && (
-              <textarea
-                className="w-full h-[104px] flex-grow py-2 pl-3 pr-3 box-border rounded-lg bg-gray-50"
-                placeholder={`${item.name}${!item.required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
-                value={inputs?.[item.key] || ''}
-                onChange={(e) => { setInputs({ ...inputs, [item.key]: e.target.value }) }}
-              />
-            )}
-            {item.type === 'number' && (
-              <input
-                type='number'
-                placeholder={`${item.name}${!item.required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
-                value={inputs?.[item.key] || ''}
-                onChange={(e) => { setInputs({ ...inputs, [item.key]: e.target.value }) }}
-                className={'w-full flex-grow py-2 pl-3 pr-3 box-border rounded-lg bg-gray-50'}
-              />
-            )}
-          </div>
-        ))}
-      </div>
-    )
-  }
-
-  const canChat = () => {
-    const prompt_variables = promptConfig?.prompt_variables
-    if (!inputs || !prompt_variables || prompt_variables?.length === 0)
-      return true
-
-    let hasEmptyInput = ''
-    const requiredVars = prompt_variables?.filter(({ key, name, required }) => {
-      const res = (!key || !key.trim()) || (!name || !name.trim()) || (required || required === undefined || required === null)
-      return res
-    }) || [] // compatible with old version
-    requiredVars.forEach(({ key, name }) => {
-      if (hasEmptyInput)
-        return
-
-      if (!inputs?.[key])
-        hasEmptyInput = name
-    })
-
-    if (hasEmptyInput) {
-      logError(t('appDebug.errorMessage.valueOfVarRequired', { key: hasEmptyInput }))
-      return false
-    }
-    return !hasEmptyInput
-  }
-
-  const handleChat = () => {
-    if (!canChat())
-      return
-
-    onStartChat(inputs)
-  }
-
-  const renderNoVarPanel = () => {
-    if (isPublicVersion) {
-      return (
-        <div>
-          <AppInfo siteInfo={siteInfo} />
-          <TemplateVarPanel
-            isFold={false}
-            header={
-              <>
-                <PanelTitle
-                  title={t('share.chat.publicPromptConfigTitle')}
-                  className='mb-1'
-                />
-                <PromptTemplate html={highLightPromoptTemplate} />
-              </>
-            }
-          >
-            <ChatBtn onClick={handleChat} />
-          </TemplateVarPanel>
-        </div>
-      )
-    }
-    // private version
-    return (
-      <TemplateVarPanel
-        isFold={false}
-        header={
-          <AppInfo siteInfo={siteInfo} />
-        }
-      >
-        <ChatBtn onClick={handleChat} />
-      </TemplateVarPanel>
-    )
-  }
-
-  const renderVarPanel = () => {
-    return (
-      <TemplateVarPanel
-        isFold={false}
-        header={
-          <AppInfo siteInfo={siteInfo} />
-        }
-      >
-        {renderInputs()}
-        <ChatBtn
-          className='mt-3 mobile:ml-0 tablet:ml-[128px]'
-          onClick={handleChat}
-        />
-      </TemplateVarPanel>
-    )
-  }
-
-  const renderVarOpBtnGroup = () => {
-    return (
-      <VarOpBtnGroup
-        onConfirm={() => {
-          if (!canChat())
-            return
-
-          onInputsChange(inputs)
-          setIsFold(true)
-        }}
-        onCancel={() => {
-          setInputs(savedInputs)
-          setIsFold(true)
-        }}
-      />
-    )
-  }
-
-  const renderHasSetInputsPublic = () => {
-    if (!canEditInputs) {
-      return (
-        <TemplateVarPanel
-          isFold={false}
-          header={
-            <>
-              <PanelTitle
-                title={t('share.chat.publicPromptConfigTitle')}
-                className='mb-1'
-              />
-              <PromptTemplate html={highLightPromoptTemplate} />
-            </>
-          }
-        />
-      )
-    }
-
-    return (
-      <TemplateVarPanel
-        isFold={isFold}
-        header={
-          <>
-            <PanelTitle
-              title={t('share.chat.publicPromptConfigTitle')}
-              className='mb-1'
-            />
-            <PromptTemplate html={highLightPromoptTemplate} />
-            {isFold && (
-              <div className='flex items-center justify-between mt-3 border-t border-indigo-100 pt-4 text-xs text-indigo-600'>
-                <span className='text-gray-700'>{t('share.chat.configStatusDes')}</span>
-                <EditBtn onClick={() => setIsFold(false)} />
-              </div>
-            )}
-          </>
-        }
-      >
-        {renderInputs()}
-        {renderVarOpBtnGroup()}
-      </TemplateVarPanel>
-    )
-  }
-
-  const renderHasSetInputsPrivate = () => {
-    if (!canEditInputs || !hasVar)
-      return null
-
-    return (
-      <TemplateVarPanel
-        isFold={isFold}
-        header={
-          <div className='flex items-center justify-between text-indigo-600'>
-            <PanelTitle
-              title={!isFold ? t('share.chat.privatePromptConfigTitle') : t('share.chat.configStatusDes')}
-            />
-            {isFold && (
-              <EditBtn onClick={() => setIsFold(false)} />
-            )}
-          </div>
-        }
-      >
-        {renderInputs()}
-        {renderVarOpBtnGroup()}
-      </TemplateVarPanel>
-    )
-  }
-
-  const renderHasSetInputs = () => {
-    if ((!isPublicVersion && !canEditInputs) || !hasVar)
-      return null
-
-    return (
-      <div
-        className='pt-[88px] mb-5'
-      >
-        {isPublicVersion ? renderHasSetInputsPublic() : renderHasSetInputsPrivate()}
-      </div>)
-  }
-
-  return (
-    <div className='relative tablet:min-h-[64px]'>
-      {/* {hasSetInputs && renderHeader()} */}
-      <div className='mx-auto pc:w-[794px] max-w-full mobile:w-full px-3.5'>
-        {/*  Has't set inputs  */}
-        {
-          !hasSetInputs && (
-            <div className='mobile:pt-[72px] tablet:pt-[128px] pc:pt-[200px]'>
-              {hasVar
-                ? (
-                  renderVarPanel()
-                )
-                : (
-                  renderNoVarPanel()
-                )}
-            </div>
-          )
-        }
-
-        {/* Has set inputs */}
-        {hasSetInputs && renderHasSetInputs()}
-
-        {/* foot */}
-        {!hasSetInputs && (
-          <div className='mt-4 flex justify-between items-center h-8 text-xs text-gray-400'>
-
-            {siteInfo.privacy_policy
-              ? <div>{t('share.chat.privacyPolicyLeft')}
-                <a
-                  className='text-gray-500 px-1'
-                  href={siteInfo.privacy_policy}
-                  target='_blank' rel='noopener noreferrer'>{t('share.chat.privacyPolicyMiddle')}</a>
-                {t('share.chat.privacyPolicyRight')}
-              </div>
-              : <div>
-              </div>}
-            {
-              customConfig?.remove_webapp_brand
-                ? null
-                : (
-                  <a className='flex items-center pr-3 space-x-3' href="https://dify.ai/" target="_blank">
-                    <span className='uppercase'>{t('share.chat.powerBy')}</span>
-                    {
-                      customConfig?.replace_webapp_logo
-                        ? <img src={customConfig?.replace_webapp_logo} alt='logo' className='block w-auto h-5' />
-                        : <FootLogo />
-                    }
-                  </a>
-                )
-            }
-          </div>
-        )}
-      </div>
-    </div >
-  )
-}
-
-export default React.memo(Welcome)

+ 0 - 75
web/app/components/share/chatbot/welcome/massive-component.tsx

@@ -1,75 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React from 'react'
-import cn from 'classnames'
-import { useTranslation } from 'react-i18next'
-import {
-  PencilIcon,
-} from '@heroicons/react/24/solid'
-import s from './style.module.css'
-import type { SiteInfo } from '@/models/share'
-import Button from '@/app/components/base/button'
-import LogoSite from '@/app/components/base/logo/logo-site'
-
-export const AppInfo: FC<{ siteInfo: SiteInfo }> = ({ siteInfo }) => {
-  const { t } = useTranslation()
-  return (
-    <div>
-      <div className='flex items-center py-2 text-xl font-medium text-gray-700 rounded-md'>👏 {t('share.common.welcome')} {siteInfo.title}</div>
-      <p className='text-sm text-gray-500'>{siteInfo.description}</p>
-    </div>
-  )
-}
-
-export const PromptTemplate: FC<{ html: string }> = ({ html }) => {
-  return (
-    <div
-      className={' box-border text-sm text-gray-700'}
-      dangerouslySetInnerHTML={{ __html: html }}
-    ></div>
-  )
-}
-
-export const StarIcon = () => (
-  <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
-    <path d="M2.75 1C2.75 0.723858 2.52614 0.5 2.25 0.5C1.97386 0.5 1.75 0.723858 1.75 1V1.75H1C0.723858 1.75 0.5 1.97386 0.5 2.25C0.5 2.52614 0.723858 2.75 1 2.75H1.75V3.5C1.75 3.77614 1.97386 4 2.25 4C2.52614 4 2.75 3.77614 2.75 3.5V2.75H3.5C3.77614 2.75 4 2.52614 4 2.25C4 1.97386 3.77614 1.75 3.5 1.75H2.75V1Z" fill="#444CE7" />
-    <path d="M2.75 8.5C2.75 8.22386 2.52614 8 2.25 8C1.97386 8 1.75 8.22386 1.75 8.5V9.25H1C0.723858 9.25 0.5 9.47386 0.5 9.75C0.5 10.0261 0.723858 10.25 1 10.25H1.75V11C1.75 11.2761 1.97386 11.5 2.25 11.5C2.52614 11.5 2.75 11.2761 2.75 11V10.25H3.5C3.77614 10.25 4 10.0261 4 9.75C4 9.47386 3.77614 9.25 3.5 9.25H2.75V8.5Z" fill="#444CE7" />
-    <path d="M6.96667 1.32051C6.8924 1.12741 6.70689 1 6.5 1C6.29311 1 6.10759 1.12741 6.03333 1.32051L5.16624 3.57494C5.01604 3.96546 4.96884 4.078 4.90428 4.1688C4.8395 4.2599 4.7599 4.3395 4.6688 4.40428C4.578 4.46884 4.46546 4.51604 4.07494 4.66624L1.82051 5.53333C1.62741 5.60759 1.5 5.79311 1.5 6C1.5 6.20689 1.62741 6.39241 1.82051 6.46667L4.07494 7.33376C4.46546 7.48396 4.578 7.53116 4.6688 7.59572C4.7599 7.6605 4.8395 7.7401 4.90428 7.8312C4.96884 7.922 5.01604 8.03454 5.16624 8.42506L6.03333 10.6795C6.1076 10.8726 6.29311 11 6.5 11C6.70689 11 6.89241 10.8726 6.96667 10.6795L7.83376 8.42506C7.98396 8.03454 8.03116 7.922 8.09572 7.8312C8.1605 7.7401 8.2401 7.6605 8.3312 7.59572C8.422 7.53116 8.53454 7.48396 8.92506 7.33376L11.1795 6.46667C11.3726 6.39241 11.5 6.20689 11.5 6C11.5 5.79311 11.3726 5.60759 11.1795 5.53333L8.92506 4.66624C8.53454 4.51604 8.422 4.46884 8.3312 4.40428C8.2401 4.3395 8.1605 4.2599 8.09572 4.1688C8.03116 4.078 7.98396 3.96546 7.83376 3.57494L6.96667 1.32051Z" fill="#444CE7" />
-  </svg>
-)
-
-export const ChatBtn: FC<{ onClick: () => void; className?: string }> = ({
-  className,
-  onClick,
-}) => {
-  const { t } = useTranslation()
-  return (
-    <Button
-      variant='primary'
-      className={cn(className, `!p-0 space-x-2 flex items-center ${s.customBtn}`)}
-      onClick={onClick}>
-      <svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
-        <path fillRule="evenodd" clipRule="evenodd" d="M18 10.5C18 14.366 14.418 17.5 10 17.5C8.58005 17.506 7.17955 17.1698 5.917 16.52L2 17.5L3.338 14.377C2.493 13.267 2 11.934 2 10.5C2 6.634 5.582 3.5 10 3.5C14.418 3.5 18 6.634 18 10.5ZM7 9.5H5V11.5H7V9.5ZM15 9.5H13V11.5H15V9.5ZM9 9.5H11V11.5H9V9.5Z" fill="white" />
-      </svg>
-      {t('share.chat.startChat')}
-    </Button>
-  )
-}
-
-export const EditBtn = ({ className, onClick }: { className?: string; onClick: () => void }) => {
-  const { t } = useTranslation()
-
-  return (
-    <div
-      className={cn('px-2 flex space-x-1 items-center rounded-md  cursor-pointer', className)}
-      onClick={onClick}
-    >
-      <PencilIcon className='w-3 h-3' />
-      <span>{t('common.operation.edit')}</span>
-    </div>
-  )
-}
-
-export const FootLogo = () => (
-  <LogoSite className='!h-5' />
-)

+ 0 - 22
web/app/components/share/chatbot/welcome/style.module.css

@@ -1,22 +0,0 @@
-.boxShodow {
-  box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
-}
-
-.bgGrayColor {
-  background-color: #F9FAFB;
-}
-
-.headerBg {
-  height: 3.5rem;
-  padding-left: 1.5rem;
-  padding-right: 1.5rem;
-}
-
-.formLabel {
-  width: 120px;
-  margin-right: 8px;
-}
-
-.customBtn {
-  width: 136px;
-}

+ 0 - 88
web/app/components/share/header.tsx

@@ -1,88 +0,0 @@
-import type { FC } from 'react'
-import React from 'react'
-import {
-  Bars3Icon,
-  PencilSquareIcon,
-} from '@heroicons/react/24/solid'
-import { useTranslation } from 'react-i18next'
-import AppIcon from '@/app/components/base/app-icon'
-import { ReplayIcon } from '@/app/components/app/chat/icon-component'
-import Tooltip from '@/app/components/base/tooltip'
-
-export type IHeaderProps = {
-  title: string
-  customerIcon?: React.ReactNode
-  icon: string
-  icon_background: string
-  isMobile?: boolean
-  isEmbedScene?: boolean
-  onShowSideBar?: () => void
-  onCreateNewChat?: () => void
-}
-const Header: FC<IHeaderProps> = ({
-  title,
-  isMobile,
-  customerIcon,
-  icon,
-  icon_background,
-  isEmbedScene = false,
-  onShowSideBar,
-  onCreateNewChat,
-}) => {
-  const { t } = useTranslation()
-  if (!isMobile)
-    return null
-
-  if (isEmbedScene) {
-    return (
-      <div
-        className={`
-          shrink-0 flex items-center justify-between h-14 px-4 bg-gray-100 
-          bg-gradient-to-r from-blue-600 to-sky-500
-        `}
-      >
-        <div className="flex items-center space-x-2">
-          {customerIcon || <AppIcon size="small" icon={icon} background={icon_background} />}
-          <div
-            className={'text-sm font-bold text-white'}
-          >
-            {title}
-          </div>
-        </div>
-        <Tooltip
-          selector={'embed-scene-restart-button'}
-          htmlContent={t('share.chat.resetChat')}
-          position='top'
-        >
-          <div className='flex cursor-pointer hover:rounded-lg hover:bg-black/5 w-8 h-8 items-center justify-center' onClick={() => {
-            onCreateNewChat?.()
-          }}>
-            <ReplayIcon className="h-4 w-4 text-sm font-bold text-white" />
-          </div>
-        </Tooltip>
-      </div>
-    )
-  }
-
-  return (
-    <div className="shrink-0 flex items-center justify-between h-14 px-4 bg-gray-100">
-      <div
-        className='flex items-center justify-center h-8 w-8 cursor-pointer'
-        onClick={() => onShowSideBar?.()}
-      >
-        <Bars3Icon className="h-4 w-4 text-gray-500" />
-      </div>
-      <div className='flex items-center space-x-2'>
-        <AppIcon size="small" icon={icon} background={icon_background} />
-        <div className=" text-sm text-gray-800 font-bold">{title}</div>
-      </div>
-      <div className='flex items-center justify-center h-8 w-8 cursor-pointer'
-        onClick={() => onCreateNewChat?.()}
-      >
-        <PencilSquareIcon className="h-4 w-4 text-gray-500" />
-      </div>
-    </div>
-  )
-}
-
-export default React.memo(Header)

+ 1 - 1
web/app/components/share/text-generation/result/content.tsx

@@ -1,7 +1,7 @@
 import type { FC } from 'react'
 import React from 'react'
 import Header from './header'
-import type { Feedbacktype } from '@/app/components/app/chat/type'
+import type { Feedbacktype } from '@/app/components/base/chat/chat/type'
 import { format } from '@/service/base'
 
 export type IResultProps = {

+ 1 - 1
web/app/components/share/text-generation/result/header.tsx

@@ -4,7 +4,7 @@ import React from 'react'
 import { useTranslation } from 'react-i18next'
 import { ClipboardDocumentIcon, HandThumbDownIcon, HandThumbUpIcon } from '@heroicons/react/24/outline'
 import copy from 'copy-to-clipboard'
-import type { Feedbacktype } from '@/app/components/app/chat/type'
+import type { Feedbacktype } from '@/app/components/base/chat/chat/type'
 import Button from '@/app/components/base/button'
 import Toast from '@/app/components/base/toast'
 import Tooltip from '@/app/components/base/tooltip'

+ 1 - 1
web/app/components/share/text-generation/result/index.tsx

@@ -9,7 +9,7 @@ import TextGenerationRes from '@/app/components/app/text-generate/item'
 import NoData from '@/app/components/share/text-generation/no-data'
 import Toast from '@/app/components/base/toast'
 import { sendCompletionMessage, sendWorkflowMessage, updateFeedback } from '@/service/share'
-import type { Feedbacktype } from '@/app/components/app/chat/type'
+import type { Feedbacktype } from '@/app/components/base/chat/chat/type'
 import Loading from '@/app/components/base/loading'
 import type { PromptConfig } from '@/models/debug'
 import type { InstalledApp } from '@/models/explore'

+ 1 - 1
web/app/components/tools/utils/index.ts

@@ -1,4 +1,4 @@
-import type { ThoughtItem } from '../../app/chat/type'
+import type { ThoughtItem } from '@/app/components/base/chat/chat/type'
 import type { VisionFile } from '@/types/app'
 
 export const sortAgentSorts = (list: ThoughtItem[]) => {

+ 5 - 5
web/app/components/workflow/panel/chat-record/index.tsx

@@ -14,7 +14,7 @@ import { useWorkflowRun } from '../../hooks'
 import UserInput from './user-input'
 import Chat from '@/app/components/base/chat/chat'
 import type { ChatItem } from '@/app/components/base/chat/types'
-import { fetchConvesationMessages } from '@/service/debug'
+import { fetchConversationMessages } from '@/service/debug'
 import { useStore as useAppStore } from '@/app/components/app/store'
 import Loading from '@/app/components/base/loading'
 
@@ -51,11 +51,11 @@ const ChatRecord = () => {
     return res
   }, [chatList])
 
-  const handleFetchConvesationMessages = useCallback(async () => {
+  const handleFetchConversationMessages = useCallback(async () => {
     if (appDetail && currentConversationID) {
       try {
         setFetched(false)
-        const res = await fetchConvesationMessages(appDetail.id, currentConversationID)
+        const res = await fetchConversationMessages(appDetail.id, currentConversationID)
         setFetched(true)
         setChatList((res as any).data)
       }
@@ -65,8 +65,8 @@ const ChatRecord = () => {
     }
   }, [appDetail, currentConversationID])
   useEffect(() => {
-    handleFetchConvesationMessages()
-  }, [currentConversationID, appDetail, handleFetchConvesationMessages])
+    handleFetchConversationMessages()
+  }, [currentConversationID, appDetail, handleFetchConversationMessages])
 
   return (
     <div

+ 1 - 1
web/app/components/workflow/run/output-panel.tsx

@@ -3,7 +3,7 @@ import type { FC } from 'react'
 import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
 import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
 import { Markdown } from '@/app/components/base/markdown'
-import LoadingAnim from '@/app/components/app/chat/loading-anim'
+import LoadingAnim from '@/app/components/base/chat/chat/loading-anim'
 
 type OutputPanelProps = {
   isRunning?: boolean

+ 1 - 1
web/app/components/workflow/run/result-text.tsx

@@ -3,7 +3,7 @@ import type { FC } from 'react'
 import { useTranslation } from 'react-i18next'
 import { ImageIndentLeft } from '@/app/components/base/icons/src/vender/line/editor'
 import { Markdown } from '@/app/components/base/markdown'
-import LoadingAnim from '@/app/components/app/chat/loading-anim'
+import LoadingAnim from '@/app/components/base/chat/chat/loading-anim'
 
 type ResultTextProps = {
   isRunning?: boolean

+ 1 - 1
web/service/base.ts

@@ -1,6 +1,6 @@
 import { API_PREFIX, IS_CE_EDITION, PUBLIC_API_PREFIX } from '@/config'
 import Toast from '@/app/components/base/toast'
-import type { AnnotationReply, MessageEnd, MessageReplace, ThoughtItem } from '@/app/components/app/chat/type'
+import type { AnnotationReply, MessageEnd, MessageReplace, ThoughtItem } from '@/app/components/base/chat/chat/type'
 import type { VisionFile } from '@/types/app'
 import type {
   IterationFinishedResponse,

+ 1 - 1
web/service/debug.ts

@@ -55,7 +55,7 @@ export const fetchSuggestedQuestions = (appId: string, messageId: string, getAbo
   )
 }
 
-export const fetchConvesationMessages = (appId: string, conversation_id: string, getAbortController?: any) => {
+export const fetchConversationMessages = (appId: string, conversation_id: string, getAbortController?: any) => {
   return get(`apps/${appId}/chat-messages`, {
     params: {
       conversation_id,

+ 1 - 1
web/service/share.ts

@@ -3,7 +3,7 @@ import {
   del as consoleDel, get as consoleGet, patch as consolePatch, post as consolePost,
   delPublic as del, getPublic as get, patchPublic as patch, postPublic as post, ssePost,
 } from './base'
-import type { Feedbacktype } from '@/app/components/app/chat/type'
+import type { Feedbacktype } from '@/app/components/base/chat/chat/type'
 import type {
   AppConversationData,
   AppData,

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio