fix(editor): subscribe docLinkClicked event for text renderer (#12406)

Closes: [BS-3520](https://linear.app/affine-design/issue/BS-3520/chat-panel-doc-citation-点击没有响应)

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

- **New Features**
  - Enhanced citation cards to support double-click actions for improved interaction.
  - Added the ability to open a preview view when clicking document links within rendered text content.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
donteatfriedrice
2025-05-21 07:34:28 +00:00
parent 928892c5b4
commit 20e93543e2
2 changed files with 35 additions and 2 deletions

View File

@@ -259,7 +259,7 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
); );
} }
private _handleDoubleClick(event: MouseEvent) { private readonly _handleDoubleClick = (event: MouseEvent) => {
event.stopPropagation(); event.stopPropagation();
const openDocService = this.std.get(OpenDocExtensionIdentifier); const openDocService = this.std.get(OpenDocExtensionIdentifier);
const shouldOpenInPeek = const shouldOpenInPeek =
@@ -270,7 +270,7 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
: 'open-in-active-view', : 'open-in-active-view',
event, event,
}); });
} };
private _isDocEmpty() { private _isDocEmpty() {
const linkedDoc = this.linkedDoc; const linkedDoc = this.linkedDoc;
@@ -311,6 +311,7 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
.citationIdentifier=${footnoteIdentifier} .citationIdentifier=${footnoteIdentifier}
.active=${this.selected$.value} .active=${this.selected$.value}
.onClickCallback=${this._handleClick} .onClickCallback=${this._handleClick}
.onDoubleClickCallback=${this._handleDoubleClick}
></affine-citation-card> ></affine-citation-card>
</div> `; </div> `;
}; };

View File

@@ -2,8 +2,10 @@ import { createReactComponentFromLit } from '@affine/component';
import { getStoreManager } from '@affine/core/blocksuite/manager/store'; import { getStoreManager } from '@affine/core/blocksuite/manager/store';
import { getViewManager } from '@affine/core/blocksuite/manager/view'; import { getViewManager } from '@affine/core/blocksuite/manager/view';
import type { FeatureFlagService } from '@affine/core/modules/feature-flag'; import type { FeatureFlagService } from '@affine/core/modules/feature-flag';
import { PeekViewProvider } from '@blocksuite/affine/components/peek';
import { Container, type ServiceProvider } from '@blocksuite/affine/global/di'; import { Container, type ServiceProvider } from '@blocksuite/affine/global/di';
import { WithDisposable } from '@blocksuite/affine/global/lit'; import { WithDisposable } from '@blocksuite/affine/global/lit';
import { RefNodeSlotsProvider } from '@blocksuite/affine/inlines/reference';
import { import {
codeBlockWrapMiddleware, codeBlockWrapMiddleware,
defaultImageProxyMiddleware, defaultImageProxyMiddleware,
@@ -34,6 +36,7 @@ import { classMap } from 'lit/directives/class-map.js';
import { keyed } from 'lit/directives/keyed.js'; import { keyed } from 'lit/directives/keyed.js';
import { literal } from 'lit/static-html.js'; import { literal } from 'lit/static-html.js';
import React from 'react'; import React from 'react';
import { filter } from 'rxjs/operators';
import { markDownToDoc } from '../../utils'; import { markDownToDoc } from '../../utils';
import type { import type {
@@ -240,6 +243,28 @@ export class TextRenderer extends WithDisposable(ShadowlessElement) {
private _timer?: ReturnType<typeof setInterval> | null = null; private _timer?: ReturnType<typeof setInterval> | null = null;
private readonly _subscribeDocLinkClicked = () => {
const refNodeSlots = this.host?.std.getOptional(RefNodeSlotsProvider);
if (!refNodeSlots) return;
this.disposables.add(
refNodeSlots.docLinkClicked
.pipe(
filter(
options => !!this._previewHost && options.host === this._previewHost
)
)
.subscribe(options => {
// Open the doc in center peek
this.host?.std
.getOptional(PeekViewProvider)
?.peek({
docId: options.pageId,
})
.catch(console.error);
})
);
};
private readonly _updateDoc = () => { private readonly _updateDoc = () => {
if (this._answers.length > 0) { if (this._answers.length > 0) {
const latestAnswer = this._answers.pop(); const latestAnswer = this._answers.pop();
@@ -311,6 +336,10 @@ export class TextRenderer extends WithDisposable(ShadowlessElement) {
} }
} }
override firstUpdated() {
this._subscribeDocLinkClicked();
}
private disposeDoc() { private disposeDoc() {
this._doc?.dispose(); this._doc?.dispose();
this._doc?.workspace.dispose(); this._doc?.workspace.dispose();
@@ -382,6 +411,9 @@ export class TextRenderer extends WithDisposable(ShadowlessElement) {
@query('.text-renderer-container') @query('.text-renderer-container')
private accessor _container!: HTMLDivElement; private accessor _container!: HTMLDivElement;
@query('.text-renderer-container editor-host')
private accessor _previewHost: EditorHost | null = null;
@property({ attribute: false }) @property({ attribute: false })
accessor answer!: string; accessor answer!: string;