diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/index.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/index.ts index a702917ad0..9e65935e0c 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/index.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/index.ts @@ -411,6 +411,7 @@ export class ChatPanel extends SignalWatcher( .onEmbeddingProgressChange=${this.onEmbeddingProgressChange} .onContextChange=${this.onContextChange} .width=${this.sidebarWidth} + .onOpenDoc=${this.openDoc} >` )} `; 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 4db241869f..45f43a6d50 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 @@ -86,6 +86,9 @@ export class ChatMessageAssistant extends WithDisposable(ShadowlessElement) { @property({ attribute: false }) accessor docDisplayService!: DocDisplayConfig; + @property({ attribute: false }) + accessor onOpenDoc!: (docId: string, sessionId?: string) => void; + get state() { const { isLast, status } = this; return isLast @@ -146,6 +149,7 @@ export class ChatMessageAssistant extends WithDisposable(ShadowlessElement) { .notificationService=${this.notificationService} .theme=${this.affineThemeService.appTheme.themeSignal} .docDisplayService=${this.docDisplayService} + .onOpenDoc=${this.onOpenDoc} >`; } diff --git a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-content/ai-chat-content.ts b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-content/ai-chat-content.ts index 8e0fc6e542..7a4dab495d 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-content/ai-chat-content.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-content/ai-chat-content.ts @@ -157,6 +157,9 @@ export class AIChatContent extends SignalWatcher( @property({ attribute: false }) accessor onContextChange!: (context: Partial) => void; + @property({ attribute: false }) + accessor onOpenDoc!: (docId: string, sessionId?: string) => void; + @property({ attribute: false }) accessor width: Signal | undefined; @@ -378,6 +381,7 @@ export class AIChatContent extends SignalWatcher( .independentMode=${this.independentMode} .messages=${this.messages} .docDisplayService=${this.docDisplayConfig} + .onOpenDoc=${this.onOpenDoc} > void; + @query('.chat-panel-messages-container') accessor messagesContainer: HTMLDivElement | null = null; @@ -333,6 +336,7 @@ export class AIChatMessages extends WithDisposable(ShadowlessElement) { .width=${this.width} .independentMode=${this.independentMode} .docDisplayService=${this.docDisplayService} + .onOpenDoc=${this.onOpenDoc} >`; } else if (isChatAction(item) && this.host) { return html` void; + private renderToolCall(streamObject: StreamObject) { if (streamObject.type !== 'tool-call') { return nothing; @@ -183,11 +186,13 @@ export class ChatContentStreamObjects extends WithDisposable( .data=${streamObject} .width=${this.width} .docDisplayService=${this.docDisplayService} + .onOpenDoc=${this.onOpenDoc} >`; case 'doc_keyword_search': return html``; case 'doc_read': return html` | undefined; + @property({ attribute: false }) + accessor onOpenDoc!: (docId: string, sessionId?: string) => void; + renderToolCall() { return html` ({ - title: item.title, + title: html` this.onOpenDoc(item.docId)} + > + ${item.title} + `, icon: PageIcon(), })); } catch (err) { diff --git a/packages/frontend/core/src/blocksuite/ai/components/ai-tools/doc-semantic-search-result.ts b/packages/frontend/core/src/blocksuite/ai/components/ai-tools/doc-semantic-search-result.ts index a9771f4dd8..152741fc01 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/ai-tools/doc-semantic-search-result.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/ai-tools/doc-semantic-search-result.ts @@ -2,7 +2,7 @@ import { WithDisposable } from '@blocksuite/global/lit'; import { AiEmbeddingIcon, PageIcon } from '@blocksuite/icons/lit'; import { ShadowlessElement } from '@blocksuite/std'; import type { Signal } from '@preact/signals-core'; -import { html, nothing } from 'lit'; +import { css, html, nothing } from 'lit'; import { property } from 'lit/decorators.js'; import type { DocDisplayConfig } from '../ai-chat-chips'; @@ -54,6 +54,12 @@ function parseResultContent(content: string) { } export class DocSemanticSearchResult extends WithDisposable(ShadowlessElement) { + static override styles = css` + .doc-semantic-search-result-title { + cursor: pointer; + } + `; + @property({ attribute: false }) accessor data!: DocSemanticSearchToolCall | DocSemanticSearchToolResult; @@ -63,6 +69,9 @@ export class DocSemanticSearchResult extends WithDisposable(ShadowlessElement) { @property({ attribute: false }) accessor docDisplayService!: DocDisplayConfig; + @property({ attribute: false }) + accessor onOpenDoc!: (docId: string, sessionId?: string) => void; + renderToolCall() { return html` ({ ...parseResultContent(result.content), - title: this.docDisplayService.getTitle(result.docId), + title: html` this.onOpenDoc(result.docId)} + > + ${this.docDisplayService.getTitle(result.docId)} + `, })) .filter(Boolean)} >`; diff --git a/packages/frontend/core/src/blocksuite/ai/components/ai-tools/tool-result-card.ts b/packages/frontend/core/src/blocksuite/ai/components/ai-tools/tool-result-card.ts index 5264b2cc6c..318cb36758 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/ai-tools/tool-result-card.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/ai-tools/tool-result-card.ts @@ -8,7 +8,7 @@ import { css, html, nothing, type TemplateResult } from 'lit'; import { property, state } from 'lit/decorators.js'; export interface ToolResult { - title: string; + title: string | TemplateResult<1>; icon?: string | TemplateResult<1>; content?: string; } 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 a5331b32d1..f4bdc5c7f6 100644 --- a/packages/frontend/core/src/desktop/pages/workspace/chat/index.tsx +++ b/packages/frontend/core/src/desktop/pages/workspace/chat/index.tsx @@ -94,6 +94,7 @@ export const Component = () => { const chatToolContainerRef = useRef(null); const widthSignalRef = useRef>(signal(0)); const client = useCopilotClient(); + const workbench = useService(WorkbenchService).workbench; const workspaceId = useService(WorkspaceService).workspace.id; @@ -173,6 +174,13 @@ export const Component = () => { setStatus(context.status ?? 'idle'); }, []); + const onOpenDoc = useCallback( + (docId: string) => { + workbench.openDoc(docId, { at: 'active' }); + }, + [workbench] + ); + const confirmModal = useConfirmModal(); const specs = useAISpecs(); const mockStd = useMockStd(); @@ -208,6 +216,7 @@ export const Component = () => { confirmModal.openConfirmModal ); content.createSession = createSession; + content.onOpenDoc = onOpenDoc; if (!chatContent) { // initial values that won't change @@ -232,6 +241,7 @@ export const Component = () => { confirmModal, onContextChange, specs, + onOpenDoc, ]); // init or update header ai-chat-toolbar