diff --git a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-toolbar/configure-ai-chat-toolbar.ts b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-toolbar/configure-ai-chat-toolbar.ts new file mode 100644 index 0000000000..5194de06aa --- /dev/null +++ b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-toolbar/configure-ai-chat-toolbar.ts @@ -0,0 +1,44 @@ +import type { CopilotChatHistoryFragment } from '@affine/graphql'; +import type { NotificationService } from '@blocksuite/affine/shared/services'; + +import type { DocDisplayConfig } from '../ai-chat-chips'; +import type { ChatStatus } from '../ai-chat-messages'; +import { AIChatToolbar } from './ai-chat-toolbar'; + +export type ConfigureAIChatToolbarOptions = { + session: CopilotChatHistoryFragment | null | undefined; + workspaceId: string; + docId?: string; + status: ChatStatus; + docDisplayConfig: DocDisplayConfig; + notificationService: NotificationService; + onNewSession: () => void; + onTogglePin: () => Promise; + onOpenSession: (sessionId: string) => void; + onOpenDoc: (docId: string, sessionId: string) => void; + onSessionDelete: (session: BlockSuitePresets.AIRecentSession) => void; +}; + +export function getOrCreateAIChatToolbar( + current: AIChatToolbar | null | undefined +): AIChatToolbar { + return current ?? new AIChatToolbar(); +} + +export function configureAIChatToolbar( + tool: AIChatToolbar, + options: ConfigureAIChatToolbarOptions +): AIChatToolbar { + tool.session = options.session; + tool.workspaceId = options.workspaceId; + tool.docId = options.docId; + tool.status = options.status; + tool.docDisplayConfig = options.docDisplayConfig; + tool.notificationService = options.notificationService; + tool.onNewSession = options.onNewSession; + tool.onTogglePin = options.onTogglePin; + tool.onOpenSession = options.onOpenSession; + tool.onOpenDoc = options.onOpenDoc; + tool.onSessionDelete = options.onSessionDelete; + return tool; +} diff --git a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-toolbar/index.ts b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-toolbar/index.ts index 8576112c2f..3d6378ec36 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-toolbar/index.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-toolbar/index.ts @@ -1,2 +1,3 @@ export * from './ai-chat-toolbar'; export * from './ai-session-history'; +export * from './configure-ai-chat-toolbar'; diff --git a/packages/frontend/core/src/components/root-app-sidebar/index.tsx b/packages/frontend/core/src/components/root-app-sidebar/index.tsx index cd2c391410..2e0bddddce 100644 --- a/packages/frontend/core/src/components/root-app-sidebar/index.tsx +++ b/packages/frontend/core/src/components/root-app-sidebar/index.tsx @@ -89,6 +89,7 @@ const AllDocsButton = () => { }; const AIChatButton = () => { + const t = useI18n(); const featureFlagService = useService(FeatureFlagService); const serverService = useService(ServerService); const serverFeatures = useLiveData(serverService.server.features$); @@ -108,7 +109,9 @@ const AIChatButton = () => { return ( } active={aiChatActive} to={'/chat'}> - Intelligence + + {t['com.affine.workspaceSubPath.chat']()} + ); }; diff --git a/packages/frontend/core/src/desktop/pages/workspace/chat-panel-utils.ts b/packages/frontend/core/src/desktop/pages/workspace/chat-panel-utils.ts new file mode 100644 index 0000000000..7d79ec385a --- /dev/null +++ b/packages/frontend/core/src/desktop/pages/workspace/chat-panel-utils.ts @@ -0,0 +1,61 @@ +import type { I18nInstance } from '@affine/i18n'; +import type { NotificationService } from '@blocksuite/affine/shared/services'; + +export type SessionDeleteCleanupFn = ( + session: BlockSuitePresets.AIRecentSession +) => Promise; + +export type CreateSessionDeleteHandlerOptions = { + t: I18nInstance; + notificationService: NotificationService; + cleanupSession: SessionDeleteCleanupFn; + canDeleteSession?: (session: BlockSuitePresets.AIRecentSession) => boolean; + isActiveSession?: (session: BlockSuitePresets.AIRecentSession) => boolean; + onActiveSessionDeleted?: () => void; +}; + +export function createSessionDeleteHandler({ + t, + notificationService, + cleanupSession, + canDeleteSession, + isActiveSession, + onActiveSessionDeleted, +}: CreateSessionDeleteHandlerOptions) { + return async (sessionToDelete: BlockSuitePresets.AIRecentSession) => { + if (canDeleteSession && !canDeleteSession(sessionToDelete)) { + notificationService.toast( + t['com.affine.ai.chat-panel.session.delete.toast.failed']() + ); + return; + } + + const confirm = await notificationService.confirm({ + title: t['com.affine.ai.chat-panel.session.delete.confirm.title'](), + message: t['com.affine.ai.chat-panel.session.delete.confirm.message'](), + confirmText: t['Delete'](), + cancelText: t['Cancel'](), + }); + + if (!confirm) { + return; + } + + try { + await cleanupSession(sessionToDelete); + notificationService.toast( + t['com.affine.ai.chat-panel.session.delete.toast.success']() + ); + } catch (error) { + console.error(error); + notificationService.toast( + t['com.affine.ai.chat-panel.session.delete.toast.failed']() + ); + return; + } + + if (isActiveSession?.(sessionToDelete)) { + onActiveSessionDeleted?.(); + } + }; +} diff --git a/packages/frontend/core/src/desktop/pages/workspace/chat/index.tsx b/packages/frontend/core/src/desktop/pages/workspace/chat/index.tsx index 019bbbc640..f687fab2b8 100644 --- a/packages/frontend/core/src/desktop/pages/workspace/chat/index.tsx +++ b/packages/frontend/core/src/desktop/pages/workspace/chat/index.tsx @@ -5,7 +5,11 @@ import { type ChatContextValue, } from '@affine/core/blocksuite/ai/components/ai-chat-content'; import type { ChatStatus } from '@affine/core/blocksuite/ai/components/ai-chat-messages'; -import { AIChatToolbar } from '@affine/core/blocksuite/ai/components/ai-chat-toolbar'; +import { + AIChatToolbar, + configureAIChatToolbar, + getOrCreateAIChatToolbar, +} from '@affine/core/blocksuite/ai/components/ai-chat-toolbar'; import type { PromptKey } from '@affine/core/blocksuite/ai/provider/prompt'; import { getViewManager } from '@affine/core/blocksuite/manager/view'; import { NotificationServiceImpl } from '@affine/core/blocksuite/view-extensions/editor-view/notification-service'; @@ -37,6 +41,7 @@ import { WorkbenchService, } from '@affine/core/modules/workbench'; import { WorkspaceService } from '@affine/core/modules/workspace'; +import { useI18n } from '@affine/i18n'; import { RefNodeSlotsProvider } from '@blocksuite/affine/inlines/reference'; import { BlockStdScope } from '@blocksuite/affine/std'; import type { Workspace } from '@blocksuite/affine/store'; @@ -45,6 +50,7 @@ import { useFramework, useService } from '@toeverything/infra'; import { nanoid } from 'nanoid'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { createSessionDeleteHandler } from '../chat-panel-utils'; import * as styles from './index.css'; type CopilotSession = Awaited>; @@ -88,6 +94,7 @@ function useMockStd() { } export const Component = () => { + const t = useI18n(); const framework = useFramework(); const [isBodyProvided, setIsBodyProvided] = useState(false); const [isHeaderProvided, setIsHeaderProvided] = useState(false); @@ -193,10 +200,46 @@ export const Component = () => { ); const confirmModal = useConfirmModal(); + const notificationService = useMemo( + () => + new NotificationServiceImpl( + confirmModal.closeConfirmModal, + confirmModal.openConfirmModal + ), + [confirmModal.closeConfirmModal, confirmModal.openConfirmModal] + ); const specs = useAISpecs(); const mockStd = useMockStd(); const handleAISubscribe = useAISubscribe(); + const deleteSession = useMemo( + () => + createSessionDeleteHandler({ + t, + notificationService, + cleanupSession: async sessionToDelete => { + await client.cleanupSessions({ + workspaceId: sessionToDelete.workspaceId, + docId: sessionToDelete.docId || undefined, + sessionIds: [sessionToDelete.sessionId], + }); + }, + isActiveSession: sessionToDelete => + sessionToDelete.sessionId === currentSession?.sessionId, + onActiveSessionDeleted: () => { + setCurrentSession(null); + reMountChatContent(); + }, + }), + [ + client, + currentSession?.sessionId, + notificationService, + reMountChatContent, + t, + ] + ); + // init or update ai-chat-content useEffect(() => { if (!isBodyProvided) { @@ -223,10 +266,7 @@ export const Component = () => { ); content.peekViewService = framework.get(PeekViewService); content.affineThemeService = framework.get(AppThemeService); - content.notificationService = new NotificationServiceImpl( - confirmModal.closeConfirmModal, - confirmModal.openConfirmModal - ); + content.notificationService = notificationService; content.aiDraftService = framework.get(AIDraftService); content.aiToolsConfigService = framework.get(AIToolsConfigService); content.serverService = framework.get(ServerService); @@ -258,6 +298,7 @@ export const Component = () => { workspaceId, confirmModal, onContextChange, + notificationService, specs, onOpenDoc, handleAISubscribe, @@ -268,39 +309,31 @@ export const Component = () => { if (!isHeaderProvided || !chatToolContainerRef.current) { return; } - let tool = chatTool; - - if (!tool) { - tool = new AIChatToolbar(); - } - - tool.session = currentSession; - tool.workspaceId = workspaceId; - tool.status = status; - tool.docDisplayConfig = docDisplayConfig; - tool.onOpenSession = onOpenSession; - tool.notificationService = new NotificationServiceImpl( - confirmModal.closeConfirmModal, - confirmModal.openConfirmModal - ); - - tool.onNewSession = () => { - if (!currentSession) return; - setCurrentSession(null); - reMountChatContent(); - }; - - tool.onTogglePin = async () => { - await togglePin(); - }; - - tool.onOpenDoc = (docId: string, sessionId: string) => { - const { workbench } = framework.get(WorkbenchService); - const viewService = framework.get(ViewService); - workbench.open(`/${docId}?sessionId=${sessionId}`, { at: 'active' }); - workbench.openSidebar(); - viewService.view.activeSidebarTab('chat'); - }; + const tool = getOrCreateAIChatToolbar(chatTool); + configureAIChatToolbar(tool, { + session: currentSession, + workspaceId, + status, + docDisplayConfig, + notificationService, + onOpenSession, + onNewSession: () => { + if (!currentSession) return; + setCurrentSession(null); + reMountChatContent(); + }, + onTogglePin: togglePin, + onOpenDoc: (docId: string, sessionId: string) => { + const { workbench } = framework.get(WorkbenchService); + const viewService = framework.get(ViewService); + workbench.open(`/${docId}?sessionId=${sessionId}`, { at: 'active' }); + workbench.openSidebar(); + viewService.view.activeSidebarTab('chat'); + }, + onSessionDelete: (sessionToDelete: BlockSuitePresets.AIRecentSession) => { + deleteSession(sessionToDelete).catch(console.error); + }, + }); // initial props if (!chatTool) { @@ -318,8 +351,10 @@ export const Component = () => { workspaceId, confirmModal, framework, + deleteSession, status, reMountChatContent, + notificationService, ]); useEffect(() => { @@ -390,7 +425,7 @@ export const Component = () => { return ( <> - +
diff --git a/packages/frontend/core/src/desktop/pages/workspace/detail-page/tabs/chat.tsx b/packages/frontend/core/src/desktop/pages/workspace/detail-page/tabs/chat.tsx index 5e21223358..6fe34d7eb0 100644 --- a/packages/frontend/core/src/desktop/pages/workspace/detail-page/tabs/chat.tsx +++ b/packages/frontend/core/src/desktop/pages/workspace/detail-page/tabs/chat.tsx @@ -6,7 +6,11 @@ import { type ChatContextValue, } from '@affine/core/blocksuite/ai/components/ai-chat-content'; import type { ChatStatus } from '@affine/core/blocksuite/ai/components/ai-chat-messages'; -import { AIChatToolbar } from '@affine/core/blocksuite/ai/components/ai-chat-toolbar'; +import { + AIChatToolbar, + configureAIChatToolbar, + getOrCreateAIChatToolbar, +} from '@affine/core/blocksuite/ai/components/ai-chat-toolbar'; import { createPlaygroundModal } from '@affine/core/blocksuite/ai/components/playground/modal'; import { registerAIAppEffects } from '@affine/core/blocksuite/ai/effects/app'; import type { AffineEditorContainer } from '@affine/core/blocksuite/block-suite-editor'; @@ -31,6 +35,7 @@ import type { CopilotChatHistoryFragment, UpdateChatSessionInput, } from '@affine/graphql'; +import { useI18n } from '@affine/i18n'; import { RefNodeSlotsProvider } from '@blocksuite/affine/inlines/reference'; import { DocModeProvider } from '@blocksuite/affine/shared/services'; import { createSignalFromObservable } from '@blocksuite/affine/shared/utils'; @@ -40,6 +45,7 @@ import { useFramework, useService } from '@toeverything/infra'; import { html } from 'lit'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { createSessionDeleteHandler } from '../../chat-panel-utils'; import * as styles from './chat.css'; import { resolveInitialSession, @@ -56,6 +62,7 @@ export interface SidebarTabProps { export const EditorChatPanel = ({ editor, onLoad }: SidebarTabProps) => { const framework = useFramework(); const workbench = useService(WorkbenchService).workbench; + const t = useI18n(); const { closeConfirmModal, openConfirmModal } = useConfirmModal(); const notificationService = useMemo( @@ -230,30 +237,24 @@ export const EditorChatPanel = ({ editor, onLoad }: SidebarTabProps) => { [doc, openSession, session?.pinned, session?.sessionId, workbench] ); - const deleteSession = useCallback( - async (sessionToDelete: BlockSuitePresets.AIRecentSession) => { - if (!AIProvider.histories) { - return; - } - const confirm = await notificationService.confirm({ - title: 'Delete this history?', - message: - 'Do you want to delete this AI conversation history? Once deleted, it cannot be recovered.', - confirmText: 'Delete', - cancelText: 'Cancel', - }); - if (confirm) { - await AIProvider.histories.cleanup( - sessionToDelete.workspaceId, - sessionToDelete.docId || undefined, - [sessionToDelete.sessionId] - ); - if (sessionToDelete.sessionId === session?.sessionId) { - newSession(); - } - } - }, - [newSession, notificationService, session?.sessionId] + const deleteSession = useMemo( + () => + createSessionDeleteHandler({ + t, + notificationService, + canDeleteSession: () => Boolean(AIProvider.histories), + cleanupSession: async sessionToDelete => { + await AIProvider.histories?.cleanup( + sessionToDelete.workspaceId, + sessionToDelete.docId || undefined, + [sessionToDelete.sessionId] + ); + }, + isActiveSession: sessionToDelete => + sessionToDelete.sessionId === session?.sessionId, + onActiveSessionDeleted: newSession, + }), + [newSession, notificationService, session?.sessionId, t] ); const togglePin = useCallback(async () => { @@ -460,31 +461,26 @@ export const EditorChatPanel = ({ editor, onLoad }: SidebarTabProps) => { return; } - let tool = chatToolbar; - - if (!tool) { - tool = new AIChatToolbar(); - } - - tool.session = session; - tool.workspaceId = doc.workspace.id; - tool.docId = doc.id; - tool.status = status; - tool.docDisplayConfig = docDisplayConfig; - tool.notificationService = notificationService; - tool.onNewSession = newSession; - tool.onTogglePin = togglePin; - tool.onOpenSession = (sessionId: string) => { - openSession(sessionId).catch(console.error); - }; - tool.onOpenDoc = (docId: string, sessionId: string) => { - openDoc(docId, sessionId).catch(console.error); - }; - tool.onSessionDelete = ( - sessionToDelete: BlockSuitePresets.AIRecentSession - ) => { - deleteSession(sessionToDelete).catch(console.error); - }; + const tool = getOrCreateAIChatToolbar(chatToolbar); + configureAIChatToolbar(tool, { + session, + workspaceId: doc.workspace.id, + docId: doc.id, + status, + docDisplayConfig, + notificationService, + onNewSession: newSession, + onTogglePin: togglePin, + onOpenSession: (sessionId: string) => { + openSession(sessionId).catch(console.error); + }, + onOpenDoc: (docId: string, sessionId: string) => { + openDoc(docId, sessionId).catch(console.error); + }, + onSessionDelete: (sessionToDelete: BlockSuitePresets.AIRecentSession) => { + deleteSession(sessionToDelete).catch(console.error); + }, + }); if (!chatToolbar) { chatToolbarContainerRef.current.append(tool); @@ -618,7 +614,7 @@ export const EditorChatPanel = ({ editor, onLoad }: SidebarTabProps) => {
- AFFiNE AI is loading history... + {t['com.affine.ai.chat-panel.loading-history']()}
@@ -628,10 +624,13 @@ export const EditorChatPanel = ({ editor, onLoad }: SidebarTabProps) => {
{isEmbedding ? ( - Embedding {done}/{total} + {t.t('com.affine.ai.chat-panel.embedding-progress', { + done, + total, + })} ) : ( - 'AFFiNE AI' + t['com.affine.ai.chat-panel.title']() )}
{playgroundVisible ? ( diff --git a/packages/frontend/i18n/src/i18n-completenesses.json b/packages/frontend/i18n/src/i18n-completenesses.json index aa835158a1..b4cd1013b7 100644 --- a/packages/frontend/i18n/src/i18n-completenesses.json +++ b/packages/frontend/i18n/src/i18n-completenesses.json @@ -1,26 +1,26 @@ { - "ar": 99, + "ar": 98, "ca": 100, "da": 4, - "de": 100, - "el-GR": 99, + "de": 99, + "el-GR": 98, "en": 100, - "es-AR": 99, + "es-AR": 98, "es-CL": 100, - "es": 99, - "fa": 99, + "es": 98, + "fa": 98, "fr": 100, "hi": 2, "it-IT": 100, "it": 1, - "ja": 99, - "ko": 100, - "nb-NO": 49, + "ja": 98, + "ko": 99, + "nb-NO": 48, "pl": 100, - "pt-BR": 99, + "pt-BR": 98, "ru": 100, "sv-SE": 99, - "uk": 99, + "uk": 98, "ur": 2, "zh-Hans": 100, "zh-Hant": 99 diff --git a/packages/frontend/i18n/src/i18n.gen.ts b/packages/frontend/i18n/src/i18n.gen.ts index 1f61b8083b..f9fd1f65bf 100644 --- a/packages/frontend/i18n/src/i18n.gen.ts +++ b/packages/frontend/i18n/src/i18n.gen.ts @@ -867,6 +867,37 @@ export function useAFFiNEI18N(): { * `Failed to insert template, please try again.` */ ["com.affine.ai.template-insert.failed"](): string; + /** + * `AFFiNE AI` + */ + ["com.affine.ai.chat-panel.title"](): string; + /** + * `AFFiNE AI is loading history...` + */ + ["com.affine.ai.chat-panel.loading-history"](): string; + /** + * `Embedding {{done}}/{{total}}` + */ + ["com.affine.ai.chat-panel.embedding-progress"](options: Readonly<{ + done: string; + total: string; + }>): string; + /** + * `Delete this history?` + */ + ["com.affine.ai.chat-panel.session.delete.confirm.title"](): string; + /** + * `Do you want to delete this AI conversation history? Once deleted, it cannot be recovered.` + */ + ["com.affine.ai.chat-panel.session.delete.confirm.message"](): string; + /** + * `History deleted` + */ + ["com.affine.ai.chat-panel.session.delete.toast.success"](): string; + /** + * `Failed to delete history` + */ + ["com.affine.ai.chat-panel.session.delete.toast.failed"](): string; /** * `All docs` */ @@ -7246,6 +7277,10 @@ export function useAFFiNEI18N(): { * `All docs` */ ["com.affine.workspaceSubPath.all"](): string; + /** + * `Intelligence` + */ + ["com.affine.workspaceSubPath.chat"](): string; /** * `Trash` */ diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index dba91a5dfa..4b4e8ed97c 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -206,6 +206,13 @@ "com.affine.ai.login-required.dialog-content": "To use AFFiNE AI, please sign in to your AFFiNE Cloud account.", "com.affine.ai.login-required.dialog-title": "Sign in to continue", "com.affine.ai.template-insert.failed": "Failed to insert template, please try again.", + "com.affine.ai.chat-panel.title": "AFFiNE AI", + "com.affine.ai.chat-panel.loading-history": "AFFiNE AI is loading history...", + "com.affine.ai.chat-panel.embedding-progress": "Embedding {{done}}/{{total}}", + "com.affine.ai.chat-panel.session.delete.confirm.title": "Delete this history?", + "com.affine.ai.chat-panel.session.delete.confirm.message": "Do you want to delete this AI conversation history? Once deleted, it cannot be recovered.", + "com.affine.ai.chat-panel.session.delete.toast.success": "History deleted", + "com.affine.ai.chat-panel.session.delete.toast.failed": "Failed to delete history", "com.affine.all-pages.header": "All docs", "com.affine.app-sidebar.learn-more": "Learn more", "com.affine.app-sidebar.star-us": "Star us", @@ -1813,6 +1820,7 @@ "com.affine.workspaceList.workspaceListType.local": "Local storage", "com.affine.workspaceList.addServer": "Add Server", "com.affine.workspaceSubPath.all": "All docs", + "com.affine.workspaceSubPath.chat": "Intelligence", "com.affine.workspaceSubPath.trash": "Trash", "com.affine.workspaceSubPath.trash.empty-description": "Deleted docs will appear here.", "com.affine.write_with_a_blank_page": "Write with a blank page", diff --git a/packages/frontend/i18n/src/resources/zh-Hans.json b/packages/frontend/i18n/src/resources/zh-Hans.json index 329cc643a4..681f143f7c 100644 --- a/packages/frontend/i18n/src/resources/zh-Hans.json +++ b/packages/frontend/i18n/src/resources/zh-Hans.json @@ -206,6 +206,13 @@ "com.affine.ai.login-required.dialog-content": "要使用 AFFiNE AI,请先登录您的 AFFiNE Cloud 帐户。", "com.affine.ai.login-required.dialog-title": "登录以继续", "com.affine.ai.template-insert.failed": "插入模板失败,请重试。", + "com.affine.ai.chat-panel.title": "AFFiNE AI", + "com.affine.ai.chat-panel.loading-history": "AFFiNE AI 正在加载历史记录...", + "com.affine.ai.chat-panel.embedding-progress": "嵌入 {{done}}/{{total}}", + "com.affine.ai.chat-panel.session.delete.confirm.title": "删除此历史记录?", + "com.affine.ai.chat-panel.session.delete.confirm.message": "确定要删除这段 AI 对话历史记录吗?删除后无法恢复。", + "com.affine.ai.chat-panel.session.delete.toast.success": "已删除历史记录", + "com.affine.ai.chat-panel.session.delete.toast.failed": "删除历史记录失败", "com.affine.all-pages.header": "所有文档", "com.affine.app-sidebar.learn-more": "了解更多", "com.affine.app-sidebar.star-us": "给我们点亮星标", @@ -1808,6 +1815,7 @@ "com.affine.workspaceList.workspaceListType.local": "本地储存", "com.affine.workspaceList.addServer": "添加服务器", "com.affine.workspaceSubPath.all": "全部文档", + "com.affine.workspaceSubPath.chat": "Intelligence", "com.affine.workspaceSubPath.trash": "回收站", "com.affine.workspaceSubPath.trash.empty-description": "已删除的文档将显示在此处。", "com.affine.write_with_a_blank_page": "在空白页面书写", diff --git a/packages/frontend/i18n/src/resources/zh-Hant.json b/packages/frontend/i18n/src/resources/zh-Hant.json index 2cc3809268..d735db7697 100644 --- a/packages/frontend/i18n/src/resources/zh-Hant.json +++ b/packages/frontend/i18n/src/resources/zh-Hant.json @@ -205,6 +205,13 @@ "com.affine.ai.login-required.dialog-content": "要使用 AFFiNE AI,請先登入您的 AFFiNE Cloud 帳戶。", "com.affine.ai.login-required.dialog-title": "登入以繼續", "com.affine.ai.template-insert.failed": "插入模板失敗,請重試。", + "com.affine.ai.chat-panel.title": "AFFiNE AI", + "com.affine.ai.chat-panel.loading-history": "AFFiNE AI 正在載入歷史記錄...", + "com.affine.ai.chat-panel.embedding-progress": "嵌入 {{done}}/{{total}}", + "com.affine.ai.chat-panel.session.delete.confirm.title": "刪除此歷史記錄?", + "com.affine.ai.chat-panel.session.delete.confirm.message": "確定要刪除這段 AI 對話歷史記錄嗎?刪除後無法復原。", + "com.affine.ai.chat-panel.session.delete.toast.success": "已刪除歷史記錄", + "com.affine.ai.chat-panel.session.delete.toast.failed": "刪除歷史記錄失敗", "com.affine.all-pages.header": "所有文件", "com.affine.app-sidebar.learn-more": "了解更多", "com.affine.app-sidebar.star-us": "給我們點亮星標", @@ -1785,6 +1792,7 @@ "com.affine.workspaceList.workspaceListType.local": "本地儲存", "com.affine.workspaceList.addServer": "新增伺服器", "com.affine.workspaceSubPath.all": "所有頁面", + "com.affine.workspaceSubPath.chat": "Intelligence", "com.affine.workspaceSubPath.trash": "廢紙簍", "com.affine.workspaceSubPath.trash.empty-description": "已刪除的文件將顯示在此處。", "com.affine.write_with_a_blank_page": "在空白頁書寫",