OvidijusParsiunas / deep-chat

Fully customizable AI chatbot component for your website
https://deepchat.dev
MIT License
1.27k stars 175 forks source link

When streaming response data containing custom elements, the chat box scrollbar doesn't automatically scroll to the bottom #109

Closed buzhou9 closed 5 months ago

buzhou9 commented 5 months ago

When streaming response data containing custom elements, the chat box scrollbar doesn't automatically scroll to the bottom.

I look at your source code, and it's possible that the code that scrolls to the bottom is executed before the custom element is rendered.

I did tests and the rendering inside the custom element should be asynchronous.

When the connectedCallback hook for a custom element is executed, it is also not possible to set the scrollbar to the bottom because the rendering has not yet been completed.

This should be a matter of concern.

I've now set a timer to deal with this issue, and I hope you have a better suggestion.

public upsertStreamedMessage(response?: Response) {
    if (this._hasStreamEnded) return;
    if (response?.text === undefined && response?.html === undefined) {
      return console.error(ErrorMessages.INVALID_STREAM_EVENT);
    }
    const content = response?.text || response?.html || '';
    const isScrollbarAtBottomOfElement = ElementUtils.isScrollbarAtBottomOfElement(this._messages.elementRef);
    const streamType = response?.text !== undefined ? 'text' : 'html';
    if (!this._elements && this._streamedContent === '') {
      this.setInitialState(streamType, content, response?.role);
    } else if (this._streamType !== streamType) {
      return console.error(ErrorMessages.INVALID_STREAM_EVENT_MIX);
    } else {
      this.updateBasedOnType(content, streamType, this._elements?.bubbleElement as HTMLElement, response?.overwrite);
    }
// This line of code executes with the custom element not rendered
    if (isScrollbarAtBottomOfElement) ElementUtils.scrollToBottom(this._messages.elementRef);
  }
buzhou9 commented 5 months ago

I use the lit framework in developing custom elements which provides an enhanced lifecycle firstUpdated where it is feasible to execute scrolling to the bottom of the code.

override firstUpdated () {
    console.log('firstUpdated', Date.now())
    const chatbotCoreDom = document.getElementById('chatbot-core') as unknown as {
      scrollToBottom: () => {}
    }
    chatbotCoreDom.scrollToBottom()
  }
OvidijusParsiunas commented 5 months ago

Hi @buzhou9.

I have considered adding a ResizeObserver to each message element to monitor their dimensions and scroll down when their sizes change. However, there are a couple of problems with this:

For these reasons we instead recommend the use of scrollToBottom method to force scrolling when you deem it to be appropriate.

You can also experiment with native JavaScript scroll methods in your custom element's code to scroll your element into view: e.g. scrollIntoView.

buzhou9 commented 5 months ago

你好@buzhou9。

我考虑过向每个消息元素添加一个ResizeObserver以监视它们的尺寸并在它们的尺寸发生变化时向下滚动。然而,这样做有几个问题:

  • 如果消息气泡不在末尾(底部)并且它们的大小发生变化 - 自动滚动将滚动远离它们的可见视图。
  • 如果一条消息的大小正在扩大,但用户正在上方视图中阅读另一条消息 - 滚动将使他们难以阅读它。
  • 如果聊天的尺寸是动态的(允许最小化/扩展),则每次尺寸更改时都会触发滚动,即使用户专注于特定消息也是如此。

出于这些原因,我们建议在您认为合适时使用scrollToBottom方法来强制滚动。

您还可以在自定义元素的代码中尝试使用本机 JavaScript 滚动方法,将元素滚动到视图中:例如,scrollIntoView

Thanks, scrollIntoView was the one I didn't expect.