From c214a4c0b14413002a9898e0f80cf1944da1b5c7 Mon Sep 17 00:00:00 2001 From: Bereket Engida Date: Fri, 10 Oct 2025 19:01:26 -0700 Subject: [PATCH] docs: ai chatbox improvements --- docs/components/floating-ai-search.tsx | 115 ++++++++++++++++++------- 1 file changed, 85 insertions(+), 30 deletions(-) diff --git a/docs/components/floating-ai-search.tsx b/docs/components/floating-ai-search.tsx index 47535de1..a59320da 100644 --- a/docs/components/floating-ai-search.tsx +++ b/docs/components/floating-ai-search.tsx @@ -32,8 +32,8 @@ function useChatContext() { } function SearchAIActions() { - const { messages, status, setMessages, regenerate } = useChatContext(); - const isLoading = status === "streaming"; + const { messages, status, setMessages, stop } = useChatContext(); + const isGenerating = status === "streaming" || status === "submitted"; if (messages.length === 0) return null; @@ -61,22 +61,22 @@ function SearchAIActions() { buttonVariants({ color: "secondary", size: "sm", - className: "rounded-full", + className: "rounded-none", }), )} - onClick={() => setMessages([])} + onClick={isGenerating ? stop : () => setMessages([])} > - Clear Chat + {isGenerating ? "Cancel" : "Clear Chat"} ); } const suggestions = [ - "How do I set up authentication with Better Auth?", - "How to integrate Better Auth with NextJs?", - "How to add two-factor authentication?", - "How to setup SSO with Google?", + "How to configure Sqlite database?", + "How to require email verification?", + "How to change session expiry?", + "How to share cookies across subdomains?", ]; function SearchAIInput(props: ComponentProps<"form">) { @@ -92,7 +92,6 @@ function SearchAIInput(props: ComponentProps<"form">) { }; const handleSuggestionClick = (suggestion: string) => { - setInput(suggestion); void sendMessage({ text: suggestion }); }; @@ -101,7 +100,7 @@ function SearchAIInput(props: ComponentProps<"form">) { }, [isLoading]); return ( -
+
) { > ) {
{showSuggestions && ( -
+

Try asking:

@@ -175,8 +174,27 @@ function SearchAIInput(props: ComponentProps<"form">) { ); } -function List(props: Omit, "dir">) { +function List( + props: Omit, "dir"> & { messageCount: number }, +) { const containerRef = useRef(null); + const isUserScrollingRef = useRef(false); + const prevMessageCountRef = useRef(props.messageCount); + + // Scroll to bottom when new message is submitted + useEffect(() => { + if (props.messageCount > prevMessageCountRef.current) { + // New message submitted, reset scroll lock and scroll to bottom + isUserScrollingRef.current = false; + if (containerRef.current) { + containerRef.current.scrollTo({ + top: containerRef.current.scrollHeight, + behavior: "smooth", + }); + } + } + prevMessageCountRef.current = props.messageCount; + }, [props.messageCount]); useEffect(() => { if (!containerRef.current) return; @@ -184,10 +202,13 @@ function List(props: Omit, "dir">) { const container = containerRef.current; if (!container) return; - container.scrollTo({ - top: container.scrollHeight, - behavior: "instant", - }); + // Only auto-scroll if user hasn't manually scrolled up + if (!isUserScrollingRef.current) { + container.scrollTo({ + top: container.scrollHeight, + behavior: "instant", + }); + } } const observer = new ResizeObserver(callback); @@ -204,6 +225,23 @@ function List(props: Omit, "dir">) { }; }, []); + // Track when user manually scrolls + useEffect(() => { + const container = containerRef.current; + if (!container) return; + + const handleScroll = () => { + const { scrollTop, scrollHeight, clientHeight } = container; + const isNearBottom = scrollHeight - scrollTop - clientHeight < 50; + + // If user is near bottom, enable auto-scroll, otherwise disable it + isUserScrollingRef.current = !isNearBottom; + }; + + container.addEventListener("scroll", handleScroll); + return () => container.removeEventListener("scroll", handleScroll); + }, []); + return (
) { const roleName: Record = { user: "you", - assistant: "better-auth bot", + assistant: "BA bot", }; +function ThinkingIndicator() { + return ( +
+

+ BA bot +

+
+ Thinking +
+ + + +
+
+
+ ); +} + function Message({ message, ...props @@ -253,10 +309,7 @@ function Message({ for (const part of message.parts ?? []) { if (part.type === "text") { - const textWithCitations = part.text.replace( - /\((\d+)\)/g, - '
 ($1) 
', - ); + const textWithCitations = part.text.replace(/\((\d+)\)/g, ""); markdown += textWithCitations; continue; } @@ -351,7 +404,7 @@ export function AISearchTrigger() { } }} > -
+
( ))} + {chat.status === "submitted" && }