mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-07 13:59:28 +08:00
fix: handle optional list length in DraggableVirtualList and update padding in QuickPanel
This commit is contained in:
parent
f01b7075fb
commit
30b080efbd
@ -226,10 +226,11 @@ const StyledModal = styled(Modal)<{ $isFullscreen?: boolean }>`
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ant-modal-header {
|
.ant-modal-header {
|
||||||
padding: 10px 12px !important;
|
padding: 10px !important;
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid var(--color-border);
|
||||||
background: var(--color-background);
|
background: var(--color-background);
|
||||||
margin-bottom: 0 !important;
|
margin-bottom: 0 !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|||||||
@ -82,7 +82,7 @@ function DraggableVirtualList<T>({
|
|||||||
const parentRef = useRef<HTMLDivElement>(null)
|
const parentRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
const virtualizer = useVirtualizer({
|
const virtualizer = useVirtualizer({
|
||||||
count: list.length,
|
count: list?.length ?? 0,
|
||||||
getScrollElement: useCallback(() => parentRef.current, []),
|
getScrollElement: useCallback(() => parentRef.current, []),
|
||||||
getItemKey: itemKey,
|
getItemKey: itemKey,
|
||||||
estimateSize: useCallback(() => 50, []),
|
estimateSize: useCallback(() => 50, []),
|
||||||
|
|||||||
@ -611,7 +611,7 @@ const QuickPanelContainer = styled.div<{
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 30px 0 30px;
|
padding: 0 35px 0 35px;
|
||||||
transform: translateY(-100%);
|
transform: translateY(-100%);
|
||||||
transform-origin: bottom;
|
transform-origin: bottom;
|
||||||
transition: max-height 0.2s ease;
|
transition: max-height 0.2s ease;
|
||||||
|
|||||||
@ -410,6 +410,7 @@ export const REFERENCE_PROMPT = `Please answer the question based on the referen
|
|||||||
- Please cite the context at the end of sentences when appropriate.
|
- Please cite the context at the end of sentences when appropriate.
|
||||||
- Please use the format of citation number [number] to reference the context in corresponding parts of your answer.
|
- Please use the format of citation number [number] to reference the context in corresponding parts of your answer.
|
||||||
- If a sentence comes from multiple contexts, please list all relevant citation numbers, e.g., [1][2]. Remember not to group citations at the end but list them in the corresponding parts of your answer.
|
- If a sentence comes from multiple contexts, please list all relevant citation numbers, e.g., [1][2]. Remember not to group citations at the end but list them in the corresponding parts of your answer.
|
||||||
|
- If all reference content is not relevant to the user's question, please answer based on your knowledge.
|
||||||
|
|
||||||
## My question is:
|
## My question is:
|
||||||
|
|
||||||
|
|||||||
@ -460,7 +460,8 @@
|
|||||||
"swap": "Swap",
|
"swap": "Swap",
|
||||||
"topics": "Topics",
|
"topics": "Topics",
|
||||||
"warning": "Warning",
|
"warning": "Warning",
|
||||||
"you": "You"
|
"you": "You",
|
||||||
|
"i_know": "I know"
|
||||||
},
|
},
|
||||||
"docs": {
|
"docs": {
|
||||||
"title": "Docs"
|
"title": "Docs"
|
||||||
@ -2308,7 +2309,7 @@
|
|||||||
},
|
},
|
||||||
"provider": "OCR Provider",
|
"provider": "OCR Provider",
|
||||||
"provider_placeholder": "Choose an OCR provider",
|
"provider_placeholder": "Choose an OCR provider",
|
||||||
"title": "OCR"
|
"title": "OCR Settings"
|
||||||
},
|
},
|
||||||
"preprocess": {
|
"preprocess": {
|
||||||
"provider": "Pre Process Provider",
|
"provider": "Pre Process Provider",
|
||||||
@ -2557,7 +2558,8 @@
|
|||||||
"please_select_embedding_model": "Please select an embedding model",
|
"please_select_embedding_model": "Please select an embedding model",
|
||||||
"select_embedding_model_placeholder": "Select Embedding Model",
|
"select_embedding_model_placeholder": "Select Embedding Model",
|
||||||
"embedding_dimensions": "Embedding Dimensions",
|
"embedding_dimensions": "Embedding Dimensions",
|
||||||
"stored_memories": "Stored Memories"
|
"stored_memories": "Stored Memories",
|
||||||
|
"global_memory_description": "To use memory features, please enable global memory in assistant settings."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -460,7 +460,8 @@
|
|||||||
"swap": "交換",
|
"swap": "交換",
|
||||||
"topics": "トピック",
|
"topics": "トピック",
|
||||||
"warning": "警告",
|
"warning": "警告",
|
||||||
"you": "あなた"
|
"you": "あなた",
|
||||||
|
"i_know": "わかりました"
|
||||||
},
|
},
|
||||||
"docs": {
|
"docs": {
|
||||||
"title": "ドキュメント"
|
"title": "ドキュメント"
|
||||||
@ -2455,6 +2456,7 @@
|
|||||||
"visualization": "可視化"
|
"visualization": "可視化"
|
||||||
},
|
},
|
||||||
"memory": {
|
"memory": {
|
||||||
|
"title": "グローバルメモリ",
|
||||||
"add_memory": "メモリーを追加",
|
"add_memory": "メモリーを追加",
|
||||||
"edit_memory": "メモリーを編集",
|
"edit_memory": "メモリーを編集",
|
||||||
"memory_content": "メモリー内容",
|
"memory_content": "メモリー内容",
|
||||||
@ -2476,7 +2478,6 @@
|
|||||||
"user": "ユーザー",
|
"user": "ユーザー",
|
||||||
"content": "内容",
|
"content": "内容",
|
||||||
"score": "スコア",
|
"score": "スコア",
|
||||||
"title": "メモリー",
|
|
||||||
"memories_description": "{{total}}件中{{count}}件のメモリーを表示",
|
"memories_description": "{{total}}件中{{count}}件のメモリーを表示",
|
||||||
"search_placeholder": "メモリーを検索...",
|
"search_placeholder": "メモリーを検索...",
|
||||||
"start_date": "開始日",
|
"start_date": "開始日",
|
||||||
@ -2557,7 +2558,8 @@
|
|||||||
"please_select_embedding_model": "埋め込みモデルを選択してください",
|
"please_select_embedding_model": "埋め込みモデルを選択してください",
|
||||||
"select_embedding_model_placeholder": "埋め込みモデルを選択",
|
"select_embedding_model_placeholder": "埋め込みモデルを選択",
|
||||||
"embedding_dimensions": "埋め込み次元",
|
"embedding_dimensions": "埋め込み次元",
|
||||||
"stored_memories": "保存された記憶"
|
"stored_memories": "保存された記憶",
|
||||||
|
"global_memory_description": "メモリ機能を使用するには、アシスタント設定でグローバルメモリを有効にしてください。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -460,7 +460,8 @@
|
|||||||
"swap": "Поменять местами",
|
"swap": "Поменять местами",
|
||||||
"topics": "Топики",
|
"topics": "Топики",
|
||||||
"warning": "Предупреждение",
|
"warning": "Предупреждение",
|
||||||
"you": "Вы"
|
"you": "Вы",
|
||||||
|
"i_know": "Я понял"
|
||||||
},
|
},
|
||||||
"docs": {
|
"docs": {
|
||||||
"title": "Документация"
|
"title": "Документация"
|
||||||
@ -2455,6 +2456,7 @@
|
|||||||
"visualization": "Визуализация"
|
"visualization": "Визуализация"
|
||||||
},
|
},
|
||||||
"memory": {
|
"memory": {
|
||||||
|
"title": "Глобальная память",
|
||||||
"add_memory": "Добавить память",
|
"add_memory": "Добавить память",
|
||||||
"edit_memory": "Редактировать память",
|
"edit_memory": "Редактировать память",
|
||||||
"memory_content": "Содержимое памяти",
|
"memory_content": "Содержимое памяти",
|
||||||
@ -2531,7 +2533,6 @@
|
|||||||
"total_memories": "всего воспоминаний",
|
"total_memories": "всего воспоминаний",
|
||||||
"default": "По умолчанию",
|
"default": "По умолчанию",
|
||||||
"custom": "Пользовательский",
|
"custom": "Пользовательский",
|
||||||
"title": "Воспоминания",
|
|
||||||
"description": "Память позволяет хранить и управлять информацией о ваших взаимодействиях с ассистентом. Вы можете добавлять, редактировать и удалять воспоминания, а также фильтровать и искать их.",
|
"description": "Память позволяет хранить и управлять информацией о ваших взаимодействиях с ассистентом. Вы можете добавлять, редактировать и удалять воспоминания, а также фильтровать и искать их.",
|
||||||
"global_memory_enabled": "Глобальная память включена",
|
"global_memory_enabled": "Глобальная память включена",
|
||||||
"global_memory": "Глобальная память",
|
"global_memory": "Глобальная память",
|
||||||
@ -2557,7 +2558,8 @@
|
|||||||
"please_select_embedding_model": "Пожалуйста, выберите модель для внедрения",
|
"please_select_embedding_model": "Пожалуйста, выберите модель для внедрения",
|
||||||
"select_embedding_model_placeholder": "Выберите модель внедрения",
|
"select_embedding_model_placeholder": "Выберите модель внедрения",
|
||||||
"embedding_dimensions": "Размерность вложения",
|
"embedding_dimensions": "Размерность вложения",
|
||||||
"stored_memories": "Запасённые воспоминания"
|
"stored_memories": "Запасённые воспоминания",
|
||||||
|
"global_memory_description": "Для использования функций памяти необходимо включить глобальную память в настройках ассистента."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -460,7 +460,8 @@
|
|||||||
"swap": "交换",
|
"swap": "交换",
|
||||||
"topics": "话题",
|
"topics": "话题",
|
||||||
"warning": "警告",
|
"warning": "警告",
|
||||||
"you": "用户"
|
"you": "用户",
|
||||||
|
"i_know": "我知道了"
|
||||||
},
|
},
|
||||||
"docs": {
|
"docs": {
|
||||||
"title": "帮助文档"
|
"title": "帮助文档"
|
||||||
@ -2308,7 +2309,7 @@
|
|||||||
},
|
},
|
||||||
"provider": "OCR 服务商",
|
"provider": "OCR 服务商",
|
||||||
"provider_placeholder": "选择一个 OCR 服务商",
|
"provider_placeholder": "选择一个 OCR 服务商",
|
||||||
"title": "OCR"
|
"title": "OCR 文字识别"
|
||||||
},
|
},
|
||||||
"preprocess": {
|
"preprocess": {
|
||||||
"provider": "文档预处理服务商",
|
"provider": "文档预处理服务商",
|
||||||
@ -2455,6 +2456,7 @@
|
|||||||
"visualization": "可视化"
|
"visualization": "可视化"
|
||||||
},
|
},
|
||||||
"memory": {
|
"memory": {
|
||||||
|
"title": "全局记忆",
|
||||||
"settings": "设置",
|
"settings": "设置",
|
||||||
"statistics": "统计",
|
"statistics": "统计",
|
||||||
"search": "搜索",
|
"search": "搜索",
|
||||||
@ -2464,8 +2466,8 @@
|
|||||||
"memory_content": "记忆内容",
|
"memory_content": "记忆内容",
|
||||||
"please_enter_memory": "请输入记忆内容",
|
"please_enter_memory": "请输入记忆内容",
|
||||||
"memory_placeholder": "输入记忆内容...",
|
"memory_placeholder": "输入记忆内容...",
|
||||||
"user_id": "用户ID",
|
"user_id": "用户 ID",
|
||||||
"user_id_placeholder": "输入用户ID(可选)",
|
"user_id_placeholder": "输入用户 ID(可选)",
|
||||||
"load_failed": "加载记忆失败",
|
"load_failed": "加载记忆失败",
|
||||||
"add_success": "记忆添加成功",
|
"add_success": "记忆添加成功",
|
||||||
"add_failed": "添加记忆失败",
|
"add_failed": "添加记忆失败",
|
||||||
@ -2480,7 +2482,6 @@
|
|||||||
"user": "用户",
|
"user": "用户",
|
||||||
"content": "内容",
|
"content": "内容",
|
||||||
"score": "分数",
|
"score": "分数",
|
||||||
"title": "记忆",
|
|
||||||
"memories_description": "显示 {{count}} / {{total}} 条记忆",
|
"memories_description": "显示 {{count}} / {{total}} 条记忆",
|
||||||
"search_placeholder": "搜索记忆...",
|
"search_placeholder": "搜索记忆...",
|
||||||
"start_date": "开始日期",
|
"start_date": "开始日期",
|
||||||
@ -2557,7 +2558,8 @@
|
|||||||
"please_select_embedding_model": "请选择嵌入模型",
|
"please_select_embedding_model": "请选择嵌入模型",
|
||||||
"select_embedding_model_placeholder": "选择嵌入模型",
|
"select_embedding_model_placeholder": "选择嵌入模型",
|
||||||
"embedding_dimensions": "嵌入维度",
|
"embedding_dimensions": "嵌入维度",
|
||||||
"stored_memories": "已存储记忆"
|
"stored_memories": "已存储记忆",
|
||||||
|
"global_memory_description": "需要开启助手设置中的全局记忆才能使用"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -460,7 +460,8 @@
|
|||||||
"swap": "交換",
|
"swap": "交換",
|
||||||
"topics": "話題",
|
"topics": "話題",
|
||||||
"warning": "警告",
|
"warning": "警告",
|
||||||
"you": "您"
|
"you": "您",
|
||||||
|
"i_know": "我知道了"
|
||||||
},
|
},
|
||||||
"docs": {
|
"docs": {
|
||||||
"title": "說明文件"
|
"title": "說明文件"
|
||||||
@ -2308,7 +2309,7 @@
|
|||||||
},
|
},
|
||||||
"provider": "OCR 供應商",
|
"provider": "OCR 供應商",
|
||||||
"provider_placeholder": "選擇一個OCR服務提供商",
|
"provider_placeholder": "選擇一個OCR服務提供商",
|
||||||
"title": "光學字符識別"
|
"title": "OCR 文字識別"
|
||||||
},
|
},
|
||||||
"preprocess": {
|
"preprocess": {
|
||||||
"provider": "前置處理供應商",
|
"provider": "前置處理供應商",
|
||||||
@ -2455,6 +2456,7 @@
|
|||||||
"visualization": "視覺化"
|
"visualization": "視覺化"
|
||||||
},
|
},
|
||||||
"memory": {
|
"memory": {
|
||||||
|
"title": "全域記憶",
|
||||||
"add_memory": "新增記憶",
|
"add_memory": "新增記憶",
|
||||||
"edit_memory": "編輯記憶",
|
"edit_memory": "編輯記憶",
|
||||||
"memory_content": "記憶內容",
|
"memory_content": "記憶內容",
|
||||||
@ -2476,7 +2478,6 @@
|
|||||||
"user": "使用者",
|
"user": "使用者",
|
||||||
"content": "內容",
|
"content": "內容",
|
||||||
"score": "分數",
|
"score": "分數",
|
||||||
"title": "記憶",
|
|
||||||
"memories_description": "顯示 {{count}} / {{total}} 條記憶",
|
"memories_description": "顯示 {{count}} / {{total}} 條記憶",
|
||||||
"search_placeholder": "搜尋記憶...",
|
"search_placeholder": "搜尋記憶...",
|
||||||
"start_date": "開始日期",
|
"start_date": "開始日期",
|
||||||
@ -2557,7 +2558,8 @@
|
|||||||
"please_select_embedding_model": "請選擇一個嵌入模型",
|
"please_select_embedding_model": "請選擇一個嵌入模型",
|
||||||
"select_embedding_model_placeholder": "選擇嵌入模型",
|
"select_embedding_model_placeholder": "選擇嵌入模型",
|
||||||
"embedding_dimensions": "嵌入維度",
|
"embedding_dimensions": "嵌入維度",
|
||||||
"stored_memories": "儲存的記憶"
|
"stored_memories": "儲存的記憶",
|
||||||
|
"global_memory_description": "需要開啟助手設定中的全域記憶才能使用"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -960,7 +960,7 @@ const InputBarContainer = styled.div`
|
|||||||
border: 0.5px solid var(--color-border);
|
border: 0.5px solid var(--color-border);
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 20px;
|
border-radius: 17px;
|
||||||
padding-top: 8px; // 为拖动手柄留出空间
|
padding-top: 8px; // 为拖动手柄留出空间
|
||||||
background-color: var(--color-background-opacity);
|
background-color: var(--color-background-opacity);
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { useKnowledge } from '@renderer/hooks/useKnowledge'
|
|||||||
import FileItem from '@renderer/pages/files/FileItem'
|
import FileItem from '@renderer/pages/files/FileItem'
|
||||||
import { getProviderName } from '@renderer/services/ProviderService'
|
import { getProviderName } from '@renderer/services/ProviderService'
|
||||||
import { KnowledgeBase, KnowledgeItem } from '@renderer/types'
|
import { KnowledgeBase, KnowledgeItem } from '@renderer/types'
|
||||||
import { Button, Dropdown, message, Tooltip } from 'antd'
|
import { Button, Dropdown, Tooltip } from 'antd'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { Plus } from 'lucide-react'
|
import { Plus } from 'lucide-react'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
@ -72,7 +72,7 @@ const KnowledgeUrls: FC<KnowledgeContentProps> = ({ selectedBase }) => {
|
|||||||
if (!urlItems.find((item) => item.content === url.trim())) {
|
if (!urlItems.find((item) => item.content === url.trim())) {
|
||||||
addUrl(url.trim())
|
addUrl(url.trim())
|
||||||
} else {
|
} else {
|
||||||
message.success(t('knowledge.url_added'))
|
window.message.success(t('knowledge.url_added'))
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Skip invalid URLs silently
|
// Skip invalid URLs silently
|
||||||
@ -143,7 +143,7 @@ const KnowledgeUrls: FC<KnowledgeContentProps> = ({ selectedBase }) => {
|
|||||||
label: t('common.copy'),
|
label: t('common.copy'),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
navigator.clipboard.writeText(item.content as string)
|
navigator.clipboard.writeText(item.content as string)
|
||||||
message.success(t('message.copied'))
|
window.message.success(t('message.copied'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -539,8 +539,6 @@ const MemoriesPage = () => {
|
|||||||
title: t('memory.delete_user_confirm_title'),
|
title: t('memory.delete_user_confirm_title'),
|
||||||
content: t('memory.delete_user_confirm_content', { user: userId }),
|
content: t('memory.delete_user_confirm_content', { user: userId }),
|
||||||
icon: <ExclamationCircleOutlined />,
|
icon: <ExclamationCircleOutlined />,
|
||||||
okText: t('common.yes'),
|
|
||||||
cancelText: t('common.no'),
|
|
||||||
okType: 'danger',
|
okType: 'danger',
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import { InfoCircleOutlined, SettingOutlined } from '@ant-design/icons'
|
import { InfoCircleOutlined } from '@ant-design/icons'
|
||||||
import { Box } from '@renderer/components/Layout'
|
import { Box } from '@renderer/components/Layout'
|
||||||
import MemoryService from '@renderer/services/MemoryService'
|
import MemoryService from '@renderer/services/MemoryService'
|
||||||
import { selectGlobalMemoryEnabled, selectMemoryConfig } from '@renderer/store/memory'
|
import { selectGlobalMemoryEnabled, selectMemoryConfig } from '@renderer/store/memory'
|
||||||
import { Assistant, AssistantSettings } from '@renderer/types'
|
import { Assistant, AssistantSettings } from '@renderer/types'
|
||||||
import { Alert, Button, Card, Space, Switch, Tooltip, Typography } from 'antd'
|
import { Alert, Button, Card, Space, Switch, Tooltip, Typography } from 'antd'
|
||||||
import { useForm } from 'antd/es/form/Form'
|
import { useForm } from 'antd/es/form/Form'
|
||||||
|
import { Settings2 } from 'lucide-react'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
@ -78,9 +79,7 @@ const AssistantMemorySettings: React.FC<Props> = ({ assistant, updateAssistant,
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
<Space>
|
<Space>
|
||||||
<Button size="small" icon={<SettingOutlined />} onClick={handleNavigateToMemory}>
|
<Button type="text" icon={<Settings2 size={15} />} onClick={handleNavigateToMemory} />
|
||||||
{t('common.settings')}
|
|
||||||
</Button>
|
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
!globalMemoryEnabled
|
!globalMemoryEnabled
|
||||||
|
|||||||
@ -6,16 +6,18 @@ import {
|
|||||||
MoreOutlined,
|
MoreOutlined,
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
ReloadOutlined,
|
ReloadOutlined,
|
||||||
SettingOutlined,
|
|
||||||
UserAddOutlined,
|
UserAddOutlined,
|
||||||
UserDeleteOutlined,
|
UserDeleteOutlined,
|
||||||
UserOutlined
|
UserOutlined
|
||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
|
import { HStack } from '@renderer/components/Layout'
|
||||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
|
import { useModel } from '@renderer/hooks/useModel'
|
||||||
import MemoryService from '@renderer/services/MemoryService'
|
import MemoryService from '@renderer/services/MemoryService'
|
||||||
import {
|
import {
|
||||||
selectCurrentUserId,
|
selectCurrentUserId,
|
||||||
selectGlobalMemoryEnabled,
|
selectGlobalMemoryEnabled,
|
||||||
|
selectMemoryConfig,
|
||||||
setCurrentUserId,
|
setCurrentUserId,
|
||||||
setGlobalMemoryEnabled
|
setGlobalMemoryEnabled
|
||||||
} from '@renderer/store/memory'
|
} from '@renderer/store/memory'
|
||||||
@ -37,7 +39,7 @@ import {
|
|||||||
} from 'antd'
|
} from 'antd'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||||
import { Brain } from 'lucide-react'
|
import { Brain, Settings2 } from 'lucide-react'
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
@ -123,10 +125,7 @@ const AddMemoryModal: React.FC<AddMemoryModalProps> = ({ visible, onCancel, onAd
|
|||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<Form form={form} layout="vertical" onFinish={handleSubmit}>
|
<Form form={form} layout="vertical" onFinish={handleSubmit}>
|
||||||
<Form.Item
|
<Form.Item name="memory" rules={[{ required: true, message: t('memory.please_enter_memory') }]}>
|
||||||
label={t('memory.memory_content')}
|
|
||||||
name="memory"
|
|
||||||
rules={[{ required: true, message: t('memory.please_enter_memory') }]}>
|
|
||||||
<TextArea
|
<TextArea
|
||||||
rows={5}
|
rows={5}
|
||||||
placeholder={t('memory.memory_placeholder')}
|
placeholder={t('memory.memory_placeholder')}
|
||||||
@ -502,11 +501,16 @@ const MemorySettings = () => {
|
|||||||
const handleSettingsSubmit = async () => {
|
const handleSettingsSubmit = async () => {
|
||||||
setSettingsModalVisible(false)
|
setSettingsModalVisible(false)
|
||||||
await memoryService.updateConfig()
|
await memoryService.updateConfig()
|
||||||
|
if (window.keyv.get('memory.wait.settings')) {
|
||||||
|
window.keyv.remove('memory.wait.settings')
|
||||||
|
dispatch(setGlobalMemoryEnabled(true))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSettingsCancel = () => {
|
const handleSettingsCancel = () => {
|
||||||
setSettingsModalVisible(false)
|
setSettingsModalVisible(false)
|
||||||
form.resetFields()
|
form.resetFields()
|
||||||
|
window.keyv.remove('memory.wait.settings')
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleResetMemories = async (userId: string) => {
|
const handleResetMemories = async (userId: string) => {
|
||||||
@ -544,8 +548,6 @@ const MemorySettings = () => {
|
|||||||
title: t('memory.delete_user_confirm_title'),
|
title: t('memory.delete_user_confirm_title'),
|
||||||
content: t('memory.delete_user_confirm_content', { user: userId }),
|
content: t('memory.delete_user_confirm_content', { user: userId }),
|
||||||
icon: <ExclamationCircleOutlined />,
|
icon: <ExclamationCircleOutlined />,
|
||||||
okText: t('common.yes'),
|
|
||||||
cancelText: t('common.no'),
|
|
||||||
okType: 'danger',
|
okType: 'danger',
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
try {
|
try {
|
||||||
@ -569,9 +571,32 @@ const MemorySettings = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleGlobalMemoryToggle = (enabled: boolean) => {
|
const memoryConfig = useSelector(selectMemoryConfig)
|
||||||
|
const embedderModel = useModel(memoryConfig.embedderApiClient?.model, memoryConfig.embedderApiClient?.provider)
|
||||||
|
|
||||||
|
const handleGlobalMemoryToggle = async (enabled: boolean) => {
|
||||||
|
if (enabled && !embedderModel) {
|
||||||
|
window.keyv.set('memory.wait.settings', true)
|
||||||
|
return setSettingsModalVisible(true)
|
||||||
|
}
|
||||||
|
|
||||||
dispatch(setGlobalMemoryEnabled(enabled))
|
dispatch(setGlobalMemoryEnabled(enabled))
|
||||||
window.message.success(enabled ? t('memory.global_memory_enabled') : t('memory.global_memory_disabled_title'))
|
|
||||||
|
if (enabled) {
|
||||||
|
return window.modal.confirm({
|
||||||
|
centered: true,
|
||||||
|
title: t('memory.global_memory_enabled'),
|
||||||
|
content: t('memory.global_memory_description'),
|
||||||
|
okText: t('common.i_know'),
|
||||||
|
cancelButtonProps: {
|
||||||
|
style: {
|
||||||
|
display: 'none'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
window.message.success(t('memory.global_memory_disabled_title'))
|
||||||
}
|
}
|
||||||
|
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
@ -579,33 +604,27 @@ const MemorySettings = () => {
|
|||||||
return (
|
return (
|
||||||
<SettingContainer theme={theme}>
|
<SettingContainer theme={theme}>
|
||||||
{/* Memory Settings */}
|
{/* Memory Settings */}
|
||||||
<SettingGroup theme={theme}>
|
<SettingGroup style={{ justifyContent: 'space-between', alignItems: 'center' }} theme={theme}>
|
||||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: '2px' }}>
|
<HStack style={{ justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
<SettingTitle>{t('memory.settings')}</SettingTitle>
|
<HStack style={{ alignItems: 'center', gap: '2px' }}>
|
||||||
<span
|
<SettingRowTitle>{t('memory.global_memory')}</SettingRowTitle>
|
||||||
style={{
|
<span
|
||||||
fontSize: '12px',
|
style={{
|
||||||
color: 'var(--color-primary)',
|
fontSize: '12px',
|
||||||
background: 'var(--color-primary-bg)',
|
color: 'var(--color-primary)',
|
||||||
padding: '2px 6px',
|
background: 'var(--color-primary-bg)',
|
||||||
borderRadius: '4px',
|
padding: '2px 6px',
|
||||||
fontWeight: '500'
|
borderRadius: '4px',
|
||||||
}}>
|
fontWeight: '500'
|
||||||
Beta
|
}}>
|
||||||
</span>
|
Beta
|
||||||
</div>
|
</span>
|
||||||
<SettingDivider />
|
</HStack>
|
||||||
<SettingRow>
|
<HStack style={{ alignItems: 'center', gap: 10 }}>
|
||||||
<SettingRowTitle>{t('memory.global_memory')}</SettingRowTitle>
|
<Switch checked={globalMemoryEnabled} onChange={handleGlobalMemoryToggle} />
|
||||||
<Switch checked={globalMemoryEnabled} onChange={handleGlobalMemoryToggle} />
|
<Button icon={<Settings2 size={16} />} onClick={() => setSettingsModalVisible(true)} />
|
||||||
</SettingRow>
|
</HStack>
|
||||||
<SettingDivider />
|
</HStack>
|
||||||
<SettingRow>
|
|
||||||
<SettingRowTitle>{t('memory.settings')}</SettingRowTitle>
|
|
||||||
<Button icon={<SettingOutlined />} onClick={() => setSettingsModalVisible(true)}>
|
|
||||||
{t('common.settings')}
|
|
||||||
</Button>
|
|
||||||
</SettingRow>
|
|
||||||
</SettingGroup>
|
</SettingGroup>
|
||||||
|
|
||||||
{/* User Management */}
|
{/* User Management */}
|
||||||
@ -636,32 +655,32 @@ const MemorySettings = () => {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'flex-start'
|
justifyContent: 'flex-start'
|
||||||
}}>
|
}}>
|
||||||
<Space align="center">
|
<HStack alignItems="center" gap={10}>
|
||||||
<UserAddOutlined />
|
<UserAddOutlined />
|
||||||
<span>{t('memory.add_new_user')}</span>
|
<span>{t('memory.add_new_user')}</span>
|
||||||
</Space>
|
</HStack>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}>
|
)}>
|
||||||
<Option value={DEFAULT_USER_ID}>
|
<Option value={DEFAULT_USER_ID}>
|
||||||
<Space align="center">
|
<HStack alignItems="center" gap={10}>
|
||||||
<Avatar size={20} style={{ background: 'var(--color-primary)' }}>
|
<Avatar size={20} style={{ background: 'var(--color-primary)' }}>
|
||||||
{getUserAvatar(DEFAULT_USER_ID)}
|
{getUserAvatar(DEFAULT_USER_ID)}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<span>{t('memory.default_user')}</span>
|
<span>{t('memory.default_user')}</span>
|
||||||
</Space>
|
</HStack>
|
||||||
</Option>
|
</Option>
|
||||||
{uniqueUsers
|
{uniqueUsers
|
||||||
.filter((user) => user !== DEFAULT_USER_ID)
|
.filter((user) => user !== DEFAULT_USER_ID)
|
||||||
.map((user) => (
|
.map((user) => (
|
||||||
<Option key={user} value={user}>
|
<Option key={user} value={user}>
|
||||||
<Space align="center">
|
<HStack alignItems="center" gap={10}>
|
||||||
<Avatar size={20} style={{ background: 'var(--color-primary)' }}>
|
<Avatar size={20} style={{ background: 'var(--color-primary)' }}>
|
||||||
{getUserAvatar(user)}
|
{getUserAvatar(user)}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<span>{user}</span>
|
<span>{user}</span>
|
||||||
</Space>
|
</HStack>
|
||||||
</Option>
|
</Option>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
@ -736,7 +755,8 @@ const MemorySettings = () => {
|
|||||||
</Dropdown>
|
</Dropdown>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<SettingDivider />
|
|
||||||
|
<SettingDivider style={{ marginBottom: 15 }} />
|
||||||
|
|
||||||
{/* Memory Content Area */}
|
{/* Memory Content Area */}
|
||||||
<div style={{ minHeight: 400 }}>
|
<div style={{ minHeight: 400 }}>
|
||||||
@ -867,7 +887,7 @@ const MemorySettings = () => {
|
|||||||
const MemoryListContainer = styled.div`
|
const MemoryListContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 15px;
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
`
|
`
|
||||||
@ -876,7 +896,7 @@ const MemoryItem = styled.div`
|
|||||||
padding: 12px;
|
padding: 12px;
|
||||||
background: var(--color-background-soft);
|
background: var(--color-background-soft);
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
border-radius: var(--list-item-border-radius);
|
border-radius: 10px;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|||||||
@ -590,7 +590,7 @@ const TranslatePage: FC = () => {
|
|||||||
<ContentContainer id="content-container" ref={contentContainerRef} $historyDrawerVisible={historyDrawerVisible}>
|
<ContentContainer id="content-container" ref={contentContainerRef} $historyDrawerVisible={historyDrawerVisible}>
|
||||||
<HistoryContainer $historyDrawerVisible={historyDrawerVisible}>
|
<HistoryContainer $historyDrawerVisible={historyDrawerVisible}>
|
||||||
<OperationBar>
|
<OperationBar>
|
||||||
<span style={{ fontSize: 16 }}>{t('translate.history.title')}</span>
|
<span style={{ fontSize: 14 }}>{t('translate.history.title')}</span>
|
||||||
{!isEmpty(translateHistory) && (
|
{!isEmpty(translateHistory) && (
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={t('translate.history.clear')}
|
title={t('translate.history.clear')}
|
||||||
@ -623,13 +623,8 @@ const TranslatePage: FC = () => {
|
|||||||
<Flex justify="space-between" vertical gap={4} style={{ width: '100%' }}>
|
<Flex justify="space-between" vertical gap={4} style={{ width: '100%' }}>
|
||||||
<Flex align="center" justify="space-between" style={{ flex: 1 }}>
|
<Flex align="center" justify="space-between" style={{ flex: 1 }}>
|
||||||
<Flex align="center" gap={6}>
|
<Flex align="center" gap={6}>
|
||||||
<span>
|
<HistoryListItemLanguage>{item._sourceLanguage.label()} →</HistoryListItemLanguage>
|
||||||
{item._sourceLanguage.emoji} {item._sourceLanguage.label()}
|
<HistoryListItemLanguage>{item._targetLanguage.label()}</HistoryListItemLanguage>
|
||||||
</span>
|
|
||||||
→
|
|
||||||
<span>
|
|
||||||
{item._targetLanguage.emoji} {item._targetLanguage.label()}
|
|
||||||
</span>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
<HistoryListItemDate>{dayjs(item.createdAt).format('MM/DD HH:mm')}</HistoryListItemDate>
|
<HistoryListItemDate>{dayjs(item.createdAt).format('MM/DD HH:mm')}</HistoryListItemDate>
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -933,4 +928,9 @@ const HistoryListItemDate = styled.div`
|
|||||||
color: var(--color-text-3);
|
color: var(--color-text-3);
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const HistoryListItemLanguage = styled.div`
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
`
|
||||||
|
|
||||||
export default TranslatePage
|
export default TranslatePage
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user