memory panel in mobile

This commit is contained in:
JzoNg
2025-07-31 16:18:19 +08:00
parent fbcfebbba1
commit 8547d4b1ff
6 changed files with 74 additions and 6 deletions

View File

@@ -28,6 +28,8 @@ const HeaderInMobile = () => {
handleRenameConversation,
conversationRenaming,
inputsForms,
showChatMemory,
setShowChatMemory,
} = useChatWithHistoryContext()
const { t } = useTranslation()
const isPin = pinnedConversationList.some(item => item.id === currentConversationId)
@@ -60,6 +62,9 @@ const HeaderInMobile = () => {
if (showRename)
handleRenameConversation(showRename.id, newName, { onSuccess: handleCancelRename })
}, [showRename, handleRenameConversation, handleCancelRename])
const handleChatMemoryToggle = useCallback(() => {
setShowChatMemory(!showChatMemory)
}, [setShowChatMemory, showChatMemory])
const [showSidebar, setShowSidebar] = useState(false)
const [showChatSettings, setShowChatSettings] = useState(false)
@@ -98,6 +103,7 @@ const HeaderInMobile = () => {
)}
</div>
<MobileOperationDropdown
handleChatMemoryToggle={handleChatMemoryToggle}
handleResetChat={handleNewConversation}
handleViewChatSettings={() => setShowChatSettings(true)}
hideViewChatSettings={inputsForms.length < 1}

View File

