{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "messenger-testimonial",
  "dependencies": [
    "motion"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "../../packages/blocks/src/testimonial-section/messenger-testimonial.tsx",
      "content": "'use client'\r\n\r\nimport { cn } from '@/lib/utils'\r\nimport React, { useState, useEffect } from 'react'\r\nimport { motion, AnimatePresence } from 'motion/react'\r\n\r\ninterface Message {\r\n  id: number\r\n  sender: string\r\n  avatar: string\r\n  text: string\r\n  side: 'left' | 'right'\r\n  time: string\r\n  reactions: string[]\r\n}\r\n\r\nconst messages: Message[] = [\r\n  {\r\n    id: 1,\r\n    sender: 'Emily Chen',\r\n    avatar:\r\n      'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100&h=100&fit=crop',\r\n    text: 'Just integrated the new grid system. Incredible work!',\r\n    side: 'left',\r\n    time: '2:34 PM',\r\n    reactions: ['👏', '🔥'],\r\n  },\r\n  {\r\n    id: 2,\r\n    sender: 'Team Lab',\r\n    avatar: 'TL',\r\n    text: 'So glad you like it Emily! Let us know if you need any help.',\r\n    side: 'right',\r\n    time: '2:35 PM',\r\n    reactions: ['❤️'],\r\n  },\r\n  {\r\n    id: 3,\r\n    sender: 'Markus T.',\r\n    avatar:\r\n      'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop',\r\n    text: 'The documentation is actually readable for once. Thank you.',\r\n    side: 'left',\r\n    time: '2:37 PM',\r\n    reactions: ['😄', '💯'],\r\n  },\r\n  {\r\n    id: 4,\r\n    sender: 'Emily Chen',\r\n    avatar:\r\n      'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100&h=100&fit=crop',\r\n    text: 'Btw, the dark mode tokens are perfect.',\r\n    side: 'left',\r\n    time: '2:38 PM',\r\n    reactions: ['✨'],\r\n  },\r\n  {\r\n    id: 5,\r\n    sender: 'Sarah K.',\r\n    avatar:\r\n      'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100&h=100&fit=crop',\r\n    text: 'Migration took 10 minutes. Best decision we made this quarter!',\r\n    side: 'left',\r\n    time: '2:40 PM',\r\n    reactions: ['🚀', '💪'],\r\n  },\r\n]\r\n\r\nconst TypingIndicator = () => (\r\n  <div className=\"flex gap-1 px-4 py-3\">\r\n    {[0, 1, 2].map((i) => (\r\n      <motion.div\r\n        key={i}\r\n        className=\"size-2 bg-zinc-400 rounded-full\"\r\n        animate={{ scale: [1, 1.2, 1], opacity: [0.5, 1, 0.5] }}\r\n        transition={{\r\n          duration: 1,\r\n          repeat: Infinity,\r\n          delay: i * 0.2,\r\n          ease: 'easeInOut',\r\n        }}\r\n      />\r\n    ))}\r\n  </div>\r\n)\r\n\r\nexport const MessengerTestimonials: React.FC = () => {\r\n  const [visibleMessages, setVisibleMessages] = useState<number[]>([])\r\n  const [showTyping, setShowTyping] = useState(false)\r\n  const [activeReactions, setActiveReactions] = useState<Set<number>>(new Set())\r\n\r\n  useEffect(() => {\r\n    const showMessages = async () => {\r\n      for (let i = 0; i < messages.length; i++) {\r\n        // Show typing indicator\r\n        setShowTyping(true)\r\n        await new Promise((resolve) => setTimeout(resolve, 800))\r\n\r\n        // Hide typing and show message\r\n        setShowTyping(false)\r\n        setVisibleMessages((prev) => [...prev, i])\r\n\r\n        // Wait before next message\r\n        await new Promise((resolve) => setTimeout(resolve, 1200))\r\n      }\r\n    }\r\n\r\n    showMessages()\r\n  }, [])\r\n\r\n  const toggleReaction = (messageId: number) => {\r\n    setActiveReactions((prev) => {\r\n      const newSet = new Set(prev)\r\n      if (newSet.has(messageId)) {\r\n        newSet.delete(messageId)\r\n      } else {\r\n        newSet.add(messageId)\r\n      }\r\n      return newSet\r\n    })\r\n  }\r\n\r\n  return (\r\n    <section className=\"bg-zinc-100 min-h-screen px-4 font-manrope py-10\">\r\n      {/* Header */}\r\n      <div className=\"max-w-xl mx-auto text-center\">\r\n        <h2 className=\"text-4xl font-bold tracking-tight text-zinc-900 mb-4\">\r\n          Live Customer Conversations\r\n        </h2>\r\n        <p className=\"text-zinc-600 \">\r\n          Watch real-time interactions between our support team and customers.\r\n          Experience the responsive, helpful communication that sets us apart.\r\n        </p>\r\n      </div>\r\n\r\n      {/* chat interface */}\r\n      <div className=\"max-w-xl mx-auto flex flex-col border-x border-neutral-200\">\r\n        {/* Header */}\r\n        <div className=\"text-center py-5 pb-10 border-b border-neutral-200\">\r\n          <motion.div\r\n            initial={{ opacity: 0, y: -10 }}\r\n            animate={{ opacity: 1, y: 0 }}\r\n            transition={{ duration: 0.5 }}\r\n            className=\"inline-flex items-center gap-2 font-manrope bg-white border border-zinc-200 rounded-full px-4 py-2\"\r\n          >\r\n            <div className=\"size-2 bg-green-500 rounded-full animate-pulse\" />\r\n            <span className=\"text-xs font-black uppercase tracking-[0.2em] text-zinc-600\">\r\n              Live Customer Conversations\r\n            </span>\r\n          </motion.div>\r\n        </div>\r\n\r\n        {/* Messages */}\r\n        <div className=\"flex flex-col gap-3 p-5 border-b border-neutral-200\">\r\n          <AnimatePresence>\r\n            {visibleMessages.map((index) => {\r\n              const msg = messages[index]\r\n              const showReactions = activeReactions.has(msg?.id || 0)\r\n\r\n              return (\r\n                <motion.div\r\n                  key={msg?.id}\r\n                  initial={{ opacity: 0, y: 20, scale: 0.95 }}\r\n                  animate={{ opacity: 1, y: 0, scale: 1 }}\r\n                  transition={{ duration: 0.4, ease: [0.32, 0.72, 0, 1] }}\r\n                  className={cn(\r\n                    'flex gap-2 max-w-[85%]',\r\n                    msg?.side === 'right'\r\n                      ? 'ml-auto flex-row-reverse'\r\n                      : 'mr-auto'\r\n                  )}\r\n                >\r\n                  {/* Avatar */}\r\n                  <motion.div\r\n                    initial={{ scale: 0 }}\r\n                    animate={{ scale: 1 }}\r\n                    transition={{\r\n                      delay: 0.2,\r\n                      type: 'spring',\r\n                      stiffness: 500,\r\n                      damping: 25,\r\n                    }}\r\n                    className={cn(\r\n                      'size-8 rounded-full flex items-center justify-center text-xs font-bold shrink-0 overflow-hidden',\r\n                      msg?.side === 'right'\r\n                        ? 'bg-zinc-900 text-white'\r\n                        : 'bg-zinc-200'\r\n                    )}\r\n                  >\r\n                    {msg?.avatar.startsWith('http') ? (\r\n                      <img\r\n                        src={msg.avatar}\r\n                        alt={msg.sender}\r\n                        className=\"size-full object-cover\"\r\n                      />\r\n                    ) : (\r\n                      <span\r\n                        className={\r\n                          msg?.side === 'right' ? 'text-white' : 'text-zinc-900'\r\n                        }\r\n                      >\r\n                        {msg?.avatar}\r\n                      </span>\r\n                    )}\r\n                  </motion.div>\r\n\r\n                  {/* Message Content */}\r\n                  <div\r\n                    className={cn(\r\n                      'flex flex-col gap-1',\r\n                      msg?.side === 'right' ? 'items-end' : 'items-start'\r\n                    )}\r\n                  >\r\n                    {/* Sender Name */}\r\n                    <span className=\"text-xs text-zinc-500 font-medium px-1\">\r\n                      {msg?.sender}\r\n                    </span>\r\n\r\n                    {/* Message Bubble */}\r\n                    <div className=\"relative group\">\r\n                      <motion.div\r\n                        whileHover={{ scale: 1.02 }}\r\n                        className={cn(\r\n                          'px-4 py-2.5 rounded-2xl text-sm -sm cursor-pointer transition-all',\r\n                          msg?.side === 'right'\r\n                            ? 'bg-zinc-900 text-white rounded-tr-sm'\r\n                            : 'bg-white border border-zinc-200 text-zinc-900 rounded-tl-sm hover:border-zinc-300'\r\n                        )}\r\n                        onClick={() => toggleReaction(msg?.id || 0)}\r\n                      >\r\n                        {msg?.text}\r\n                      </motion.div>\r\n\r\n                      {/* Reactions */}\r\n                      <AnimatePresence>\r\n                        {showReactions && (\r\n                          <motion.div\r\n                            initial={{ opacity: 0, scale: 0.8, y: 5 }}\r\n                            animate={{ opacity: 1, scale: 1, y: 0 }}\r\n                            exit={{ opacity: 0, scale: 0.8, y: 5 }}\r\n                            transition={{ duration: 0.2 }}\r\n                            className={cn(\r\n                              'absolute -bottom-3 flex gap-0.5 bg-white border border-zinc-200 rounded-full px-2 py-0.5 -md',\r\n                              msg?.side === 'right' ? 'right-0' : 'left-0'\r\n                            )}\r\n                          >\r\n                            {msg?.reactions.map((reaction, idx) => (\r\n                              <motion.span\r\n                                key={idx}\r\n                                initial={{ scale: 0 }}\r\n                                animate={{ scale: 1 }}\r\n                                transition={{\r\n                                  delay: idx * 0.05,\r\n                                  type: 'spring',\r\n                                  stiffness: 500,\r\n                                }}\r\n                                className=\"text-xs\"\r\n                              >\r\n                                {reaction}\r\n                              </motion.span>\r\n                            ))}\r\n                          </motion.div>\r\n                        )}\r\n                      </AnimatePresence>\r\n                    </div>\r\n\r\n                    {/* Timestamp */}\r\n                    <span className=\"text-[9px] text-zinc-400 font-medium px-1\">\r\n                      {msg?.time}\r\n                    </span>\r\n                  </div>\r\n                </motion.div>\r\n              )\r\n            })}\r\n          </AnimatePresence>\r\n\r\n          {/* Typing Indicator */}\r\n          <AnimatePresence>\r\n            {showTyping && (\r\n              <motion.div\r\n                initial={{ opacity: 0, y: 20 }}\r\n                animate={{ opacity: 1, y: 0 }}\r\n                exit={{ opacity: 0, y: -10 }}\r\n                transition={{ duration: 0.3 }}\r\n                className=\"flex gap-2 max-w-[85%] mr-auto\"\r\n              >\r\n                <div className=\"size-8 rounded-full overflow-hidden shrink-0\">\r\n                  <img\r\n                    src=\"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop\"\r\n                    alt=\"User typing\"\r\n                    className=\"size-full object-cover\"\r\n                  />\r\n                </div>\r\n                <div className=\"bg-white border border-zinc-200 rounded-2xl rounded-tl-sm -sm\">\r\n                  <TypingIndicator />\r\n                </div>\r\n              </motion.div>\r\n            )}\r\n          </AnimatePresence>\r\n        </div>\r\n\r\n        {/* Footer Stats */}\r\n        <motion.div\r\n          initial={{ opacity: 0, y: 20 }}\r\n          animate={{ opacity: 1, y: 0 }}\r\n          transition={{ delay: 0.5, duration: 0.5 }}\r\n          className=\"border-b bg-neutral-200 border-neutral-200 p-5 flex items-center justify-between backdrop-blur-sm\"\r\n        >\r\n          <div className=\"flex items-center gap-3\">\r\n            <div className=\"relative\">\r\n              <div className=\"size-2 bg-green-500 rounded-full animate-pulse\" />\r\n              <div className=\"absolute inset-0 size-2 bg-green-500 rounded-full animate-ping opacity-75\" />\r\n            </div>\r\n            <div className=\"flex flex-col\">\r\n              <p className=\"text-xs font-bold font-manrope text-zinc-900\">\r\n                42 customers online\r\n              </p>\r\n              <p className=\"text-xs text-zinc-500\">Average response: 2 min</p>\r\n            </div>\r\n          </div>\r\n          <motion.button\r\n            whileHover={{ scale: 1.05 }}\r\n            whileTap={{ scale: 0.95 }}\r\n            className=\"text-xs font-semibold uppercase tracking-widest text-zinc-900 bg-white border border-zinc-300 px-4 py-2 rounded-lg transition-all\"\r\n          >\r\n            View Feed →\r\n          </motion.button>\r\n        </motion.div>\r\n\r\n        <motion.div\r\n          initial={{ opacity: 0 }}\r\n          animate={{ opacity: 1 }}\r\n          transition={{ delay: 1, duration: 0.5 }}\r\n          className=\"grid grid-cols-3 gap-2 p-5\"\r\n        >\r\n          {[\r\n            { label: 'Response Rate', value: '98%' },\r\n            { label: 'Avg. Rating', value: '4.9★' },\r\n            { label: 'Active Users', value: '2.4k' },\r\n          ].map((stat, idx) => (\r\n            <motion.div\r\n              key={idx}\r\n              initial={{ opacity: 0, y: 10 }}\r\n              animate={{ opacity: 1, y: 0 }}\r\n              transition={{ delay: 1 + idx * 0.1 }}\r\n              className=\"bg-white border border-neutral-200 rounded-xl p-3 text-center\"\r\n            >\r\n              <p className=\"text-lg font-bold text-zinc-900 font-manrope\">\r\n                {stat.value}\r\n              </p>\r\n              <p className=\"text-xs text-zinc-500 uppercase tracking-wider\">\r\n                {stat.label}\r\n              </p>\r\n            </motion.div>\r\n          ))}\r\n        </motion.div>\r\n      </div>\r\n    </section>\r\n  )\r\n}\r\n",
      "type": "registry:block"
    },
    {
      "path": "./components/ui/timeline-animation.tsx",
      "content": "import type { Variants } from 'motion/react';\nimport { type HTMLMotionProps, motion, useInView } from 'motion/react';\nimport type React from 'react';\n\ntype TimelineContentProps<T extends keyof HTMLElementTagNameMap> = {\n  children?: React.ReactNode;\n  animationNum: number;\n  className?: string;\n  timelineRef: React.RefObject<HTMLElement | null>;\n  as?: T;\n  customVariants?: Variants;\n  once?: boolean;\n} & HTMLMotionProps<T>;\n\nexport const TimelineAnimation = <T extends keyof HTMLElementTagNameMap = 'div'>({\n  children,\n  animationNum,\n  timelineRef,\n  className,\n  as,\n  customVariants,\n  once = true,\n  ...props\n}: TimelineContentProps<T>) => {\n  const defaultSequenceVariants = {\n    visible: (i: number) => ({\n      filter: 'blur(0px)',\n      y: 0,\n      opacity: 1,\n      transition: {\n        delay: i * 0.5,\n        duration: 0.5,\n      },\n    }),\n    hidden: {\n      filter: 'blur(20px)',\n      y: 0,\n      opacity: 0,\n    },\n  };\n\n  const sequenceVariants = customVariants || defaultSequenceVariants;\n\n  const isInView = useInView(timelineRef, {\n    once,\n  });\n\n  const MotionComponent = motion[as || 'div'] as React.ElementType;\n\n  return (\n    <MotionComponent\n      initial='hidden'\n      animate={isInView ? 'visible' : 'hidden'}\n      custom={animationNum}\n      variants={sequenceVariants}\n      className={className}\n      {...props}\n    >\n      {children}\n    </MotionComponent>\n  );\n};\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:block"
}