diff --git a/web/app/components/base/chat/chat-with-history/hooks.tsx b/web/app/components/base/chat/chat-with-history/hooks.tsx index cdd046b9ec..22668fcc2e 100644 --- a/web/app/components/base/chat/chat-with-history/hooks.tsx +++ b/web/app/components/base/chat/chat-with-history/hooks.tsx @@ -478,7 +478,7 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { notify({ type: 'success', message: t('common.api.success') }) }, [isInstalledApp, appId, t, notify]) - const [showChatMemory, setShowChatMemory] = useState(true) + const [showChatMemory, setShowChatMemory] = useState(false) return { isInstalledApp, diff --git a/web/app/components/base/chat/embedded-chatbot/context.tsx b/web/app/components/base/chat/embedded-chatbot/context.tsx index 544da253af..f5a888a2ab 100644 --- a/web/app/components/base/chat/embedded-chatbot/context.tsx +++ b/web/app/components/base/chat/embedded-chatbot/context.tsx @@ -56,6 +56,8 @@ export type EmbeddedChatbotContextValue = { name?: string avatar_url?: string } + showChatMemory?: boolean + setShowChatMemory: (state: boolean) => void } export const EmbeddedChatbotContext = createContext({ @@ -86,5 +88,7 @@ export const EmbeddedChatbotContext = createContext setCurrentConversationInputs: noop, allInputsHidden: false, initUserVariables: {}, + showChatMemory: false, + setShowChatMemory: noop, }) export const useEmbeddedChatbotContext = () => useContext(EmbeddedChatbotContext) diff --git a/web/app/components/base/chat/embedded-chatbot/header/index.tsx b/web/app/components/base/chat/embedded-chatbot/header/index.tsx index 95975e29e7..7ad96cce02 100644 --- a/web/app/components/base/chat/embedded-chatbot/header/index.tsx +++ b/web/app/components/base/chat/embedded-chatbot/header/index.tsx @@ -7,8 +7,9 @@ import { CssTransform } from '../theme/utils' import { useEmbeddedChatbotContext, } from '../context' +import { Memory } from '@/app/components/base/icons/src/vender/line/others' import Tooltip from '@/app/components/base/tooltip' -import ActionButton from '@/app/components/base/action-button' +import ActionButton, { ActionButtonState } from '@/app/components/base/action-button' import Divider from '@/app/components/base/divider' import ViewFormDropdown from '@/app/components/base/chat/embedded-chatbot/inputs-form/view-form-dropdown' import DifyLogo from '@/app/components/base/logo/dify-logo' @@ -36,6 +37,8 @@ const Header: FC = ({ appData, currentConversationId, inputsForms, + showChatMemory, + setShowChatMemory, } = useEmbeddedChatbotContext() const isClient = typeof window !== 'undefined' @@ -76,6 +79,10 @@ const Header: FC = ({ }, parentOrigin) }, [isIframe, parentOrigin, showToggleExpandButton, expanded]) + const handleChatMemoryToggle = useCallback(() => { + setShowChatMemory(!showChatMemory) + }, [setShowChatMemory, showChatMemory]) + if (!isMobile) { return (
@@ -127,6 +134,15 @@ const Header: FC = ({ {currentConversationId && inputsForms.length > 0 && ( )} + {currentConversationId && ( + + + + + + )}
) @@ -174,6 +190,15 @@ const Header: FC = ({ {currentConversationId && inputsForms.length > 0 && ( )} + {currentConversationId && ( + + + + + + )} ) diff --git a/web/app/components/base/chat/embedded-chatbot/hooks.tsx b/web/app/components/base/chat/embedded-chatbot/hooks.tsx index 4e86ad50e4..2ac4632234 100644 --- a/web/app/components/base/chat/embedded-chatbot/hooks.tsx +++ b/web/app/components/base/chat/embedded-chatbot/hooks.tsx @@ -380,6 +380,8 @@ export const useEmbeddedChatbot = () => { notify({ type: 'success', message: t('common.api.success') }) }, [isInstalledApp, appId, t, notify]) + const [showChatMemory, setShowChatMemory] = useState(false) + return { appInfoError, appInfoLoading: appInfoLoading || (systemFeatures.webapp_auth.enabled && isCheckingPermission), @@ -422,5 +424,7 @@ export const useEmbeddedChatbot = () => { setCurrentConversationInputs, allInputsHidden, initUserVariables, + showChatMemory, + setShowChatMemory, } } diff --git a/web/app/components/base/chat/embedded-chatbot/index.tsx b/web/app/components/base/chat/embedded-chatbot/index.tsx index 4c8c0a2455..f02eec0d4c 100644 --- a/web/app/components/base/chat/embedded-chatbot/index.tsx +++ b/web/app/components/base/chat/embedded-chatbot/index.tsx @@ -16,6 +16,7 @@ import Loading from '@/app/components/base/loading' import LogoHeader from '@/app/components/base/logo/logo-embedded-chat-header' import Header from '@/app/components/base/chat/embedded-chatbot/header' import ChatWrapper from '@/app/components/base/chat/embedded-chatbot/chat-wrapper' +import MemoryPanel from './memory' import DifyLogo from '@/app/components/base/logo/dify-logo' import cn from '@/utils/classnames' import useDocumentTitle from '@/hooks/use-document-title' @@ -30,6 +31,8 @@ const Chatbot = () => { chatShouldReloadKey, handleNewConversation, themeBuilder, + showChatMemory, + setShowChatMemory, } = useEmbeddedChatbotContext() const { t } = useTranslation() const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) @@ -90,6 +93,15 @@ const Chatbot = () => { )} )} + {showChatMemory && ( +
setShowChatMemory(false)} + > +
e.stopPropagation()}> + +
+
+ )} ) } @@ -132,6 +144,8 @@ const EmbeddedChatbotWrapper = () => { setCurrentConversationInputs, allInputsHidden, initUserVariables, + showChatMemory, + setShowChatMemory, } = useEmbeddedChatbot() return { setCurrentConversationInputs, allInputsHidden, initUserVariables, + showChatMemory, + setShowChatMemory, }}> diff --git a/web/app/components/base/chat/embedded-chatbot/memory/card/edit-modal.tsx b/web/app/components/base/chat/embedded-chatbot/memory/card/edit-modal.tsx new file mode 100644 index 0000000000..b3ba52f0e3 --- /dev/null +++ b/web/app/components/base/chat/embedded-chatbot/memory/card/edit-modal.tsx @@ -0,0 +1,117 @@ +'use client' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { RiCloseLine } from '@remixicon/react' +import { Memory } from '@/app/components/base/icons/src/vender/line/others' +import Modal from '@/app/components/base/modal' +import ActionButton from '@/app/components/base/action-button' +import Badge from '@/app/components/base/badge' +import Button from '@/app/components/base/button' +import Textarea from '@/app/components/base/textarea' +import Divider from '@/app/components/base/divider' +import Toast from '@/app/components/base/toast' +import type { MemoryItem } from '../type' +import { noop } from 'lodash-es' +import cn from '@/utils/classnames' + +type Props = { + memory: MemoryItem + show: boolean + onConfirm: (info: MemoryItem) => Promise + onHide: () => void + isMobile?: boolean +} + +const MemoryEditModal = ({ + memory, + show = false, + onConfirm, + onHide, + isMobile, +}: Props) => { + const { t } = useTranslation() + const [content, setContent] = React.useState(memory.content) + + const submit = () => { + if (!content.trim()) { + Toast.notify({ type: 'error', message: 'content is required' }) + return + } + onConfirm({ ...memory, content }) + onHide() + } + + if (isMobile) { + return ( +
+
e.stopPropagation()}> +
+ + + +
+
+
{t('share.chat.memory.editTitle')}
+
+ +
{memory.name}
+ +
+
+
+