{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "waveform-scrub",
  "type": "registry:component",
  "title": "Waveform Scrub",
  "description": "Interactive waveform scrubber enabling precise audio navigation and playback control.",
  "dependencies": [
    "framer-motion",
    "next-themes",
    "react-icons"
  ],
  "files": [
    {
      "path": "components/watermelon/waveform-scrub.tsx",
      "type": "registry:component",
      "content": "\"use client\";\n\nimport React, { useState, useEffect, useRef } from 'react';\nimport {\n  motion,\n  useMotionValue,\n  useTransform,\n  useMotionValueEvent,\n  AnimatePresence,\n} from 'framer-motion';\nimport {\n  TbPlayerPauseFilled,\n  TbPlayerPlayFilled,\n  TbRotateClockwise2,\n} from 'react-icons/tb';\nimport { useTheme } from 'next-themes';\n\ninterface WaveformScrubProps {\n  duration?: number;\n  fileName?: string;\n  waveformHeights?: number[];\n}\n\nconst DEFAULT_WAVEFORM = [\n  4, 7, 9, 6, 11, 14, 12, 8, 5, 10, 15, 13, 11, 9, 6, 10, 12, 9, 7, 5, 8, 12,\n  10, 7, 6, 9, 13, 11, 8, 6, 5, 11, 8, 6, 5, 11, 8, 6, 5, 8, 5, 10, 15, 13, 11,\n  9,\n];\n\nexport const WaveformScrub: React.FC<WaveformScrubProps> = ({\n  duration = 30,\n  fileName = 'Mom.mp3',\n  waveformHeights = DEFAULT_WAVEFORM,\n}) => {\n  const [currentTime, setCurrentTime] = useState(0);\n  const [isPlaying, setIsPlaying] = useState(false);\n  const { theme } = useTheme();\n  const [containerWidth, setContainerWidth] = useState(0);\n\n  const waveformRef = useRef<HTMLDivElement>(null);\n  const x = useMotionValue(0);\n  const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);\n  const isDark = theme === 'dark';\n\n  const isFinished = currentTime >= duration;\n\n  useEffect(() => {\n    const updateWidth = () => {\n      if (waveformRef.current) {\n        const newWidth = waveformRef.current.offsetWidth;\n        setContainerWidth(newWidth);\n        x.set((currentTime / duration) * newWidth);\n      }\n    };\n\n    updateWidth();\n    window.addEventListener('resize', updateWidth);\n    return () => window.removeEventListener('resize', updateWidth);\n  }, [duration, currentTime, x]);\n\n  useEffect(() => {\n    if (isPlaying && currentTime < duration) {\n      timerRef.current = setInterval(() => {\n        setCurrentTime((prev) => {\n          const next = Math.min(prev + 0.1, duration);\n          x.set((next / duration) * containerWidth);\n          if (next >= duration) setIsPlaying(false);\n          return next;\n        });\n      }, 100);\n    } else {\n      if (timerRef.current) clearInterval(timerRef.current);\n    }\n    return () => {\n      if (timerRef.current) clearInterval(timerRef.current);\n    };\n  }, [isPlaying, duration, x, containerWidth, currentTime]);\n\n  useMotionValueEvent(x, 'change', (latest) => {\n    if (!isPlaying && containerWidth > 0) {\n      const progress = latest / containerWidth;\n      setCurrentTime(progress * duration);\n    }\n  });\n\n  const activeProgress = useTransform(\n    x,\n    [0, containerWidth || 1],\n    ['0%', '100%'],\n  );\n  const displayTime = Math.round(duration - currentTime);\n\n  const handleTogglePlay = () => {\n    if (isFinished) {\n      setCurrentTime(0);\n      x.set(0);\n      setIsPlaying(true);\n    } else {\n      setIsPlaying(!isPlaying);\n    }\n  };\n\n  return (\n    <div className=\"w-full px-4\">\n      <div className=\"flex min-h-full flex-col items-center justify-center bg-transparent py-10 font-sans antialiased\">\n        <div className=\"w-full max-w-110 rounded-[24px] bg-neutral-100 px-2 pt-4 pb-3 shadow-sm transition-colors duration-300 dark:bg-neutral-900\">\n          <div className=\"mb-4 flex items-center justify-between px-2 pr-4\">\n            <div className=\"flex items-center gap-2 overflow-hidden\">\n              <AnimatePresence mode=\"popLayout\" initial={false}>\n                <motion.button\n                  key={isFinished ? 'reset' : isPlaying ? 'pause' : 'play'}\n                  initial={{ opacity: 0, scale: 0.25, filter: 'blur(4px)' }}\n                  animate={{ opacity: 1, scale: 1, filter: 'blur(0px)' }}\n                  exit={{ opacity: 0, scale: 0.25, filter: 'blur(4px)' }}\n                  transition={{\n                    type: 'spring',\n                    duration: 0.3,\n                    bounce: 0,\n                  }}\n                  onClick={handleTogglePlay}\n                  className=\"shrink-0 cursor-pointer text-neutral-500 dark:text-neutral-100 z-20\"\n                >\n                  {isFinished ? (\n                    <TbRotateClockwise2 size={22} />\n                  ) : isPlaying ? (\n                    <TbPlayerPauseFilled size={22} />\n                  ) : (\n                    <TbPlayerPlayFilled size={22} />\n                  )}\n                </motion.button>\n              </AnimatePresence>\n              <span className=\"truncate text-[17px] font-normal tracking-tight text-neutral-500 transition-colors sm:text-[19px] dark:text-neutral-100\">\n                {fileName}\n              </span>\n            </div>\n            <span className=\"shrink-0 text-[18px] font-semibold text-neutral-500 tabular-nums transition-colors sm:text-[20px] dark:text-neutral-100\">\n              {displayTime}s\n            </span>\n          </div>\n\n          <div className=\"relative flex h-17 items-center justify-center rounded-3xl border-[1.6px] border-[#fefefe]/70 bg-[#fefefe] shadow-[inset_0_1px_4px_rgba(0,0,0,0.02)] dark:border-[#0A0A0A]/70 dark:bg-neutral-800\">\n            <motion.div\n              style={{\n                width: activeProgress,\n                backgroundImage: `linear-gradient(-45deg, ${isDark ? '#FFF' : '#000'} 25%, transparent 25%, transparent 50%, ${isDark ? '#FFF' : '#000'} 50%, ${isDark ? '#FFF' : '#000'} 75%, transparent 75%, transparent)`,\n                backgroundSize: '4px 4px',\n              }}\n              animate={{\n                backgroundPositionX: ['0px', '4px'],\n              }}\n              transition={{\n                repeat: Infinity,\n                duration: 0.5,\n                ease: 'linear',\n              }}\n              className=\"pointer-events-none absolute inset-y-0 left-0 rounded-l-3xl opacity-[0.04] transition-opacity dark:opacity-[0.1]\"\n            />\n\n            <div ref={waveformRef} className=\"relative mx-2 h-7 w-full\">\n              <div className=\"absolute inset-0 flex w-full items-center justify-between\">\n                {waveformHeights.map((h, i) => (\n                  <div\n                    key={i}\n                    className=\"w-1 shrink-0 rounded-full bg-neutral-200 transition-colors sm:w-0.75 dark:bg-neutral-400\"\n                    style={{ height: h * 1.6 }}\n                  />\n                ))}\n              </div>\n\n              <motion.div\n                style={{ width: activeProgress }}\n                className=\"pointer-events-none absolute inset-y-0 left-0 z-10 overflow-hidden\"\n              >\n                <div\n                  className=\"flex h-full items-center justify-between\"\n                  style={{ width: containerWidth }}\n                >\n                  {waveformHeights.map((h, i) => (\n                    <div\n                      key={i}\n                      className=\"w-1 shrink-0 rounded-full bg-neutral-800 transition-colors sm:w-0.75 dark:bg-neutral-100\"\n                      style={{ height: h * 1.6 }}\n                    />\n                  ))}\n                </div>\n              </motion.div>\n\n              <motion.div\n                drag=\"x\"\n                dragConstraints={{ left: 0, right: containerWidth }}\n                dragElastic={0}\n                dragMomentum={false}\n                onDragStart={() => setIsPlaying(false)}\n                style={{ x, left: -10 }}\n                className=\"absolute top-full z-10 flex h-40 -translate-y-[85%] cursor-grab flex-col items-center active:cursor-grabbing\"\n              >\n                <div\n                  className=\"h-4.5 w-5.5 bg-[#1C1C1E] shadow-[0_4px_20px_rgba(0,0,0,0.3)] transition-colors dark:bg-white\"\n                  style={{\n                    clipPath: `polygon(15% 0%, 85% 0%, 100% 20%, 100% 60%, 60% 100%, 40% 100%, 0% 60%, 0% 20%)`,\n                  }}\n                />\n                <div className=\"w-1 flex-1 rounded-b-full bg-[#1C1C1E] shadow-md transition-colors dark:bg-white\" />\n              </motion.div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n};\n"
    }
  ]
}
