diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/components/add-popover.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/components/add-popover.ts
index dffe8fc4b9..e72959702a 100644
--- a/packages/frontend/core/src/blocksuite/ai/chat-panel/components/add-popover.ts
+++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/components/add-popover.ts
@@ -94,6 +94,11 @@ export class ChatPanelAddPopover extends SignalWatcher(
font-size: var(--affine-font-sm);
color: var(--affine-text-secondary-color);
}
+ .item-suffix {
+ margin-left: auto;
+ font-size: var(--affine-font-xs);
+ color: var(--affine-text-secondary-color);
+ }
${scrollbarStyle('.add-popover')}
`;
@@ -155,8 +160,28 @@ export class ChatPanelAddPopover extends SignalWatcher(
],
};
+ private get _menuGroup() {
+ switch (this._mode) {
+ case AddPopoverMode.Tags:
+ return [];
+ case AddPopoverMode.Collections:
+ return [];
+ default:
+ if (this._query) {
+ return [this._docGroup, this.uploadGroup];
+ }
+ return [this._docGroup, this.tcGroup, this.uploadGroup];
+ }
+ }
+
+ private get _flattenMenuGroup() {
+ return this._menuGroup.flatMap(group => {
+ return Array.isArray(group.items) ? group.items : group.items.value;
+ });
+ }
+
@state()
- private accessor _activatedItemIndex = 0;
+ private accessor _activatedIndex = 0;
@state()
private accessor _mode: AddPopoverMode = AddPopoverMode.Default;
@@ -176,6 +201,7 @@ export class ChatPanelAddPopover extends SignalWatcher(
override connectedCallback() {
super.connectedCallback();
this._updateDocGroup();
+ this.addEventListener('keydown', this._handleKeyDown);
}
override firstUpdated() {
@@ -184,11 +210,15 @@ export class ChatPanelAddPopover extends SignalWatcher(
});
}
+ override disconnectedCallback() {
+ super.disconnectedCallback();
+ this.removeEventListener('keydown', this._handleKeyDown);
+ }
+
override render() {
- const groups = this._getMenuGroup();
return html`
${this._renderSearchInput()} ${this._renderDivider()}
- ${this._renderMenuGroup(groups)}
+ ${this._renderMenuGroup(this._menuGroup)}
`;
}
@@ -238,40 +268,29 @@ export class ChatPanelAddPopover extends SignalWatcher(
private _renderMenuItems(items: MenuItem[], startIndex: number) {
return html`
${items.length > 0
- ? items.map(({ key, name, icon, action }, idx) => {
+ ? items.map(({ key, name, icon, action, suffix }, idx) => {
const curIdx = startIndex + idx;
return html`
action()?.catch(console.error)}
- @mousemove=${() => (this._activatedItemIndex = curIdx)}
+ @mousemove=${() => (this._activatedIndex = curIdx)}
>
${icon}
+ ${suffix ? html`${suffix}
` : ''}
`;
})
: html`
No Result
`}
`;
}
- private _getMenuGroup() {
- switch (this._mode) {
- case AddPopoverMode.Tags:
- return [];
- case AddPopoverMode.Collections:
- return [];
- default:
- if (this._query) {
- return [this._docGroup, this.uploadGroup];
- }
- return [this._docGroup, this.tcGroup, this.uploadGroup];
- }
- }
-
private _onInput(event: Event) {
this._query = (event.target as HTMLInputElement).value;
+ this._activatedIndex = 0;
this._updateDocGroup();
}
@@ -290,4 +309,46 @@ export class ChatPanelAddPopover extends SignalWatcher(
});
this.abortController.abort();
};
+
+ private readonly _handleKeyDown = (event: KeyboardEvent) => {
+ if (event.isComposing) return;
+
+ const { key } = event;
+
+ if (key === 'ArrowDown' || key === 'ArrowUp') {
+ event.preventDefault();
+ const totalItems = this._flattenMenuGroup.length;
+ if (totalItems === 0) return;
+
+ if (key === 'ArrowDown') {
+ this._activatedIndex = (this._activatedIndex + 1) % totalItems;
+ } else if (key === 'ArrowUp') {
+ this._activatedIndex =
+ (this._activatedIndex - 1 + totalItems) % totalItems;
+ }
+ this._scrollItemIntoView();
+ } else if (key === 'Enter') {
+ event.preventDefault();
+ this._flattenMenuGroup[this._activatedIndex]
+ .action()
+ ?.catch(console.error);
+ } else if (key === 'Escape') {
+ event.preventDefault();
+ this.abortController.abort();
+ }
+ };
+
+ private _scrollItemIntoView() {
+ requestAnimationFrame(() => {
+ const element = this.renderRoot.querySelector(
+ `[data-index="${this._activatedIndex}"]`
+ );
+ if (element) {
+ element.scrollIntoView({
+ behavior: 'smooth',
+ block: 'nearest',
+ });
+ }
+ });
+ }
}