mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-14 05:24:24 +01:00
feat: stop costbox from displaying during responses and make scroll to bottom ux more consistent
This commit is contained in:
parent
794fe6fd11
commit
ba8c09b361
4 changed files with 70 additions and 14 deletions
|
|
@ -38,7 +38,7 @@ function ChatView({ index = 0 }: { index?: number }) {
|
|||
|
||||
const fileMap = useFileMapContext();
|
||||
|
||||
const [showCostBar, setShowCostBar] = useState(true);
|
||||
const [showCostBar, setShowCostBar] = useState(false);
|
||||
const lastScrollY = useRef(0);
|
||||
|
||||
const { data: messagesTree = null, isLoading } = useGetMessagesByConvoId(conversationId ?? '', {
|
||||
|
|
@ -65,23 +65,33 @@ function ChatView({ index = 0 }: { index?: number }) {
|
|||
useSSE(rootSubmission, chatHelpers, false);
|
||||
useSSE(addedSubmission, addedChatHelpers, true);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = (event: Event) => {
|
||||
const target = event.target as HTMLElement;
|
||||
const currentScrollY = target.scrollTop;
|
||||
const scrollHeight = target.scrollHeight;
|
||||
const clientHeight = target.clientHeight;
|
||||
const checkIfAtBottom = useCallback(
|
||||
(container: HTMLElement) => {
|
||||
const currentScrollY = container.scrollTop;
|
||||
const scrollHeight = container.scrollHeight;
|
||||
const clientHeight = container.clientHeight;
|
||||
|
||||
const distanceFromBottom = scrollHeight - currentScrollY - clientHeight;
|
||||
const isAtBottom = distanceFromBottom < 10;
|
||||
|
||||
setShowCostBar(isAtBottom);
|
||||
const isStreaming = chatHelpers.isSubmitting || addedChatHelpers.isSubmitting;
|
||||
setShowCostBar(isAtBottom && !isStreaming);
|
||||
lastScrollY.current = currentScrollY;
|
||||
},
|
||||
[chatHelpers.isSubmitting, addedChatHelpers.isSubmitting],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = (event: Event) => {
|
||||
const target = event.target as HTMLElement;
|
||||
checkIfAtBottom(target);
|
||||
};
|
||||
|
||||
const findAndAttachScrollListener = () => {
|
||||
const messagesContainer = document.querySelector('[class*="scrollbar-gutter-stable"]');
|
||||
if (messagesContainer) {
|
||||
checkIfAtBottom(messagesContainer as HTMLElement);
|
||||
|
||||
messagesContainer.addEventListener('scroll', handleScroll, { passive: true });
|
||||
return () => {
|
||||
messagesContainer.removeEventListener('scroll', handleScroll);
|
||||
|
|
@ -93,7 +103,19 @@ function ChatView({ index = 0 }: { index?: number }) {
|
|||
const cleanup = findAndAttachScrollListener();
|
||||
|
||||
return cleanup;
|
||||
}, [messagesTree]);
|
||||
}, [messagesTree, checkIfAtBottom]);
|
||||
|
||||
useEffect(() => {
|
||||
const isStreaming = chatHelpers.isSubmitting || addedChatHelpers.isSubmitting;
|
||||
if (isStreaming) {
|
||||
setShowCostBar(false);
|
||||
} else {
|
||||
const messagesContainer = document.querySelector('[class*="scrollbar-gutter-stable"]');
|
||||
if (messagesContainer) {
|
||||
checkIfAtBottom(messagesContainer as HTMLElement);
|
||||
}
|
||||
}
|
||||
}, [chatHelpers.isSubmitting, addedChatHelpers.isSubmitting, checkIfAtBottom]);
|
||||
|
||||
const methods = useForm<ChatFormValues>({
|
||||
defaultValues: { text: '' },
|
||||
|
|
@ -110,6 +132,7 @@ function ChatView({ index = 0 }: { index?: number }) {
|
|||
} else if ((isLoading || isNavigating) && !isLandingPage) {
|
||||
content = <LoadingSpinner />;
|
||||
} else if (!isLandingPage) {
|
||||
const isStreaming = chatHelpers.isSubmitting || addedChatHelpers.isSubmitting;
|
||||
content = (
|
||||
<MessagesView
|
||||
messagesTree={messagesTree}
|
||||
|
|
@ -117,7 +140,10 @@ function ChatView({ index = 0 }: { index?: number }) {
|
|||
!isLandingPage &&
|
||||
conversationCosts &&
|
||||
conversationCosts.totals && (
|
||||
<CostBar conversationCosts={conversationCosts} showCostBar={showCostBar} />
|
||||
<CostBar
|
||||
conversationCosts={conversationCosts}
|
||||
showCostBar={showCostBar && !isStreaming}
|
||||
/>
|
||||
)
|
||||
}
|
||||
costs={conversationCosts}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export default function MessagesView({
|
|||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col pb-9 dark:bg-transparent">
|
||||
<div className="flex flex-col dark:bg-transparent">
|
||||
{(_messagesTree && _messagesTree.length == 0) || _messagesTree === null ? (
|
||||
<div
|
||||
className={cn(
|
||||
|
|
@ -74,7 +74,7 @@ export default function MessagesView({
|
|||
)}
|
||||
<div
|
||||
id="messages-end"
|
||||
className="group h-0 w-full flex-shrink-0"
|
||||
className="group h-1 w-full flex-shrink-0 pb-7"
|
||||
ref={messagesEndRef}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ export default function useMessageScrolling(messagesTree?: TMessage[] | null) {
|
|||
const { conversationId } = conversation ?? {};
|
||||
|
||||
const timeoutIdRef = useRef<NodeJS.Timeout>();
|
||||
const prevIsSubmittingRef = useRef<boolean>(false);
|
||||
|
||||
const debouncedSetShowScrollButton = useCallback((value: boolean) => {
|
||||
clearTimeout(timeoutIdRef.current);
|
||||
|
|
@ -60,7 +61,10 @@ export default function useMessageScrolling(messagesTree?: TMessage[] | null) {
|
|||
}
|
||||
}, [debouncedSetShowScrollButton]);
|
||||
|
||||
const scrollCallback = () => debouncedSetShowScrollButton(false);
|
||||
const scrollCallback = useCallback(
|
||||
() => debouncedSetShowScrollButton(false),
|
||||
[debouncedSetShowScrollButton],
|
||||
);
|
||||
|
||||
const { scrollToRef: scrollToBottom, handleSmoothToRef } = useScrollToRef({
|
||||
targetRef: messagesEndRef,
|
||||
|
|
@ -71,6 +75,18 @@ export default function useMessageScrolling(messagesTree?: TMessage[] | null) {
|
|||
},
|
||||
});
|
||||
|
||||
const smoothScrollToBottom = useCallback(() => {
|
||||
if (messagesEndRef.current) {
|
||||
messagesEndRef.current.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'end',
|
||||
inline: 'nearest',
|
||||
});
|
||||
scrollCallback();
|
||||
setAbortScroll(false);
|
||||
}
|
||||
}, [scrollCallback, setAbortScroll]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!messagesTree || messagesTree.length === 0) {
|
||||
return;
|
||||
|
|
@ -91,6 +107,20 @@ export default function useMessageScrolling(messagesTree?: TMessage[] | null) {
|
|||
};
|
||||
}, [isSubmitting, messagesTree, scrollToBottom, abortScroll]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!messagesEndRef.current || !scrollableRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (prevIsSubmittingRef.current && !isSubmitting && abortScroll !== true) {
|
||||
setTimeout(() => {
|
||||
smoothScrollToBottom();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
prevIsSubmittingRef.current = isSubmitting;
|
||||
}, [isSubmitting, smoothScrollToBottom, abortScroll]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!messagesEndRef.current || !scrollableRef.current) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export default function useScrollToRef({
|
|||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const scrollToRef = useCallback(
|
||||
throttle(() => logAndScroll('instant', callback), 145, { leading: true }),
|
||||
throttle(() => logAndScroll('instant', callback), 100, { leading: true }),
|
||||
[targetRef],
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue