Open-Assistant icon indicating copy to clipboard operation
Open-Assistant copied to clipboard

Implement auto scroll when stream message

Open notmd opened this issue 1 year ago • 2 comments

After trying a bunch of methods I found this is the easiest way to do it. Other approaches are way more complicated. Also found this lib https://github.com/compulim/react-scroll-to-bottom OAI also uses it, but it doesn't support SSR and unmaintained for years. This solution is not really reliable, if you paste a prompt with many lines (4 or above), it doesn't work. It would be if someone can patch the react-scroll-to-bottom to support SSR.

notmd avatar Apr 22 '23 11:04 notmd

I would probably keep this very simple and just add

messagesEndRef.current.scrollIntoView();

in the onToken handler after the new frame, and for good measure one final time after the streaming has finished.

I personally prefer without the smooth scrolling, because the scrolling speed is inconsistent depending on whether you have a new line or not, but that's a preference.

Your solution is great but here are a few things I want to support for better UX

  • Auto scroll on first render from serverside
  • Only auto scroll when the user scrolls to bottom (with a threshold is ok but 0 threshold is much better).

I can't find any simple way to support both of these. Only the react-scroll-to-bottom is perfect but it doesn't support SSR... Also I just tested your solution, it doesn't work on the last scroll, since react doesn't render the message yet when we call setState yet. Here is the code

let message: InferenceMessage | null;
      if (status === 204) {
        // message already processed, get it immediately
        message = await get(API_ROUTES.GET_MESSAGE(chatId, assistant_message.id));
      } else {
        message = await handleChatEventStream({
          stream: body!,
          onError: console.error,
          onPending: setQueueInfo,
          onToken: async (text) => {
            setQueueInfo(null);
            setResponse(text);
            messagesEndRef.current?.scrollIntoView();
            await new Promise(requestAnimationFrame);
          },
        });
      }
      if (message) {
        setMessages((messages) => [...messages, message!]);
      }
      messagesEndRef.current?.scrollIntoView();

notmd avatar Apr 22 '23 16:04 notmd

Maybe we should go with my solution, remove the threshold check and ignore the UX...lolll

notmd avatar Apr 22 '23 16:04 notmd

@AbdBarho I just switch to the new implementation, works much better now

notmd avatar Apr 26 '23 16:04 notmd

Nice! I didn't see this when I opened #2880.

This feature is a convenience, so it's not the end of the world if it handles corner-cases imperfectly. Just having the UI generally keep up with the output will make it much nicer to use.

7ombie avatar May 02 '23 21:05 7ombie