mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-11-19 12:40:09 +01:00
Make streaming behavior more natural (#2336)
* fix scrolling behavior + add cursor to streaming chats * lint * linting --------- Co-authored-by: timothycarambat <rambat1010@gmail.com>
This commit is contained in:
parent
12b8af4654
commit
d75fee0c07
@ -20,13 +20,16 @@ export default function ChatHistory({
|
|||||||
regenerateAssistantMessage,
|
regenerateAssistantMessage,
|
||||||
hasAttachments = false,
|
hasAttachments = false,
|
||||||
}) {
|
}) {
|
||||||
|
const lastScrollTopRef = useRef(0);
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
const { threadSlug = null } = useParams();
|
const { threadSlug = null } = useParams();
|
||||||
const { showing, showModal, hideModal } = useManageWorkspaceModal();
|
const { showing, showModal, hideModal } = useManageWorkspaceModal();
|
||||||
const [isAtBottom, setIsAtBottom] = useState(true);
|
const [isAtBottom, setIsAtBottom] = useState(true);
|
||||||
const chatHistoryRef = useRef(null);
|
const chatHistoryRef = useRef(null);
|
||||||
const [textSize, setTextSize] = useState("normal");
|
const [textSize, setTextSize] = useState("normal");
|
||||||
|
const [isUserScrolling, setIsUserScrolling] = useState(false);
|
||||||
const showScrollbar = Appearance.getSettings()?.showScrollbar || false;
|
const showScrollbar = Appearance.getSettings()?.showScrollbar || false;
|
||||||
|
const isStreaming = history[history.length - 1]?.animate;
|
||||||
|
|
||||||
const getTextSizeClass = (size) => {
|
const getTextSizeClass = (size) => {
|
||||||
switch (size) {
|
switch (size) {
|
||||||
@ -58,35 +61,44 @@ export default function ChatHistory({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isAtBottom) scrollToBottom();
|
if (!isUserScrolling && (isAtBottom || isStreaming)) {
|
||||||
}, [history]);
|
scrollToBottom(false); // Use instant scroll for auto-scrolling
|
||||||
|
}
|
||||||
|
}, [history, isAtBottom, isStreaming, isUserScrolling]);
|
||||||
|
|
||||||
|
const handleScroll = (e) => {
|
||||||
|
const { scrollTop, scrollHeight, clientHeight } = e.target;
|
||||||
|
const isBottom = scrollHeight - scrollTop === clientHeight;
|
||||||
|
|
||||||
|
// Detect if this is a user-initiated scroll
|
||||||
|
if (Math.abs(scrollTop - lastScrollTopRef.current) > 10) {
|
||||||
|
setIsUserScrolling(!isBottom);
|
||||||
|
}
|
||||||
|
|
||||||
const handleScroll = () => {
|
|
||||||
const diff =
|
|
||||||
chatHistoryRef.current.scrollHeight -
|
|
||||||
chatHistoryRef.current.scrollTop -
|
|
||||||
chatHistoryRef.current.clientHeight;
|
|
||||||
// Fuzzy margin for what qualifies as "bottom". Stronger than straight comparison since that may change over time.
|
|
||||||
const isBottom = diff <= 10;
|
|
||||||
setIsAtBottom(isBottom);
|
setIsAtBottom(isBottom);
|
||||||
|
lastScrollTopRef.current = scrollTop;
|
||||||
};
|
};
|
||||||
|
|
||||||
const debouncedScroll = debounce(handleScroll, 100);
|
const debouncedScroll = debounce(handleScroll, 100);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function watchScrollEvent() {
|
const chatHistoryElement = chatHistoryRef.current;
|
||||||
if (!chatHistoryRef.current) return null;
|
if (chatHistoryElement) {
|
||||||
const chatHistoryElement = chatHistoryRef.current;
|
|
||||||
if (!chatHistoryElement) return null;
|
|
||||||
chatHistoryElement.addEventListener("scroll", debouncedScroll);
|
chatHistoryElement.addEventListener("scroll", debouncedScroll);
|
||||||
|
return () =>
|
||||||
|
chatHistoryElement.removeEventListener("scroll", debouncedScroll);
|
||||||
}
|
}
|
||||||
watchScrollEvent();
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const scrollToBottom = () => {
|
const scrollToBottom = (smooth = false) => {
|
||||||
if (chatHistoryRef.current) {
|
if (chatHistoryRef.current) {
|
||||||
chatHistoryRef.current.scrollTo({
|
chatHistoryRef.current.scrollTo({
|
||||||
top: chatHistoryRef.current.scrollHeight,
|
top: chatHistoryRef.current.scrollHeight,
|
||||||
behavior: "smooth",
|
|
||||||
|
// Smooth is on when user clicks the button but disabled during auto scroll
|
||||||
|
// We must disable this during auto scroll because it causes issues with
|
||||||
|
// detecting when we are at the bottom of the chat.
|
||||||
|
...(smooth ? { behavior: "smooth" } : {}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -197,6 +209,7 @@ export default function ChatHistory({
|
|||||||
}`}
|
}`}
|
||||||
id="chat-history"
|
id="chat-history"
|
||||||
ref={chatHistoryRef}
|
ref={chatHistoryRef}
|
||||||
|
onScroll={handleScroll}
|
||||||
>
|
>
|
||||||
{history.map((props, index) => {
|
{history.map((props, index) => {
|
||||||
const isLastBotReply =
|
const isLastBotReply =
|
||||||
@ -251,12 +264,14 @@ export default function ChatHistory({
|
|||||||
{!isAtBottom && (
|
{!isAtBottom && (
|
||||||
<div className="fixed bottom-40 right-10 md:right-20 z-50 cursor-pointer animate-pulse">
|
<div className="fixed bottom-40 right-10 md:right-20 z-50 cursor-pointer animate-pulse">
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<div className="p-1 rounded-full border border-white/10 bg-white/10 hover:bg-white/20 hover:text-white">
|
<div
|
||||||
<ArrowDown
|
className="p-1 rounded-full border border-white/10 bg-white/10 hover:bg-white/20 hover:text-white"
|
||||||
weight="bold"
|
onClick={() => {
|
||||||
className="text-white/60 w-5 h-5"
|
scrollToBottom(true);
|
||||||
onClick={scrollToBottom}
|
setIsUserScrolling(false);
|
||||||
/>
|
}}
|
||||||
|
>
|
||||||
|
<ArrowDown weight="bold" className="text-white/60 w-5 h-5" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user