"use client"; import * as React from "react"; import { MessageScroller as MessageScrollerPrimitive, useMessageScroller, useMessageScrollerScrollable, useMessageScrollerVisibility, } from "@shadcn/react/message-scroller"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; import { ArrowDownIcon } from "lucide-react"; function MessageScrollerProvider( props: React.ComponentProps, ) { return ; } function MessageScroller({ className, ...props }: React.ComponentProps) { return ( ); } function MessageScrollerViewport({ className, ...props }: React.ComponentProps) { return ( ); } function MessageScrollerContent({ className, ...props }: React.ComponentProps) { return ( ); } function MessageScrollerItem({ className, scrollAnchor = false, ...props }: React.ComponentProps) { return ( ); } function MessageScrollerButton({ direction = "end", className, children, onClick, render: _render, variant = "secondary", size = "icon-sm", behavior = "smooth", tabIndex, type = "button", ...props }: React.ComponentProps & Pick, "variant" | "size">) { void _render; const buttonRef = React.useRef(null); const controls = useMessageScroller(); const isActive = useNativeScrollButtonActive(buttonRef, direction); const handleClick = React.useCallback( (event: React.MouseEvent) => { onClick?.(event); if (event.defaultPrevented) { return; } event.preventDefault(); event.currentTarget.blur(); if (direction === "end") { controls.scrollToEnd({ behavior }); } else { controls.scrollToStart({ behavior }); } const viewport = event.currentTarget .closest('[data-slot="message-scroller"]') ?.querySelector('[data-slot="message-scroller-viewport"]'); if (!viewport) { return; } scrollViewportToEdge(viewport, direction, behavior); window.requestAnimationFrame(() => scrollViewportToEdge(viewport, direction, "auto"), ); }, [behavior, controls, direction, onClick], ); return ( ); } function useNativeScrollButtonActive( buttonRef: React.RefObject, direction: "start" | "end", ) { const [isActive, setIsActive] = React.useState(false); React.useLayoutEffect(() => { const button = buttonRef.current; const viewport = button ?.closest('[data-slot="message-scroller"]') ?.querySelector('[data-slot="message-scroller-viewport"]'); if (!viewport) { setIsActive(false); return; } const scrollViewport = viewport; const content = scrollViewport.querySelector( '[data-slot="message-scroller-content"]', ); let frame: number | null = null; function readActiveState() { if (direction === "start") { return scrollViewport.scrollTop > 8; } return ( scrollViewport.scrollHeight - scrollViewport.scrollTop - scrollViewport.clientHeight > 8 ); } function updateActiveState() { frame = null; setIsActive(readActiveState()); } function scheduleUpdate() { if (frame !== null) { return; } frame = window.requestAnimationFrame(updateActiveState); } updateActiveState(); scrollViewport.addEventListener("scroll", scheduleUpdate, { passive: true, }); window.addEventListener("resize", scheduleUpdate); window.visualViewport?.addEventListener("resize", scheduleUpdate); const resizeObserver = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(scheduleUpdate); resizeObserver?.observe(scrollViewport); if (content) { resizeObserver?.observe(content); } const mutationObserver = content && typeof MutationObserver !== "undefined" ? new MutationObserver(scheduleUpdate) : null; mutationObserver?.observe(content!, { childList: true, subtree: true, }); return () => { if (frame !== null) { window.cancelAnimationFrame(frame); } scrollViewport.removeEventListener("scroll", scheduleUpdate); window.removeEventListener("resize", scheduleUpdate); window.visualViewport?.removeEventListener("resize", scheduleUpdate); resizeObserver?.disconnect(); mutationObserver?.disconnect(); }; }, [buttonRef, direction]); return isActive; } function scrollViewportToEdge( viewport: HTMLElement, direction: "start" | "end", behavior: ScrollBehavior, ) { const top = direction === "end" ? viewport.scrollHeight : 0; if (behavior === "smooth") { viewport.scrollTo({ top, behavior }); return; } viewport.scrollTop = top; } export { MessageScrollerProvider, MessageScroller, MessageScrollerViewport, MessageScrollerContent, MessageScrollerItem, MessageScrollerButton, useMessageScroller, useMessageScrollerScrollable, useMessageScrollerVisibility, };