fix: chat session cannot delete (#14312)

fix #14309



#### PR Dependency Tree


* **PR #14312** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added AI chat session deletion with confirmation dialogs and
success/failure notifications.
* Localized AI chat panel labels, loading messages, and session
management text across multiple languages.

* **Documentation**
* Added internationalization support for chat panel titles, history
loading states, and deletion confirmations.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2026-01-26 19:47:47 +08:00
committed by GitHub
parent 5498133627
commit 27ed15a83e
11 changed files with 306 additions and 104 deletions
@@ -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<void>;
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;
}
@@ -1,2 +1,3 @@
export * from './ai-chat-toolbar'; export * from './ai-chat-toolbar';
export * from './ai-session-history'; export * from './ai-session-history';
export * from './configure-ai-chat-toolbar';
@@ -89,6 +89,7 @@ const AllDocsButton = () => {
}; };
const AIChatButton = () => { const AIChatButton = () => {
const t = useI18n();
const featureFlagService = useService(FeatureFlagService); const featureFlagService = useService(FeatureFlagService);
const serverService = useService(ServerService); const serverService = useService(ServerService);
const serverFeatures = useLiveData(serverService.server.features$); const serverFeatures = useLiveData(serverService.server.features$);
@@ -108,7 +109,9 @@ const AIChatButton = () => {
return ( return (
<MenuLinkItem icon={<AiOutlineIcon />} active={aiChatActive} to={'/chat'}> <MenuLinkItem icon={<AiOutlineIcon />} active={aiChatActive} to={'/chat'}>
<span data-testid="ai-chat">Intelligence</span> <span data-testid="ai-chat">
{t['com.affine.workspaceSubPath.chat']()}
</span>
</MenuLinkItem> </MenuLinkItem>
); );
}; };
@@ -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<void>;
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?.();
}
};
}
@@ -5,7 +5,11 @@ import {
type ChatContextValue, type ChatContextValue,
} from '@affine/core/blocksuite/ai/components/ai-chat-content'; } from '@affine/core/blocksuite/ai/components/ai-chat-content';
import type { ChatStatus } from '@affine/core/blocksuite/ai/components/ai-chat-messages'; 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 type { PromptKey } from '@affine/core/blocksuite/ai/provider/prompt';
import { getViewManager } from '@affine/core/blocksuite/manager/view'; import { getViewManager } from '@affine/core/blocksuite/manager/view';
import { NotificationServiceImpl } from '@affine/core/blocksuite/view-extensions/editor-view/notification-service'; import { NotificationServiceImpl } from '@affine/core/blocksuite/view-extensions/editor-view/notification-service';
@@ -37,6 +41,7 @@ import {
WorkbenchService, WorkbenchService,
} from '@affine/core/modules/workbench'; } from '@affine/core/modules/workbench';
import { WorkspaceService } from '@affine/core/modules/workspace'; import { WorkspaceService } from '@affine/core/modules/workspace';
import { useI18n } from '@affine/i18n';
import { RefNodeSlotsProvider } from '@blocksuite/affine/inlines/reference'; import { RefNodeSlotsProvider } from '@blocksuite/affine/inlines/reference';
import { BlockStdScope } from '@blocksuite/affine/std'; import { BlockStdScope } from '@blocksuite/affine/std';
import type { Workspace } from '@blocksuite/affine/store'; import type { Workspace } from '@blocksuite/affine/store';
@@ -45,6 +50,7 @@ import { useFramework, useService } from '@toeverything/infra';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { createSessionDeleteHandler } from '../chat-panel-utils';
import * as styles from './index.css'; import * as styles from './index.css';
type CopilotSession = Awaited<ReturnType<CopilotClient['getSession']>>; type CopilotSession = Awaited<ReturnType<CopilotClient['getSession']>>;
@@ -88,6 +94,7 @@ function useMockStd() {
} }
export const Component = () => { export const Component = () => {
const t = useI18n();
const framework = useFramework(); const framework = useFramework();
const [isBodyProvided, setIsBodyProvided] = useState(false); const [isBodyProvided, setIsBodyProvided] = useState(false);
const [isHeaderProvided, setIsHeaderProvided] = useState(false); const [isHeaderProvided, setIsHeaderProvided] = useState(false);
@@ -193,10 +200,46 @@ export const Component = () => {
); );
const confirmModal = useConfirmModal(); const confirmModal = useConfirmModal();
const notificationService = useMemo(
() =>
new NotificationServiceImpl(
confirmModal.closeConfirmModal,
confirmModal.openConfirmModal
),
[confirmModal.closeConfirmModal, confirmModal.openConfirmModal]
);
const specs = useAISpecs(); const specs = useAISpecs();
const mockStd = useMockStd(); const mockStd = useMockStd();
const handleAISubscribe = useAISubscribe(); 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 // init or update ai-chat-content
useEffect(() => { useEffect(() => {
if (!isBodyProvided) { if (!isBodyProvided) {
@@ -223,10 +266,7 @@ export const Component = () => {
); );
content.peekViewService = framework.get(PeekViewService); content.peekViewService = framework.get(PeekViewService);
content.affineThemeService = framework.get(AppThemeService); content.affineThemeService = framework.get(AppThemeService);
content.notificationService = new NotificationServiceImpl( content.notificationService = notificationService;
confirmModal.closeConfirmModal,
confirmModal.openConfirmModal
);
content.aiDraftService = framework.get(AIDraftService); content.aiDraftService = framework.get(AIDraftService);
content.aiToolsConfigService = framework.get(AIToolsConfigService); content.aiToolsConfigService = framework.get(AIToolsConfigService);
content.serverService = framework.get(ServerService); content.serverService = framework.get(ServerService);
@@ -258,6 +298,7 @@ export const Component = () => {
workspaceId, workspaceId,
confirmModal, confirmModal,
onContextChange, onContextChange,
notificationService,
specs, specs,
onOpenDoc, onOpenDoc,
handleAISubscribe, handleAISubscribe,
@@ -268,39 +309,31 @@ export const Component = () => {
if (!isHeaderProvided || !chatToolContainerRef.current) { if (!isHeaderProvided || !chatToolContainerRef.current) {
return; return;
} }
let tool = chatTool; const tool = getOrCreateAIChatToolbar(chatTool);
configureAIChatToolbar(tool, {
if (!tool) { session: currentSession,
tool = new AIChatToolbar(); workspaceId,
} status,
docDisplayConfig,
tool.session = currentSession; notificationService,
tool.workspaceId = workspaceId; onOpenSession,
tool.status = status; onNewSession: () => {
tool.docDisplayConfig = docDisplayConfig; if (!currentSession) return;
tool.onOpenSession = onOpenSession; setCurrentSession(null);
tool.notificationService = new NotificationServiceImpl( reMountChatContent();
confirmModal.closeConfirmModal, },
confirmModal.openConfirmModal onTogglePin: togglePin,
); onOpenDoc: (docId: string, sessionId: string) => {
const { workbench } = framework.get(WorkbenchService);
tool.onNewSession = () => { const viewService = framework.get(ViewService);
if (!currentSession) return; workbench.open(`/${docId}?sessionId=${sessionId}`, { at: 'active' });
setCurrentSession(null); workbench.openSidebar();
reMountChatContent(); viewService.view.activeSidebarTab('chat');
}; },
onSessionDelete: (sessionToDelete: BlockSuitePresets.AIRecentSession) => {
tool.onTogglePin = async () => { deleteSession(sessionToDelete).catch(console.error);
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');
};
// initial props // initial props
if (!chatTool) { if (!chatTool) {
@@ -318,8 +351,10 @@ export const Component = () => {
workspaceId, workspaceId,
confirmModal, confirmModal,
framework, framework,
deleteSession,
status, status,
reMountChatContent, reMountChatContent,
notificationService,
]); ]);
useEffect(() => { useEffect(() => {
@@ -390,7 +425,7 @@ export const Component = () => {
return ( return (
<> <>
<ViewTitle title="Intelligence" /> <ViewTitle title={t['com.affine.workspaceSubPath.chat']()} />
<ViewIcon icon="ai" /> <ViewIcon icon="ai" />
<ViewHeader> <ViewHeader>
<div className={styles.chatHeader}> <div className={styles.chatHeader}>
@@ -6,7 +6,11 @@ import {
type ChatContextValue, type ChatContextValue,
} from '@affine/core/blocksuite/ai/components/ai-chat-content'; } from '@affine/core/blocksuite/ai/components/ai-chat-content';
import type { ChatStatus } from '@affine/core/blocksuite/ai/components/ai-chat-messages'; 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 { createPlaygroundModal } from '@affine/core/blocksuite/ai/components/playground/modal';
import { registerAIAppEffects } from '@affine/core/blocksuite/ai/effects/app'; import { registerAIAppEffects } from '@affine/core/blocksuite/ai/effects/app';
import type { AffineEditorContainer } from '@affine/core/blocksuite/block-suite-editor'; import type { AffineEditorContainer } from '@affine/core/blocksuite/block-suite-editor';
@@ -31,6 +35,7 @@ import type {
CopilotChatHistoryFragment, CopilotChatHistoryFragment,
UpdateChatSessionInput, UpdateChatSessionInput,
} from '@affine/graphql'; } from '@affine/graphql';
import { useI18n } from '@affine/i18n';
import { RefNodeSlotsProvider } from '@blocksuite/affine/inlines/reference'; import { RefNodeSlotsProvider } from '@blocksuite/affine/inlines/reference';
import { DocModeProvider } from '@blocksuite/affine/shared/services'; import { DocModeProvider } from '@blocksuite/affine/shared/services';
import { createSignalFromObservable } from '@blocksuite/affine/shared/utils'; import { createSignalFromObservable } from '@blocksuite/affine/shared/utils';
@@ -40,6 +45,7 @@ import { useFramework, useService } from '@toeverything/infra';
import { html } from 'lit'; import { html } from 'lit';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { createSessionDeleteHandler } from '../../chat-panel-utils';
import * as styles from './chat.css'; import * as styles from './chat.css';
import { import {
resolveInitialSession, resolveInitialSession,
@@ -56,6 +62,7 @@ export interface SidebarTabProps {
export const EditorChatPanel = ({ editor, onLoad }: SidebarTabProps) => { export const EditorChatPanel = ({ editor, onLoad }: SidebarTabProps) => {
const framework = useFramework(); const framework = useFramework();
const workbench = useService(WorkbenchService).workbench; const workbench = useService(WorkbenchService).workbench;
const t = useI18n();
const { closeConfirmModal, openConfirmModal } = useConfirmModal(); const { closeConfirmModal, openConfirmModal } = useConfirmModal();
const notificationService = useMemo( const notificationService = useMemo(
@@ -230,30 +237,24 @@ export const EditorChatPanel = ({ editor, onLoad }: SidebarTabProps) => {
[doc, openSession, session?.pinned, session?.sessionId, workbench] [doc, openSession, session?.pinned, session?.sessionId, workbench]
); );
const deleteSession = useCallback( const deleteSession = useMemo(
async (sessionToDelete: BlockSuitePresets.AIRecentSession) => { () =>
if (!AIProvider.histories) { createSessionDeleteHandler({
return; t,
} notificationService,
const confirm = await notificationService.confirm({ canDeleteSession: () => Boolean(AIProvider.histories),
title: 'Delete this history?', cleanupSession: async sessionToDelete => {
message: await AIProvider.histories?.cleanup(
'Do you want to delete this AI conversation history? Once deleted, it cannot be recovered.', sessionToDelete.workspaceId,
confirmText: 'Delete', sessionToDelete.docId || undefined,
cancelText: 'Cancel', [sessionToDelete.sessionId]
}); );
if (confirm) { },
await AIProvider.histories.cleanup( isActiveSession: sessionToDelete =>
sessionToDelete.workspaceId, sessionToDelete.sessionId === session?.sessionId,
sessionToDelete.docId || undefined, onActiveSessionDeleted: newSession,
[sessionToDelete.sessionId] }),
); [newSession, notificationService, session?.sessionId, t]
if (sessionToDelete.sessionId === session?.sessionId) {
newSession();
}
}
},
[newSession, notificationService, session?.sessionId]
); );
const togglePin = useCallback(async () => { const togglePin = useCallback(async () => {
@@ -460,31 +461,26 @@ export const EditorChatPanel = ({ editor, onLoad }: SidebarTabProps) => {
return; return;
} }
let tool = chatToolbar; const tool = getOrCreateAIChatToolbar(chatToolbar);
configureAIChatToolbar(tool, {
if (!tool) { session,
tool = new AIChatToolbar(); workspaceId: doc.workspace.id,
} docId: doc.id,
status,
tool.session = session; docDisplayConfig,
tool.workspaceId = doc.workspace.id; notificationService,
tool.docId = doc.id; onNewSession: newSession,
tool.status = status; onTogglePin: togglePin,
tool.docDisplayConfig = docDisplayConfig; onOpenSession: (sessionId: string) => {
tool.notificationService = notificationService; openSession(sessionId).catch(console.error);
tool.onNewSession = newSession; },
tool.onTogglePin = togglePin; onOpenDoc: (docId: string, sessionId: string) => {
tool.onOpenSession = (sessionId: string) => { openDoc(docId, sessionId).catch(console.error);
openSession(sessionId).catch(console.error); },
}; onSessionDelete: (sessionToDelete: BlockSuitePresets.AIRecentSession) => {
tool.onOpenDoc = (docId: string, sessionId: string) => { deleteSession(sessionToDelete).catch(console.error);
openDoc(docId, sessionId).catch(console.error); },
}; });
tool.onSessionDelete = (
sessionToDelete: BlockSuitePresets.AIRecentSession
) => {
deleteSession(sessionToDelete).catch(console.error);
};
if (!chatToolbar) { if (!chatToolbar) {
chatToolbarContainerRef.current.append(tool); chatToolbarContainerRef.current.append(tool);
@@ -618,7 +614,7 @@ export const EditorChatPanel = ({ editor, onLoad }: SidebarTabProps) => {
<div className={styles.loading}> <div className={styles.loading}>
<Logo1Icon className={styles.loadingIcon} /> <Logo1Icon className={styles.loadingIcon} />
<div className={styles.loadingTitle}> <div className={styles.loadingTitle}>
AFFiNE AI is loading history... {t['com.affine.ai.chat-panel.loading-history']()}
</div> </div>
</div> </div>
</div> </div>
@@ -628,10 +624,13 @@ export const EditorChatPanel = ({ editor, onLoad }: SidebarTabProps) => {
<div className={styles.title}> <div className={styles.title}>
{isEmbedding ? ( {isEmbedding ? (
<span data-testid="chat-panel-embedding-progress"> <span data-testid="chat-panel-embedding-progress">
Embedding {done}/{total} {t.t('com.affine.ai.chat-panel.embedding-progress', {
done,
total,
})}
</span> </span>
) : ( ) : (
'AFFiNE AI' t['com.affine.ai.chat-panel.title']()
)} )}
</div> </div>
{playgroundVisible ? ( {playgroundVisible ? (
@@ -1,26 +1,26 @@
{ {
"ar": 99, "ar": 98,
"ca": 100, "ca": 100,
"da": 4, "da": 4,
"de": 100, "de": 99,
"el-GR": 99, "el-GR": 98,
"en": 100, "en": 100,
"es-AR": 99, "es-AR": 98,
"es-CL": 100, "es-CL": 100,
"es": 99, "es": 98,
"fa": 99, "fa": 98,
"fr": 100, "fr": 100,
"hi": 2, "hi": 2,
"it-IT": 100, "it-IT": 100,
"it": 1, "it": 1,
"ja": 99, "ja": 98,
"ko": 100, "ko": 99,
"nb-NO": 49, "nb-NO": 48,
"pl": 100, "pl": 100,
"pt-BR": 99, "pt-BR": 98,
"ru": 100, "ru": 100,
"sv-SE": 99, "sv-SE": 99,
"uk": 99, "uk": 98,
"ur": 2, "ur": 2,
"zh-Hans": 100, "zh-Hans": 100,
"zh-Hant": 99 "zh-Hant": 99
+35
View File
@@ -867,6 +867,37 @@ export function useAFFiNEI18N(): {
* `Failed to insert template, please try again.` * `Failed to insert template, please try again.`
*/ */
["com.affine.ai.template-insert.failed"](): string; ["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` * `All docs`
*/ */
@@ -7246,6 +7277,10 @@ export function useAFFiNEI18N(): {
* `All docs` * `All docs`
*/ */
["com.affine.workspaceSubPath.all"](): string; ["com.affine.workspaceSubPath.all"](): string;
/**
* `Intelligence`
*/
["com.affine.workspaceSubPath.chat"](): string;
/** /**
* `Trash` * `Trash`
*/ */
@@ -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-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.login-required.dialog-title": "Sign in to continue",
"com.affine.ai.template-insert.failed": "Failed to insert template, please try again.", "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.all-pages.header": "All docs",
"com.affine.app-sidebar.learn-more": "Learn more", "com.affine.app-sidebar.learn-more": "Learn more",
"com.affine.app-sidebar.star-us": "Star us", "com.affine.app-sidebar.star-us": "Star us",
@@ -1813,6 +1820,7 @@
"com.affine.workspaceList.workspaceListType.local": "Local storage", "com.affine.workspaceList.workspaceListType.local": "Local storage",
"com.affine.workspaceList.addServer": "Add Server", "com.affine.workspaceList.addServer": "Add Server",
"com.affine.workspaceSubPath.all": "All docs", "com.affine.workspaceSubPath.all": "All docs",
"com.affine.workspaceSubPath.chat": "Intelligence",
"com.affine.workspaceSubPath.trash": "Trash", "com.affine.workspaceSubPath.trash": "Trash",
"com.affine.workspaceSubPath.trash.empty-description": "Deleted docs will appear here.", "com.affine.workspaceSubPath.trash.empty-description": "Deleted docs will appear here.",
"com.affine.write_with_a_blank_page": "Write with a blank page", "com.affine.write_with_a_blank_page": "Write with a blank page",
@@ -206,6 +206,13 @@
"com.affine.ai.login-required.dialog-content": "要使用 AFFiNE AI,请先登录您的 AFFiNE Cloud 帐户。", "com.affine.ai.login-required.dialog-content": "要使用 AFFiNE AI,请先登录您的 AFFiNE Cloud 帐户。",
"com.affine.ai.login-required.dialog-title": "登录以继续", "com.affine.ai.login-required.dialog-title": "登录以继续",
"com.affine.ai.template-insert.failed": "插入模板失败,请重试。", "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.all-pages.header": "所有文档",
"com.affine.app-sidebar.learn-more": "了解更多", "com.affine.app-sidebar.learn-more": "了解更多",
"com.affine.app-sidebar.star-us": "给我们点亮星标", "com.affine.app-sidebar.star-us": "给我们点亮星标",
@@ -1808,6 +1815,7 @@
"com.affine.workspaceList.workspaceListType.local": "本地储存", "com.affine.workspaceList.workspaceListType.local": "本地储存",
"com.affine.workspaceList.addServer": "添加服务器", "com.affine.workspaceList.addServer": "添加服务器",
"com.affine.workspaceSubPath.all": "全部文档", "com.affine.workspaceSubPath.all": "全部文档",
"com.affine.workspaceSubPath.chat": "Intelligence",
"com.affine.workspaceSubPath.trash": "回收站", "com.affine.workspaceSubPath.trash": "回收站",
"com.affine.workspaceSubPath.trash.empty-description": "已删除的文档将显示在此处。", "com.affine.workspaceSubPath.trash.empty-description": "已删除的文档将显示在此处。",
"com.affine.write_with_a_blank_page": "在空白页面书写", "com.affine.write_with_a_blank_page": "在空白页面书写",
@@ -205,6 +205,13 @@
"com.affine.ai.login-required.dialog-content": "要使用 AFFiNE AI,請先登入您的 AFFiNE Cloud 帳戶。", "com.affine.ai.login-required.dialog-content": "要使用 AFFiNE AI,請先登入您的 AFFiNE Cloud 帳戶。",
"com.affine.ai.login-required.dialog-title": "登入以繼續", "com.affine.ai.login-required.dialog-title": "登入以繼續",
"com.affine.ai.template-insert.failed": "插入模板失敗,請重試。", "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.all-pages.header": "所有文件",
"com.affine.app-sidebar.learn-more": "了解更多", "com.affine.app-sidebar.learn-more": "了解更多",
"com.affine.app-sidebar.star-us": "給我們點亮星標", "com.affine.app-sidebar.star-us": "給我們點亮星標",
@@ -1785,6 +1792,7 @@
"com.affine.workspaceList.workspaceListType.local": "本地儲存", "com.affine.workspaceList.workspaceListType.local": "本地儲存",
"com.affine.workspaceList.addServer": "新增伺服器", "com.affine.workspaceList.addServer": "新增伺服器",
"com.affine.workspaceSubPath.all": "所有頁面", "com.affine.workspaceSubPath.all": "所有頁面",
"com.affine.workspaceSubPath.chat": "Intelligence",
"com.affine.workspaceSubPath.trash": "廢紙簍", "com.affine.workspaceSubPath.trash": "廢紙簍",
"com.affine.workspaceSubPath.trash.empty-description": "已刪除的文件將顯示在此處。", "com.affine.workspaceSubPath.trash.empty-description": "已刪除的文件將顯示在此處。",
"com.affine.write_with_a_blank_page": "在空白頁書寫", "com.affine.write_with_a_blank_page": "在空白頁書寫",