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
@@ -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`<chat-content-stream-objects
.host=${this.host}
.std=${this.std}
.answer=${answer}
.state=${this.state}
.width=${this.width}
@@ -6,8 +6,11 @@ import type {
CopilotChatHistoryFragment,
} from '@affine/graphql';
import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit';
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';
@@ -127,6 +130,9 @@ export class AIChatContent extends SignalWatcher(
@property({ attribute: false })
accessor host: EditorHost | null | undefined;
@property({ attribute: false })
accessor std: BlockStdScope | null | undefined;
@property({ attribute: false })
accessor session!: CopilotChatHistoryFragment | null | undefined;
@@ -400,6 +406,7 @@ export class AIChatContent extends SignalWatcher(
})}
${ref(this.chatMessagesRef)}
.host=${this.host}
.std=${this.std}
.workspaceId=${this.workspaceId}
.docId=${this.docId}
.session=${this.session}
@@ -6,8 +6,11 @@ import {
type FeatureFlagService,
type NotificationService,
} from '@blocksuite/affine/shared/services';
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 { BaseSelection, ExtensionType } from '@blocksuite/affine/store';
import { ArrowDownBigIcon as ArrowDownIcon } from '@blocksuite/icons/lit';
import type { Signal } from '@preact/signals-core';
@@ -160,6 +163,9 @@ export class AIChatMessages extends WithDisposable(ShadowlessElement) {
@property({ attribute: false })
accessor host: EditorHost | null | undefined;
@property({ attribute: false })
accessor std: BlockStdScope | null | undefined;
@property({ attribute: false })
accessor workspaceId!: string;
@@ -318,6 +324,7 @@ export class AIChatMessages extends WithDisposable(ShadowlessElement) {
} else if (isChatMessage(item) && item.role === 'assistant') {
return html`<chat-message-assistant
.host=${this.host}
.std=${this.std}
.session=${this.session}
.item=${item}
.isLast=${isLast}
@@ -1,7 +1,11 @@
import type { FeatureFlagService } from '@affine/core/modules/feature-flag';
import { WithDisposable } from '@blocksuite/affine/global/lit';
import type { ColorScheme } from '@blocksuite/affine/model';
import { type EditorHost, 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';
@@ -29,6 +33,9 @@ export class ChatContentStreamObjects extends WithDisposable(
@property({ attribute: false })
accessor host: EditorHost | null | undefined;
@property({ attribute: false })
accessor std: BlockStdScope | null | undefined;
@property({ attribute: false })
accessor state: AffineAIPanelState = 'finished';
@@ -70,7 +77,7 @@ export class ChatContentStreamObjects extends WithDisposable(
case 'doc_compose':
return html`
<doc-compose-tool
.std=${this.host?.std}
.std=${this.std || this.host?.std}
.data=${streamObject}
.width=${this.width}
.theme=${this.theme}
@@ -80,7 +87,7 @@ export class ChatContentStreamObjects extends WithDisposable(
case 'code_artifact':
return html`
<code-artifact-tool
.std=${this.host?.std}
.std=${this.std || this.host?.std}
.data=${streamObject}
.width=${this.width}
></code-artifact-tool>
@@ -125,7 +132,7 @@ export class ChatContentStreamObjects extends WithDisposable(
case 'doc_compose':
return html`
<doc-compose-tool
.std=${this.host?.std}
.std=${this.std || this.host?.std}
.data=${streamObject}
.width=${this.width}
.theme=${this.theme}
@@ -135,7 +142,7 @@ export class ChatContentStreamObjects extends WithDisposable(
case 'code_artifact':
return html`
<code-artifact-tool
.std=${this.host?.std}
.std=${this.std || this.host?.std}
.data=${streamObject}
.width=${this.width}
.theme=${this.theme}
@@ -4,7 +4,6 @@ import { getEmbedLinkedDocIcons } from '@blocksuite/affine/blocks/embed-doc';
import { LoadingIcon } from '@blocksuite/affine/components/icons';
import { RefNodeSlotsProvider } from '@blocksuite/affine/inlines/reference';
import type { ColorScheme } from '@blocksuite/affine/model';
import { NotificationProvider } from '@blocksuite/affine/shared/services';
import { unsafeCSSVarV2 } from '@blocksuite/affine/shared/theme';
import { MarkdownTransformer } from '@blocksuite/affine/widgets/linked-doc';
import { CopyIcon, PageIcon, ToolIcon } from '@blocksuite/icons/lit';
@@ -110,8 +109,6 @@ export class DocComposeTool extends ArtifactTool<
}
protected override getPreviewContent() {
if (!this.std) return html``;
const std = this.std;
const resultData = this.data;
const title = this.data.args.title;
const result = resultData.type === 'tool-result' ? resultData.result : null;
@@ -122,8 +119,7 @@ export class DocComposeTool extends ArtifactTool<
${successResult
? html`<text-renderer
.answer=${successResult.markdown}
.host=${std.host}
.schema=${std.store.schema}
.schema=${this.std?.store.schema}
.options=${{
customHeading: true,
extensions: getCustomPageEditorBlockSpecs(),
@@ -161,7 +157,6 @@ export class DocComposeTool extends ArtifactTool<
return;
}
const workspace = std.store.workspace;
const notificationService = std.get(NotificationProvider);
const refNodeSlots = std.getOptional(RefNodeSlotsProvider);
const docId = await MarkdownTransformer.importMarkdownToDoc({
collection: workspace,
@@ -171,7 +166,7 @@ export class DocComposeTool extends ArtifactTool<
extensions: getStoreManager().config.init().value.get('store'),
});
if (docId) {
const open = await notificationService.confirm({
const open = await this.notificationService.confirm({
title: 'Open the doc you just created',
message: 'Doc saved successfully! Would you like to open it now?',
cancelText: 'Cancel',