fix(core): ai chat scrolldown indicator (#13382)

Close [AI-401](https://linear.app/affine-design/issue/AI-401)

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

* **Refactor**
* Improved scrolling behavior in the AI chat messages panel by making
the entire panel the scroll container, resulting in more consistent
scroll handling.
* Adjusted the position of the down-indicator for better visibility
during scrolling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Wu Yue
2025-08-01 11:56:12 +08:00
committed by GitHub
parent a088874c41
commit 3c9fe48c6c
2 changed files with 15 additions and 21 deletions

View File

@@ -314,9 +314,8 @@ export class AIChatContent extends SignalWatcher(
if (chatMessages) { if (chatMessages) {
chatMessages.updateComplete chatMessages.updateComplete
.then(() => { .then(() => {
const scrollContainer = chatMessages.getScrollContainer(); chatMessages.addEventListener('scrollend', () => {
scrollContainer?.addEventListener('scrollend', () => { this.lastScrollTop = chatMessages.scrollTop;
this.lastScrollTop = scrollContainer.scrollTop;
}); });
this._scrollListenersInitialized = true; this._scrollListenersInitialized = true;
}) })

View File

@@ -13,7 +13,7 @@ import type { BaseSelection, ExtensionType } from '@blocksuite/affine/store';
import { ArrowDownBigIcon as ArrowDownIcon } from '@blocksuite/icons/lit'; import { ArrowDownBigIcon as ArrowDownIcon } from '@blocksuite/icons/lit';
import type { Signal } from '@preact/signals-core'; import type { Signal } from '@preact/signals-core';
import { css, html, nothing, type PropertyValues } from 'lit'; import { css, html, nothing, type PropertyValues } from 'lit';
import { property, query, state } from 'lit/decorators.js'; import { property, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js'; import { classMap } from 'lit/directives/class-map.js';
import { repeat } from 'lit/directives/repeat.js'; import { repeat } from 'lit/directives/repeat.js';
import { debounce } from 'lodash-es'; import { debounce } from 'lodash-es';
@@ -125,10 +125,10 @@ export class AIChatMessages extends WithDisposable(ShadowlessElement) {
} }
.down-indicator { .down-indicator {
position: absolute; position: fixed;
left: 50%; left: 50%;
transform: translate(-50%, 0); transform: translate(-50%, 0);
bottom: 24px; bottom: 166px;
z-index: 1; z-index: 1;
border-radius: 50%; border-radius: 50%;
width: 32px; width: 32px;
@@ -217,9 +217,6 @@ export class AIChatMessages extends WithDisposable(ShadowlessElement) {
@property({ attribute: false }) @property({ attribute: false })
accessor onOpenDoc!: (docId: string, sessionId?: string) => void; accessor onOpenDoc!: (docId: string, sessionId?: string) => void;
@query('.chat-panel-messages-container')
accessor messagesContainer: HTMLDivElement | null = null;
@property({ @property({
type: String, type: String,
attribute: 'data-testid', attribute: 'data-testid',
@@ -227,10 +224,6 @@ export class AIChatMessages extends WithDisposable(ShadowlessElement) {
}) })
accessor testId = 'chat-panel-messages'; accessor testId = 'chat-panel-messages';
getScrollContainer(): HTMLDivElement | null {
return this.messagesContainer;
}
private get _isNetworkActive() { private get _isNetworkActive() {
return ( return (
!!this.networkSearchConfig.visible.value && !!this.networkSearchConfig.visible.value &&
@@ -264,8 +257,7 @@ export class AIChatMessages extends WithDisposable(ShadowlessElement) {
} }
private readonly _onScroll = () => { private readonly _onScroll = () => {
if (!this.messagesContainer) return; const { clientHeight, scrollTop, scrollHeight } = this;
const { clientHeight, scrollTop, scrollHeight } = this.messagesContainer;
this.canScrollDown = scrollHeight - scrollTop - clientHeight > 200; this.canScrollDown = scrollHeight - scrollTop - clientHeight > 200;
}; };
@@ -293,7 +285,6 @@ export class AIChatMessages extends WithDisposable(ShadowlessElement) {
'independent-mode': !!this.independentMode, 'independent-mode': !!this.independentMode,
})} })}
data-testid="chat-panel-messages-container" data-testid="chat-panel-messages-container"
@scroll=${() => this._debouncedOnScroll()}
> >
${filteredItems.length === 0 ${filteredItems.length === 0
? html`<div ? html`<div
@@ -411,6 +402,12 @@ export class AIChatMessages extends WithDisposable(ShadowlessElement) {
) )
); );
} }
// Add scroll event listener to the host element
this.addEventListener('scroll', this._debouncedOnScroll);
disposables.add(() => {
this.removeEventListener('scroll', this._debouncedOnScroll);
});
} }
protected override updated(_changedProperties: PropertyValues) { protected override updated(_changedProperties: PropertyValues) {
@@ -428,9 +425,8 @@ export class AIChatMessages extends WithDisposable(ShadowlessElement) {
scrollToEnd() { scrollToEnd() {
requestAnimationFrame(() => { requestAnimationFrame(() => {
if (!this.messagesContainer) return; this.scrollTo({
this.messagesContainer.scrollTo({ top: this.scrollHeight,
top: this.messagesContainer.scrollHeight,
behavior: 'smooth', behavior: 'smooth',
}); });
}); });
@@ -438,8 +434,7 @@ export class AIChatMessages extends WithDisposable(ShadowlessElement) {
scrollToPos(top: number) { scrollToPos(top: number) {
requestAnimationFrame(() => { requestAnimationFrame(() => {
if (!this.messagesContainer) return; this.scrollTo({ top });
this.messagesContainer.scrollTo({ top });
}); });
} }