fix(editor): adjust highlght style of comment and comment editor flickering (#13040)

### Before


https://github.com/user-attachments/assets/6b98946b-d53c-42fb-b341-e09ba5204523

### After


https://github.com/user-attachments/assets/274341de-33c4-4fd3-b01b-a8f7c25bf2fe



#### PR Dependency Tree


* **PR #13040** 👈

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 comment highlighting: Clicking now cycles through and
highlights individual comments one at a time instead of highlighting all
at once.
* Highlighting behavior is now more flexible, allowing highlighting to
be toggled on or off in certain scenarios.

* **Bug Fixes**
* Prevented flickering in the comment editor when focusing on comments.

* **Refactor**
* Enhanced selection and anchoring logic to support the new highlight
flag and updated types for improved clarity and control.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


#### PR Dependency Tree


* **PR #13040** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)
This commit is contained in:
L-Sun
2025-07-04 19:55:00 +08:00
committed by GitHub
parent ee8c7616bc
commit eb9652ed4c
4 changed files with 34 additions and 13 deletions
@@ -41,6 +41,8 @@ export class InlineComment extends WithDisposable(ShadowlessElement) {
})
accessor commentIds!: string[];
private _index: number = 0;
@consume({ context: stdContext })
private accessor _std!: BlockStdScope;
@@ -52,8 +54,8 @@ export class InlineComment extends WithDisposable(ShadowlessElement) {
}
private readonly _handleClick = () => {
const provider = this._provider;
provider && this.commentIds.forEach(id => provider.highlightComment(id));
this._provider?.highlightComment(this.commentIds[this._index]);
this._index = (this._index + 1) % this.commentIds.length;
};
private readonly _handleHighlight = (id: CommentId | null) => {
@@ -1,8 +1,12 @@
import {
type ReferenceParams,
ReferenceParamsSchema,
} from '@blocksuite/affine-model';
import { ReferenceParamsSchema } from '@blocksuite/affine-model';
import { BaseSelection, SelectionExtension } from '@blocksuite/store';
import z from 'zod';
const HighlightSelectionParamsSchema = ReferenceParamsSchema.extend({
highlight: z.boolean().optional(),
});
type HighlightSelectionParams = z.infer<typeof HighlightSelectionParamsSchema>;
export class HighlightSelection extends BaseSelection {
static override group = 'scene';
@@ -15,16 +19,24 @@ export class HighlightSelection extends BaseSelection {
readonly mode: 'page' | 'edgeless' = 'page';
constructor({ mode, blockIds, elementIds }: ReferenceParams) {
readonly highlight: boolean = true;
constructor({
mode,
blockIds,
elementIds,
highlight = true,
}: HighlightSelectionParams) {
super({ blockId: '[scene-highlight]' });
this.mode = mode ?? 'page';
this.blockIds = blockIds ?? [];
this.elementIds = elementIds ?? [];
this.highlight = highlight;
}
static override fromJSON(json: Record<string, unknown>): HighlightSelection {
const result = ReferenceParamsSchema.parse(json);
const result = HighlightSelectionParamsSchema.parse(json);
return new HighlightSelection(result);
}
@@ -12,6 +12,7 @@ import { styleMap } from 'lit/directives/style-map.js';
type Anchor = {
id: string;
mode: DocMode;
highlight: boolean;
};
export const AFFINE_SCROLL_ANCHORING_WIDGET = 'affine-scroll-anchoring-widget';
@@ -221,6 +222,7 @@ export class AffineScrollAnchoringWidget extends WidgetComponent {
mode,
blockIds: [bid],
elementIds: [eid],
highlight,
} = highlighted;
const id = mode === 'page' ? bid : eid || bid;
if (!id) return;
@@ -228,7 +230,7 @@ export class AffineScrollAnchoringWidget extends WidgetComponent {
// Consumes highlight selection
this.std.selection.clear(['highlight']);
this.anchor$.value = { mode, id };
this.anchor$.value = { mode, id, highlight };
this.#listened = true;
})
);
@@ -241,7 +243,7 @@ export class AffineScrollAnchoringWidget extends WidgetComponent {
override render() {
const anchor = this.anchor$.value;
if (!anchor) return nothing;
if (!anchor || !anchor.highlight) return nothing;
const { mode, id } = anchor;
@@ -56,7 +56,7 @@ export class Editor extends Entity {
const mode = get(this.mode$);
let id = selector?.blockIds?.[0];
let commentId = selector?.commentId;
let key = 'blockIds';
let key: 'blockIds' | 'elementIds' = 'blockIds';
if (mode === 'edgeless') {
const elementId = selector?.elementIds?.[0];
@@ -195,7 +195,7 @@ export class Editor extends Entity {
}
handleFocusAt(focusAt: {
key: string;
key: 'blockIds' | 'elementIds';
mode: DocMode;
id?: string;
commentId?: string;
@@ -208,6 +208,7 @@ export class Editor extends Entity {
let finalId = id;
let finalKey = key;
let highlight = true;
// If we have commentId but no blockId, find the block from the comment
if (commentId && !id && editorContainer.host?.std) {
@@ -230,6 +231,9 @@ export class Editor extends Entity {
finalKey = 'blockIds';
}
}
// Workaround: clear selection to avoid comment editor flickering
selection?.clear();
highlight = false;
}
if (mode === this.mode$.value && finalId) {
@@ -237,7 +241,8 @@ export class Editor extends Entity {
selection?.create(HighlightSelection, {
mode,
[finalKey]: [finalId],
}),
highlight,
} as const),
]);
}
}