fix(core): artifact rendering issue in standalone ai chat panel (#13164)

#### PR Dependency Tree


* **PR #13164** 👈

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**
* 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.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Peng Xiao
2025-07-11 16:09:07 +08:00
committed by GitHub
parent 58dc53581f
commit fef4a9eeb6
6 changed files with 83 additions and 48 deletions
@@ -7,6 +7,7 @@ import {
import type { ChatStatus } from '@affine/core/blocksuite/ai/components/ai-chat-messages';
import { AIChatToolbar } 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';
import { useAIChatConfig } from '@affine/core/components/hooks/affine/use-ai-chat-config';
import { useAISpecs } from '@affine/core/components/hooks/affine/use-ai-specs';
@@ -27,8 +28,12 @@ import {
WorkbenchService,
} from '@affine/core/modules/workbench';
import { WorkspaceService } from '@affine/core/modules/workspace';
import { RefNodeSlotsProvider } from '@blocksuite/affine/inlines/reference';
import { BlockStdScope } from '@blocksuite/affine/std';
import type { Workspace } from '@blocksuite/affine/store';
import { type Signal, signal } from '@preact/signals-core';
import { useFramework, useService } from '@toeverything/infra';
import { nanoid } from 'nanoid';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import * as styles from './index.css';
@@ -51,6 +56,26 @@ function useCopilotClient() {
);
}
function createMockStd(workspace: Workspace) {
workspace.meta.initialize();
const store = workspace.createDoc().getStore();
const std = new BlockStdScope({
store,
extensions: [...getViewManager().config.init().value.get('page')],
});
std.render();
return std;
}
function useMockStd() {
const workspace = useService(WorkspaceService).workspace;
const std = useMemo(() => {
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) {