{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "spotify-card",
  "title": "Spotify Card",
  "description": "Display Spotify tracks with album art and blurred background.",
  "dependencies": [
    "@icons-pack/react-simple-icons",
    "spotify-url-info"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/spell-ui/spotify-card.tsx",
      "content": "\"use client\";\n\nimport { useState, useEffect, useRef } from \"react\";\nimport { SiSpotify } from \"@icons-pack/react-simple-icons\";\nimport { cn } from \"@/lib/utils\";\n\ninterface SpotifyData {\n  title: string;\n  artist: string;\n  image: string;\n  link: string;\n  audio?: string;\n}\n\ninterface SpotifyCardProps {\n  url: string;\n  className?: string;\n}\n\nconst SpotifyCardSkeleton = ({ className }: { className?: string }) => (\n  <div\n    className={cn(\n      \"relative flex max-h-[100px] h-full w-full items-stretch justify-center overflow-hidden rounded-2xl border border-border bg-muted/50 p-3\",\n      className\n    )}\n  >\n    <div className=\"aspect-square w-full max-w-[75px] animate-pulse self-center rounded-lg bg-muted\" />\n    <div className=\"z-10 flex w-full flex-col justify-end\">\n      <div className=\"flex flex-col items-end gap-1 pl-6\">\n        <div className=\"h-4 w-24 animate-pulse rounded bg-muted\" />\n        <div className=\"h-4 w-16 animate-pulse rounded bg-muted\" />\n      </div>\n    </div>\n  </div>\n);\n\nconst SpotifyCardError = ({ className }: { className?: string }) => (\n  <div\n    className={cn(\n      \"flex h-[100px] w-full items-center justify-center rounded-2xl border border-border bg-muted/50 p-6 text-muted-foreground\",\n      className\n    )}\n  >\n    <p className=\"text-sm\">Failed to load Spotify data</p>\n  </div>\n);\n\nexport function SpotifyCard({ url, className }: SpotifyCardProps) {\n  const [data, setData] = useState<SpotifyData | null>(null);\n  const [isLoading, setIsLoading] = useState(true);\n  const [error, setError] = useState(false);\n  const [isPlaying, setIsPlaying] = useState(false);\n  const audioRef = useRef<HTMLAudioElement | null>(null);\n\n  useEffect(() => {\n    const fetchSpotifyData = async () => {\n      try {\n        setIsLoading(true);\n        setError(false);\n\n        const response = await fetch(\n          `/api/spotify?url=${encodeURIComponent(url)}`\n        );\n\n        if (!response.ok) {\n          throw new Error(\"Failed to fetch\");\n        }\n\n        const spotifyData = await response.json();\n        setData(spotifyData);\n      } catch {\n        setError(true);\n      } finally {\n        setIsLoading(false);\n      }\n    };\n\n    fetchSpotifyData();\n  }, [url]);\n\n  useEffect(() => {\n    return () => {\n      if (audioRef.current) {\n        audioRef.current.pause();\n      }\n    };\n  }, []);\n\n  const handlePlayPause = () => {\n    if (!data?.audio) return;\n\n    if (!audioRef.current) {\n      audioRef.current = new Audio(data.audio);\n      audioRef.current.volume = 0.3;\n      audioRef.current.addEventListener(\"ended\", () => setIsPlaying(false));\n    }\n\n    if (isPlaying) {\n      audioRef.current.pause();\n      setIsPlaying(false);\n    } else {\n      audioRef.current.play();\n      setIsPlaying(true);\n    }\n  };\n\n  if (isLoading) {\n    return <SpotifyCardSkeleton className={className} />;\n  }\n\n  if (error || !data) {\n    return <SpotifyCardError className={className} />;\n  }\n\n  return (\n    <div\n      className={cn(\n        \"relative flex max-h-[100px] h-full w-full items-stretch justify-center overflow-hidden rounded-2xl border border-border p-3\",\n        className\n      )}\n    >\n      <div className=\"pointer-events-none absolute left-1/2 top-1/2 z-0 block aspect-square w-[120%] -translate-x-1/2 -translate-y-1/2\">\n        <div className=\"pointer-events-none flex h-full select-none opacity-100\">\n          <img\n            src={data.image}\n            alt=\"\"\n            className=\"absolute brightness-150 left-0 top-0 block h-full w-full blur-[50px]\"\n          />\n          <div className=\"absolute left-0 top-0 h-full w-full bg-[linear-gradient(180deg,_rgba(0,_0,_0,_0)_0,_rgba(0,_0,_0,_.8))]\" />\n        </div>\n      </div>\n      <button\n        onClick={data.audio ? handlePlayPause : undefined}\n        className={cn(\n          \"group relative z-[1] w-full max-w-[75px] self-center\",\n          data.audio && \"cursor-pointer\"\n        )}\n      >\n        <img\n          src={data.image}\n          alt={data.title}\n          className={cn(\n            \"pointer-events-none relative z-[1] min-h-[75px] min-w-[75px] w-full select-none rounded-lg object-cover shadow-md transition-transform duration-300 ease-out\",\n            data.audio && \"group-hover:-translate-x-0.5\",\n            isPlaying && \"-translate-x-0.5\"\n          )}\n        />\n        {data.audio && (\n          <div\n            className={cn(\n              \"absolute left-1/2 top-1/2 -z-[1] size-[80%] -translate-y-1/2 transition-all duration-300\",\n              isPlaying ? \"translate-x-[-10%]\" : \"translate-x-[-50%] group-hover:translate-x-[-10%]\"\n            )}\n          >\n            <svg\n              xmlns=\"http://www.w3.org/2000/svg\"\n              viewBox=\"0 0 110 110\"\n              className=\"size-full animate-spin\"\n              style={{\n                animationDuration: \"3s\",\n                animationPlayState: isPlaying ? \"running\" : \"paused\",\n              }}\n            >\n              <circle cx=\"55\" cy=\"55\" r=\"55\" fill=\"#000\" />\n              <mask id=\"mask0_6138_16576\" width=\"110\" height=\"110\" x=\"0\" y=\"0\" maskUnits=\"userSpaceOnUse\" style={{ maskType: \"alpha\" }}>\n                <circle cx=\"55\" cy=\"55\" r=\"55\" fill=\"#000\" />\n              </mask>\n              <g mask=\"url(#mask0_6138_16576)\">\n                <g filter=\"url(#filter0_f_6138_16576)\">\n                  <circle cx=\"55\" cy=\"55\" r=\"51.5\" stroke=\"#fff\" strokeOpacity=\"0.21\" />\n                </g>\n                <g filter=\"url(#filter1_f_6138_16576)\">\n                  <circle cx=\"55\" cy=\"55\" r=\"47.5\" stroke=\"#fff\" strokeOpacity=\"0.21\" />\n                </g>\n                <g filter=\"url(#filter2_f_6138_16576)\">\n                  <circle cx=\"55\" cy=\"55\" r=\"45.5\" stroke=\"#fff\" strokeOpacity=\"0.21\" />\n                </g>\n                <g filter=\"url(#filter3_f_6138_16576)\">\n                  <circle cx=\"55\" cy=\"55\" r=\"43.5\" stroke=\"#fff\" strokeOpacity=\"0.21\" />\n                </g>\n                <g filter=\"url(#filter4_f_6138_16576)\">\n                  <circle cx=\"55\" cy=\"55\" r=\"37.5\" stroke=\"#fff\" strokeOpacity=\"0.21\" />\n                </g>\n                <g filter=\"url(#filter5_f_6138_16576)\">\n                  <circle cx=\"55\" cy=\"55\" r=\"34.5\" stroke=\"#fff\" strokeOpacity=\"0.21\" />\n                </g>\n                <g filter=\"url(#filter6_f_6138_16576)\" opacity=\"0.4\">\n                  <path fill=\"#fff\" d=\"M-14 38l68 19.579L-14 74V38z\" />\n                </g>\n                <g filter=\"url(#filter7_f_6138_16576)\" opacity=\"0.4\">\n                  <path fill=\"#fff\" d=\"M123 38L55 57.579 123 74V38z\" />\n                </g>\n                <g filter=\"url(#filter8_f_6138_16576)\" opacity=\"0.4\">\n                  <path fill=\"#fff\" d=\"M36.5 124.5l19.579-68 16.421 68h-36z\" />\n                </g>\n                <g filter=\"url(#filter9_f_6138_16576)\" opacity=\"0.4\">\n                  <path fill=\"#fff\" d=\"M36.5-12.5l19.579 68 16.421-68h-36z\" />\n                </g>\n              </g>\n              <defs>\n                <filter id=\"filter0_f_6138_16576\" width=\"108\" height=\"108\" x=\"1\" y=\"1\" colorInterpolationFilters=\"sRGB\" filterUnits=\"userSpaceOnUse\">\n                  <feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n                  <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n                  <feGaussianBlur result=\"effect1_foregroundBlur_6138_16576\" stdDeviation=\"1\" />\n                </filter>\n                <filter id=\"filter1_f_6138_16576\" width=\"100\" height=\"100\" x=\"5\" y=\"5\" colorInterpolationFilters=\"sRGB\" filterUnits=\"userSpaceOnUse\">\n                  <feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n                  <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n                  <feGaussianBlur result=\"effect1_foregroundBlur_6138_16576\" stdDeviation=\"1\" />\n                </filter>\n                <filter id=\"filter2_f_6138_16576\" width=\"96\" height=\"96\" x=\"7\" y=\"7\" colorInterpolationFilters=\"sRGB\" filterUnits=\"userSpaceOnUse\">\n                  <feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n                  <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n                  <feGaussianBlur result=\"effect1_foregroundBlur_6138_16576\" stdDeviation=\"1\" />\n                </filter>\n                <filter id=\"filter3_f_6138_16576\" width=\"92\" height=\"92\" x=\"9\" y=\"9\" colorInterpolationFilters=\"sRGB\" filterUnits=\"userSpaceOnUse\">\n                  <feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n                  <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n                  <feGaussianBlur result=\"effect1_foregroundBlur_6138_16576\" stdDeviation=\"1\" />\n                </filter>\n                <filter id=\"filter4_f_6138_16576\" width=\"80\" height=\"80\" x=\"15\" y=\"15\" colorInterpolationFilters=\"sRGB\" filterUnits=\"userSpaceOnUse\">\n                  <feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n                  <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n                  <feGaussianBlur result=\"effect1_foregroundBlur_6138_16576\" stdDeviation=\"1\" />\n                </filter>\n                <filter id=\"filter5_f_6138_16576\" width=\"74\" height=\"74\" x=\"18\" y=\"18\" colorInterpolationFilters=\"sRGB\" filterUnits=\"userSpaceOnUse\">\n                  <feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n                  <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n                  <feGaussianBlur result=\"effect1_foregroundBlur_6138_16576\" stdDeviation=\"1\" />\n                </filter>\n                <filter id=\"filter6_f_6138_16576\" width=\"100\" height=\"68\" x=\"-30\" y=\"22\" colorInterpolationFilters=\"sRGB\" filterUnits=\"userSpaceOnUse\">\n                  <feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n                  <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n                  <feGaussianBlur result=\"effect1_foregroundBlur_6138_16576\" stdDeviation=\"8\" />\n                </filter>\n                <filter id=\"filter7_f_6138_16576\" width=\"100\" height=\"68\" x=\"39\" y=\"22\" colorInterpolationFilters=\"sRGB\" filterUnits=\"userSpaceOnUse\">\n                  <feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n                  <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n                  <feGaussianBlur result=\"effect1_foregroundBlur_6138_16576\" stdDeviation=\"8\" />\n                </filter>\n                <filter id=\"filter8_f_6138_16576\" width=\"68\" height=\"100\" x=\"20.5\" y=\"40.5\" colorInterpolationFilters=\"sRGB\" filterUnits=\"userSpaceOnUse\">\n                  <feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n                  <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n                  <feGaussianBlur result=\"effect1_foregroundBlur_6138_16576\" stdDeviation=\"8\" />\n                </filter>\n                <filter id=\"filter9_f_6138_16576\" width=\"68\" height=\"100\" x=\"20.5\" y=\"-28.5\" colorInterpolationFilters=\"sRGB\" filterUnits=\"userSpaceOnUse\">\n                  <feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n                  <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n                  <feGaussianBlur result=\"effect1_foregroundBlur_6138_16576\" stdDeviation=\"8\" />\n                </filter>\n              </defs>\n            </svg>\n          </div>\n        )}\n      </button>\n      <div className=\"z-10 flex w-full flex-col justify-between\">\n        <div className=\"flex self-end\">\n          <a\n            href={data.link}\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            className=\"cursor-pointer\"\n          >\n            <SiSpotify size={18} className=\"text-[#BAAEBA]\" />\n          </a>\n        </div>\n        <div className=\"pl-6 text-end\">\n          <h2 className=\"whitespace-nowrap text-sm font-semibold tracking-[-.006em] text-[#D6D1D4]\">\n            {data.title}\n          </h2>\n          <h2 className=\"whitespace-nowrap text-sm font-medium tracking-[-.006em] text-[#BAAEBA]\">\n            {data.artist}\n          </h2>\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}