{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chat-scroll-button",
  "title": "Chat Scroll Button",
  "description": "A button that appears once you scroll away from the latest message and brings you back.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "https://ui.ndekocode.dev/r/theme.json",
    "https://ui.ndekocode.dev/r/utils.json",
    "https://ui.ndekocode.dev/r/icons.json",
    "https://ui.ndekocode.dev/r/button.json",
    "https://ui.ndekocode.dev/r/chat-container.json"
  ],
  "files": [
    {
      "path": "registry/ndeko/ui/chat-scroll-button.tsx",
      "content": "// Adapted from prompt-kit (MIT, Copyright (c) 2025 Julien Thibeaut).\n'use client';\n\nimport {\n  AnimatePresence,\n  motion,\n  useReducedMotion,\n  type MotionProps,\n  type Transition,\n} from 'motion/react';\n\nimport { ChevronDownIcon } from '@/registry/ndeko/lib/icons';\nimport { cn } from '@/registry/ndeko/lib/utils';\nimport { Button } from '@/registry/ndeko/ui/button';\nimport { useChatContainer } from '@/registry/ndeko/ui/chat-container';\n\ntype PresenceProps = Pick<\n  MotionProps,\n  'initial' | 'animate' | 'exit' | 'transition'\n>;\n\nconst ENTER: Transition = { duration: 0.2, ease: [0.23, 1, 0.32, 1] };\n\nconst EXIT: Transition = { duration: 0.12, ease: [0.23, 1, 0.32, 1] };\n\nconst RISE_PRESENCE: PresenceProps = {\n  initial: { opacity: 0, y: 8, scale: 0.95 },\n  animate: { opacity: 1, y: 0, scale: 1 },\n  exit: { opacity: 0, y: 8, scale: 0.95, transition: EXIT },\n  transition: ENTER,\n};\n\nconst FADE_PRESENCE: PresenceProps = {\n  initial: { opacity: 0 },\n  animate: { opacity: 1 },\n  exit: { opacity: 0, transition: EXIT },\n  transition: ENTER,\n};\n\nfunction presenceOf(shouldReduceMotion: boolean | null) {\n  if (shouldReduceMotion) {\n    return FADE_PRESENCE;\n  }\n  return RISE_PRESENCE;\n}\n\ntype ScrollButtonProps = Omit<\n  React.ComponentProps<typeof Button>,\n  'children' | 'onClick'\n> & {\n  /** Classes of the element that appears and leaves, to place it over the list. */\n  wrapperClassName?: string;\n  label?: string;\n};\n\nfunction ScrollButton({\n  variant = 'outline',\n  size = 'icon',\n  label = 'Scroll to the latest message',\n  wrapperClassName,\n  className,\n  ...props\n}: ScrollButtonProps) {\n  const { isAtBottom, scrollToBottom } = useChatContainer();\n  const shouldReduceMotion = useReducedMotion();\n  const presence = presenceOf(shouldReduceMotion);\n  const isVisible = !isAtBottom;\n\n  function scrollDown() {\n    scrollToBottom();\n  }\n\n  return (\n    <AnimatePresence initial={false}>\n      {isVisible && (\n        <motion.div\n          key='scroll-button'\n          data-slot='scroll-button'\n          className={cn('z-10 inline-flex', wrapperClassName)}\n          {...presence}\n        >\n          <Button\n            type='button'\n            variant={variant}\n            size={size}\n            aria-label={label}\n            onClick={scrollDown}\n            className={cn('shadow-md shadow-black/6', className)}\n            {...props}\n          >\n            <ChevronDownIcon />\n          </Button>\n        </motion.div>\n      )}\n    </AnimatePresence>\n  );\n}\n\nexport { ScrollButton, type ScrollButtonProps };\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "primitives",
    "chat"
  ],
  "type": "registry:ui"
}