Add chat scroll-to-bottom button
This commit is contained in:
parent
52ec17eee5
commit
cf5563c3ad
2 changed files with 49 additions and 7 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { IconPlus } from "@tabler/icons-react"
|
||||
import { IconArrowDown, IconPlus } from "@tabler/icons-react"
|
||||
import { useAtom } from "jotai"
|
||||
import { type ChangeEvent, useRef, useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
|
@ -178,11 +178,17 @@ export function ChatPage() {
|
|||
const hasStreamingMessage =
|
||||
streamingEnabled &&
|
||||
messages.some((message) => message.role === "assistant" && message.streaming)
|
||||
const { scrollRef, hasScrolled, handleScroll, handleManualScrollIntent } =
|
||||
useChatAutoScroll({
|
||||
deps: [messages, isTyping],
|
||||
streaming: hasStreamingMessage,
|
||||
})
|
||||
const {
|
||||
scrollRef,
|
||||
isAtBottom,
|
||||
hasScrolled,
|
||||
handleScroll,
|
||||
handleManualScrollIntent,
|
||||
scrollToBottom,
|
||||
} = useChatAutoScroll({
|
||||
deps: [messages, isTyping],
|
||||
streaming: hasStreamingMessage,
|
||||
})
|
||||
|
||||
const handleSend = () => {
|
||||
if ((!input.trim() && attachments.length === 0) || !canInput) return
|
||||
|
|
@ -259,7 +265,7 @@ export function ChatPage() {
|
|||
canInput && (Boolean(input.trim()) || attachments.length > 0)
|
||||
|
||||
return (
|
||||
<div className="bg-background/95 flex h-full flex-col">
|
||||
<div className="bg-background/95 relative flex h-full flex-col">
|
||||
<PageHeader
|
||||
title={t("navigation.chat")}
|
||||
className={`transition-shadow ${
|
||||
|
|
@ -374,6 +380,18 @@ export function ChatPage() {
|
|||
onChange={handleImageSelection}
|
||||
/>
|
||||
|
||||
{!isAtBottom && messages.length > 0 && (
|
||||
<Button
|
||||
type="button"
|
||||
size="icon"
|
||||
className="absolute right-6 bottom-28 z-20 rounded-full shadow-lg md:right-8"
|
||||
aria-label="Scroll to bottom"
|
||||
onClick={() => scrollToBottom()}
|
||||
>
|
||||
<IconArrowDown className="size-4" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<ChatComposer
|
||||
input={input}
|
||||
attachments={attachments}
|
||||
|
|
|
|||
|
|
@ -137,6 +137,29 @@ export function useChatAutoScroll({
|
|||
cancelAnimation()
|
||||
}, [cancelAnimation])
|
||||
|
||||
const scrollToBottom = useCallback(
|
||||
(options?: { immediate?: boolean }) => {
|
||||
const element = scrollRef.current
|
||||
if (!element) {
|
||||
return
|
||||
}
|
||||
|
||||
stickyRef.current = true
|
||||
setIsAtBottom(true)
|
||||
targetScrollTopRef.current = getBottomScrollTop(element)
|
||||
|
||||
if (options?.immediate) {
|
||||
cancelAnimation()
|
||||
element.scrollTop = targetScrollTopRef.current
|
||||
syncStateFromElement(element, { programmatic: true })
|
||||
return
|
||||
}
|
||||
|
||||
animateToBottom()
|
||||
},
|
||||
[animateToBottom, cancelAnimation, syncStateFromElement],
|
||||
)
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const element = scrollRef.current
|
||||
if (!element) {
|
||||
|
|
@ -164,5 +187,6 @@ export function useChatAutoScroll({
|
|||
hasScrolled,
|
||||
handleScroll,
|
||||
handleManualScrollIntent,
|
||||
scrollToBottom,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue