Files
Alfred/src/web/consoles/workbench/components/chat/use-chat-scroll.ts

56 lines
1.5 KiB
TypeScript

import type { UIMessage } from "ai";
import { useCallback, useEffect, useState } from "react";
const NEAR_BOTTOM_THRESHOLD = 80;
interface UseChatScrollOptions {
messages: UIMessage[];
scrollRef: React.RefObject<HTMLDivElement | null>;
status: string;
}
export function useChatScroll({ messages, scrollRef, status }: UseChatScrollOptions) {
const [isAtBottom, setIsAtBottom] = useState(true);
const isStreaming = status === "streaming";
const checkNearBottom = useCallback(() => {
const el = scrollRef.current;
if (!el) return true;
return el.scrollHeight - el.scrollTop - el.clientHeight < NEAR_BOTTOM_THRESHOLD;
}, [scrollRef]);
useEffect(() => {
const el = scrollRef.current;
if (!el) return;
setIsAtBottom(checkNearBottom());
const handleScroll = () => {
setIsAtBottom(checkNearBottom());
};
el.addEventListener("scroll", handleScroll, { passive: true });
return () => el.removeEventListener("scroll", handleScroll);
}, [scrollRef, checkNearBottom]);
useEffect(() => {
const el = scrollRef.current;
if (!el || !isAtBottom) return;
el.scrollTo({
behavior: isStreaming ? "instant" : "smooth",
top: el.scrollHeight,
});
}, [messages, isStreaming, isAtBottom, scrollRef]);
const scrollToBottom = useCallback(() => {
const el = scrollRef.current;
if (!el) return;
el.scrollTo({ behavior: "smooth", top: el.scrollHeight });
setIsAtBottom(true);
}, [scrollRef]);
return { isAtBottom, scrollToBottom };
}