From fef4a9eeb65837eabb4f072268d7fc160722ca9d Mon Sep 17 00:00:00 2001 From: Peng Xiao Date: Fri, 11 Jul 2025 16:09:07 +0800 Subject: [PATCH] fix(core): artifact rendering issue in standalone ai chat panel (#13164) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### PR Dependency Tree * **PR #13164** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) ## Summary by CodeRabbit * **New Features** * Improved integration of workspace context into AI chat, enabling more responsive interactions when clicking document links within chat messages. * Enhanced document opening experience from chat by reacting to link clicks and providing direct access to related documents. * **Refactor** * Streamlined notification handling and workspace context management within chat-related components for better maintainability and performance. --- .../ai/chat-panel/message/assistant.ts | 11 ++- .../ai-chat-content/ai-chat-content.ts | 11 ++- .../ai-chat-messages/ai-chat-messages.ts | 11 ++- .../ai-message-content/stream-objects.ts | 17 +++-- .../ai/components/ai-tools/doc-compose.ts | 9 +-- .../desktop/pages/workspace/chat/index.tsx | 72 +++++++++++-------- 6 files changed, 83 insertions(+), 48 deletions(-) diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/message/assistant.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/message/assistant.ts index ec53ce1a21..1b1ef6b07d 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/message/assistant.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/message/assistant.ts @@ -3,8 +3,11 @@ import type { AppThemeService } from '@affine/core/modules/theme'; import type { CopilotChatHistoryFragment } from '@affine/graphql'; import { WithDisposable } from '@blocksuite/affine/global/lit'; import { isInsidePageEditor } from '@blocksuite/affine/shared/utils'; -import type { EditorHost } from '@blocksuite/affine/std'; -import { ShadowlessElement } from '@blocksuite/affine/std'; +import { + type BlockStdScope, + type EditorHost, + ShadowlessElement, +} from '@blocksuite/affine/std'; import type { ExtensionType } from '@blocksuite/affine/store'; import type { NotificationService } from '@blocksuite/affine-shared/services'; import type { Signal } from '@preact/signals-core'; @@ -37,6 +40,9 @@ export class ChatMessageAssistant extends WithDisposable(ShadowlessElement) { @property({ attribute: false }) accessor host: EditorHost | null | undefined; + @property({ attribute: false }) + accessor std: BlockStdScope | null | undefined; + @property({ attribute: false }) accessor item!: ChatMessage; @@ -124,6 +130,7 @@ export class ChatMessageAssistant extends WithDisposable(ShadowlessElement) { private renderStreamObjects(answer: StreamObject[]) { return html` @@ -125,7 +132,7 @@ export class ChatContentStreamObjects extends WithDisposable( case 'doc_compose': return html` { + if (!workspace) return null; + return createMockStd(workspace.docCollection); + }, [workspace]); + return std; +} + export const Component = () => { const framework = useFramework(); const [isBodyProvided, setIsBodyProvided] = useState(false); @@ -145,6 +170,7 @@ export const Component = () => { const confirmModal = useConfirmModal(); const specs = useAISpecs(); + const mockStd = useMockStd(); // init or update ai-chat-content useEffect(() => { @@ -161,6 +187,7 @@ export const Component = () => { content.session = currentSession; content.workspaceId = workspaceId; content.extensions = specs; + content.std = mockStd; content.docDisplayConfig = docDisplayConfig; content.searchMenuConfig = searchMenuConfig; content.networkSearchConfig = networkSearchConfig; @@ -192,6 +219,7 @@ export const Component = () => { docDisplayConfig, framework, isBodyProvided, + mockStd, networkSearchConfig, reasoningConfig, searchMenuConfig, @@ -260,37 +288,21 @@ export const Component = () => { status, ]); - // restore pinned session useEffect(() => { - if (!chatContent) return; - - const controller = new AbortController(); - const signal = controller.signal; - client - .getSessions( - workspaceId, - {}, - undefined, - { pinned: true, limit: 1 }, - signal - ) - .then(sessions => { - if (!Array.isArray(sessions)) return; - const session = sessions[0]; - if (!session) return; - setCurrentSession(session); - if (chatContent) { - chatContent.session = session; - chatContent.reloadSession(); - } - }) - .catch(console.error); - - // abort the request - return () => { - controller.abort(); - }; - }, [chatContent, client, workspaceId]); + const refNodeSlots = mockStd?.getOptional(RefNodeSlotsProvider); + if (!refNodeSlots) return; + const sub = refNodeSlots.docLinkClicked.subscribe(event => { + const { workbench } = framework.get(WorkbenchService); + workbench.openDoc({ + docId: event.pageId, + mode: event.params?.mode, + blockIds: event.params?.blockIds, + elementIds: event.params?.elementIds, + refreshKey: nanoid(), + }); + }); + return () => sub.unsubscribe(); + }, [framework, mockStd]); const onChatContainerRef = useCallback((node: HTMLDivElement) => { if (node) {