@@ -9,12 +9,14 @@ import ActionButton, { ActionButtonState } from '@/app/components/base/action-bu
type Props = {
handleResetChat: () => void
handleViewChatSettings: () => void
handleChatMemoryToggle?: () => void
hideViewChatSettings?: boolean
}
const MobileOperationDropdown = ({
handleResetChat,
handleViewChatSettings,
handleChatMemoryToggle,
hideViewChatSettings = false,
}: Props) => {
const { t } = useTranslation()
@@ -44,6 +46,9 @@ const MobileOperationDropdown = ({
<div className='system-md-regular flex cursor-pointer items-center space-x-1 rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover' onClick={handleResetChat}>
<span className='grow'>{t('share.chat.resetChat')}</span>
</div>
<div className='system-md-regular flex cursor-pointer items-center space-x-1 rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover' onClick={handleChatMemoryToggle}>
<span className='grow'>{t('share.chat.memory.actionButton')}</span>
</div>
{!hideViewChatSettings && (
<div className='system-md-regular flex cursor-pointer items-center space-x-1 rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover' onClick={handleViewChatSettings}>
<span className='grow'>{t('share.chat.viewChatSettings')}</span>

View File

@@ -38,6 +38,7 @@ const ChatWithHistory: FC<ChatWithHistoryProps> = ({
themeBuilder,
sidebarCollapseState,
showChatMemory,
setShowChatMemory,
} = useChatWithHistoryContext()
const isSidebarCollapsed = sidebarCollapseState
const customConfig = appData?.custom_config
@@ -84,7 +85,7 @@ const ChatWithHistory: FC<ChatWithHistoryProps> = ({
<div className={cn(
'flex h-full grow flex-col overflow-hidden border-[0,5px] border-components-panel-border-subtle bg-chatbot-bg',
isMobile ? 'rounded-t-2xl' : 'rounded-2xl',
showChatMemory && 'mr-1',
showChatMemory && !isMobile && 'mr-1',
)}>
{!isMobile && <Header />}
{appChatListDataLoading && (
@@ -94,7 +95,18 @@ const ChatWithHistory: FC<ChatWithHistoryProps> = ({
<ChatWrapper key={chatShouldReloadKey} />
)}
</div>
<MemoryPanel showChatMemory={showChatMemory} />
{!isMobile && (
<MemoryPanel showChatMemory={showChatMemory} />
)}
{isMobile && showChatMemory && (
<div className='fixed inset-0 z-50 flex flex-row-reverse bg-background-overlay p-1 backdrop-blur-sm'
onClick={() => setShowChatMemory(false)}
>
<div className='flex h-full w-[360px] rounded-xl shadow-lg' onClick={e => e.stopPropagation()}>
<MemoryPanel showChatMemory={showChatMemory} />
</div>
</div>
)}
</div>
</div>
)

View File

@@ -19,6 +19,7 @@ type Props = {
show: boolean
onConfirm: (info: MemoryItem) => Promise<void>
onHide: () => void
isMobile?: boolean
}
const MemoryEditModal = ({
@@ -26,6 +27,7 @@ const MemoryEditModal = ({
show = false,
onConfirm,
onHide,
isMobile,
}: Props) => {
const { t } = useTranslation()
const [content, setContent] = React.useState(memory.content)
@@ -39,6 +41,43 @@ const MemoryEditModal = ({
onHide()
}
if (isMobile) {
return (
<div className='fixed inset-0 z-50 flex flex-col bg-background-overlay pt-3 backdrop-blur-sm'
onClick={onHide}
>
<div className='relative flex w-full grow flex-col rounded-t-xl bg-components-panel-bg shadow-xl' onClick={e => e.stopPropagation()}>
<div className='absolute right-4 top-4 cursor-pointer p-2'>
<ActionButton onClick={onHide}>
<RiCloseLine className='h-5 w-5' />
</ActionButton>
</div>
<div className='p-4 pb-3'>
<div className='title-2xl-semi-bold mb-2 text-text-primary'>{t('share.chat.memory.editTitle')}</div>
<div className='flex items-center gap-1 pb-1 pt-2'>
<Memory className='h-4 w-4 shrink-0 text-util-colors-teal-teal-700' />
<div className='system-sm-semibold truncate text-text-primary'>{memory.name}</div>
<Badge text={`${t('share.chat.memory.updateVersion.update')} 2`} />
</div>
</div>
<div className='grow px-4'>
<Textarea
className='h-full'
value={content}
onChange={e => setContent(e.target.value)}
/>
</div>
<div className='flex flex-row-reverse items-center p-4'>
<Button className='ml-2' variant='primary' onClick={submit}>{t('share.chat.memory.operations.save')}</Button>
<Button className='ml-3' onClick={onHide}>{t('share.chat.memory.operations.cancel')}</Button>
<Divider type='vertical' className='!mx-0 !h-4' />
<Button className='mr-3' onClick={onHide}>{t('share.chat.memory.operations.reset')}</Button>
</div>
</div>
</div>
)
}
return (
<Modal
isShow={show}
@@ -65,7 +104,7 @@ const MemoryEditModal = ({
onChange={e => setContent(e.target.value)}
/>
</div>
<div className='flex flex-row-reverse p-6 pt-5'>
<div className='flex flex-row-reverse items-center p-6 pt-5'>
<Button className='ml-2' variant='primary' onClick={submit}>{t('share.chat.memory.operations.save')}</Button>
<Button className='ml-3' onClick={onHide}>{t('share.chat.memory.operations.cancel')}</Button>
<Divider type='vertical' className='!mx-0 !h-4' />

View File

@@ -15,9 +15,10 @@ import cn from '@/utils/classnames'
type Props = {
memory: MemoryItem
isMobile?: boolean
}
const MemoryCard: React.FC<Props> = ({ memory }) => {
const MemoryCard: React.FC<Props> = ({ memory, isMobile }) => {
const { t } = useTranslation()
const [isHovering, setIsHovering] = React.useState(false)
const [showEditModal, setShowEditModal] = React.useState(false)
@@ -39,7 +40,10 @@ const MemoryCard: React.FC<Props> = ({ memory }) => {
<div className='hover:bg-components-actionbar-bg-hover flex items-center gap-0.5 rounded-lg border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-md'>
<ActionButton><RiArrowUpSLine className='h-4 w-4' /></ActionButton>
<ActionButton><RiArrowDownSLine className='h-4 w-4' /></ActionButton>
<Operation onEdit={() => setShowEditModal(true)} />
<Operation onEdit={() => {
setShowEditModal(true)
setIsHovering(false)
}} />
</div>
)}
</div>
@@ -59,6 +63,7 @@ const MemoryCard: React.FC<Props> = ({ memory }) => {
</div>
{showEditModal && (
<MemoryEditModal
isMobile={isMobile}
show={showEditModal}
memory={memory}
onConfirm={async (info) => {

View File

@@ -21,6 +21,7 @@ type Props = {
const MemoryPanel: React.FC<Props> = ({ showChatMemory }) => {
const { t } = useTranslation()
const {
isMobile,
setShowChatMemory,
} = useChatWithHistoryContext()
@@ -37,7 +38,7 @@ const MemoryPanel: React.FC<Props> = ({ showChatMemory }) => {
</div>
<div className='h-0 grow overflow-y-auto px-3 pt-2'>
{mockMemoryList.map(memory => (
<MemoryCard key={memory.name} memory={memory} />
<MemoryCard key={memory.name} memory={memory} isMobile={isMobile} />
))}
<div className='flex items-center justify-center'>
<Button variant='ghost'>