{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "music-player",
  "title": "Music Player",
  "description": "A music player component featuring essential playback controls and a progress slider.",
  "registryDependencies": [
    "slider",
    "button"
  ],
  "files": [
    {
      "path": "registry/music-player/music-player.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/components/ui/button\";\nimport { Slider } from \"@/components/ui/slider\";\nimport {\n  PauseIcon,\n  RepeatIcon,\n  ShuffleIcon,\n  SkipBackIcon,\n  SkipForwardIcon,\n  MusicIcon,\n  PlayIcon,\n  Repeat1Icon,\n} from \"lucide-react\";\nimport { useMusicPlayer, Song } from \"@/hooks/use-music-player\";\n\nexport default function MusicPlayer({ song }: { song: Song }) {\n  const {\n    isPlaying,\n    duration,\n    progressPercentage,\n    formattedCurrentTime,\n    formattedDuration,\n    isShuffling,\n    repeatMode,\n    togglePlayPause,\n    handleSliderChange,\n    toggleShuffle,\n    toggleRepeat,\n  } = useMusicPlayer({ song });\n\n  return (\n    <div className=\"md:max-w-md w-full flex flex-col gap-4 bg-popover border rounded-lg p-4\">\n      <div className=\"flex items-start md:items-center flex-col md:flex-row gap-4 min-w-0\">\n        {song.album.image ? (\n          <img\n            src={song.album.image}\n            alt={`${song.name} by ${song.artists.join(\", \")}`}\n            className=\"size-full md:size-16 rounded-md object-cover\"\n            aria-hidden=\"true\"\n          />\n        ) : (\n          <div\n            className=\"size-full md:size-16 rounded-md bg-muted flex items-center justify-center\"\n            aria-hidden=\"true\"\n          >\n            <MusicIcon className=\"size-8 text-muted-foreground\" />\n          </div>\n        )}\n        <div className=\"w-full\">\n          <p className=\"text-lg font-semibold truncate\">{song.name}</p>\n          <p className=\"text-sm text-muted-foreground truncate\">\n            {song.artists.join(\", \")}\n          </p>\n          <p className=\"text-sm text-muted-foreground truncate\">\n            {song.album.name}\n          </p>\n        </div>\n      </div>\n      <div className=\"relative\">\n        <Slider\n          value={[progressPercentage]}\n          max={100}\n          step={1}\n          aria-label=\"Music progress slider\"\n          onValueChange={handleSliderChange}\n          disabled={duration === 0}\n        />\n        <div className=\"w-full flex justify-between mt-1.5 text-xs text-muted-foreground\">\n          <span>{formattedCurrentTime}</span>\n          <span>{formattedDuration}</span>\n        </div>\n      </div>\n      <div className=\"flex items-center justify-center -mt-2 -mb-1\">\n        <Button\n          variant=\"ghost\"\n          size=\"icon\"\n          className={cn(\n            \"size-7 mr-1.5\",\n            isShuffling ? \"text-primary\" : \"text-muted-foreground\",\n          )}\n          aria-label={`Shuffle ${isShuffling ? \"on\" : \"off\"}`}\n          onClick={toggleShuffle}\n        >\n          <ShuffleIcon className=\"size-3.5\" />\n        </Button>\n        <Button\n          variant=\"ghost\"\n          size=\"icon\"\n          className=\"size-8\"\n          aria-label=\"Skip backwords\"\n        >\n          <SkipBackIcon className=\"size-5\" />\n        </Button>\n        <Button\n          variant=\"ghost\"\n          size=\"icon\"\n          className=\"size-8\"\n          onClick={togglePlayPause}\n          aria-label={isPlaying ? \"Pause\" : \"Play\"}\n        >\n          {isPlaying ? (\n            <PauseIcon className=\"size-5\" />\n          ) : (\n            <PlayIcon className=\"size-5\" />\n          )}\n        </Button>\n        <Button\n          variant=\"ghost\"\n          size=\"icon\"\n          className=\"size-8\"\n          aria-label=\"Skip forward\"\n        >\n          <SkipForwardIcon className=\"size-5\" />\n        </Button>\n        <Button\n          variant=\"ghost\"\n          size=\"icon\"\n          className={cn(\n            \"size-7 ml-1.5\",\n            repeatMode !== \"off\" ? \"text-primary\" : \"text-muted-foreground\",\n          )}\n          aria-label={`Repeat: ${repeatMode}`}\n          onClick={toggleRepeat}\n        >\n          {repeatMode === \"track\" ? (\n            <Repeat1Icon className=\"size-3.5\" />\n          ) : (\n            <RepeatIcon className=\"size-3.5\" />\n          )}\n        </Button>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component"
    },
    {
      "path": "hooks/use-music-player.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nexport interface Song {\n  name: string;\n  artists: Array<string>;\n  album: { name: string; image: string };\n  duration: number; // Duration in seconds\n}\n\n// Helper function to format time from seconds to MM:SS\nexport const formatTime = (timeInSeconds: number): string => {\n  const minutes = Math.floor(timeInSeconds / 60);\n  const seconds = Math.floor(timeInSeconds % 60)\n    .toString()\n    .padStart(2, \"0\");\n  return `${minutes}:${seconds}`;\n};\n\nexport type RepeatMode = \"off\" | \"track\" | \"context\";\n\nexport interface UseMusicPlayerProps {\n  song: Song | null;\n}\n\nexport interface UseMusicPlayerReturn {\n  isPlaying: boolean;\n  currentTime: number;\n  duration: number;\n  progressPercentage: number;\n  formattedCurrentTime: string;\n  formattedDuration: string;\n  isShuffling: boolean;\n  repeatMode: RepeatMode;\n  togglePlayPause: () => void;\n  handleSliderChange: (value: number | readonly number[]) => void;\n  toggleShuffle: () => void;\n  toggleRepeat: () => void;\n  setIsPlaying: React.Dispatch<React.SetStateAction<boolean>>;\n  setCurrentTime: React.Dispatch<React.SetStateAction<number>>;\n}\n\nexport function useMusicPlayer({\n  song,\n}: UseMusicPlayerProps): UseMusicPlayerReturn {\n  const [isPlaying, setIsPlaying] = React.useState(false);\n  const [currentTime, setCurrentTime] = React.useState(0);\n  const [isShuffling, setIsShuffling] = React.useState(false);\n  const [repeatMode, setRepeatMode] = React.useState<RepeatMode>(\"off\");\n\n  const songDuration = song?.duration ?? 0;\n\n  // Effect to handle song playback timing\n  React.useEffect(() => {\n    if (!isPlaying || songDuration === 0) return;\n\n    const interval = setInterval(() => {\n      setCurrentTime((prevTime) => {\n        if (prevTime < songDuration - 1) {\n          return prevTime + 1;\n        }\n        if (repeatMode === \"track\") {\n          return 0; // Restart track\n        } else {\n          // For \"off\" and \"context\", we'd normally move to next song or stop.\n          // For now, just stop and set to duration.\n          // \"context\" repeat would be handled by a playlist manager.\n          setIsPlaying(false);\n          return songDuration;\n        }\n      });\n    }, 1000);\n\n    return () => {\n      clearInterval(interval);\n    };\n  }, [isPlaying, songDuration, repeatMode]);\n\n  React.useEffect(() => {\n    if (song) {\n      setCurrentTime(0); // Reset to beginning when song changes\n      setIsPlaying(true); // Autoplay new song\n    } else {\n      // No song, reset state\n      setCurrentTime(0);\n      setIsPlaying(false);\n    }\n  }, [song]);\n\n  const togglePlayPause = React.useCallback(() => {\n    if (!song) return;\n\n    if (currentTime >= songDuration && !isPlaying && songDuration > 0) {\n      setCurrentTime(0);\n      setIsPlaying(true);\n    } else {\n      setIsPlaying((prevIsPlaying) => !prevIsPlaying);\n    }\n  }, [currentTime, songDuration, isPlaying, song]);\n\n  const handleSliderChangeExternal = (value: number | readonly number[]) => {\n    if (!song) return;\n    const v = typeof value === \"number\" ? value : value[0];\n    const newTime = Math.floor((v / 100) * songDuration);\n    setCurrentTime(newTime);\n    if (!isPlaying && newTime < songDuration) {\n      setIsPlaying(true);\n    }\n  };\n\n  const progressPercentage =\n    songDuration > 0 ? (currentTime / songDuration) * 100 : 0;\n\n  const toggleShuffle = React.useCallback(() => {\n    setIsShuffling((prev) => !prev);\n  }, []);\n\n  const toggleRepeat = React.useCallback(() => {\n    setRepeatMode((prevMode) => {\n      if (prevMode === \"off\") return \"context\";\n      if (prevMode === \"context\") return \"track\";\n      return \"off\";\n    });\n  }, []);\n\n  return {\n    isPlaying,\n    currentTime,\n    duration: songDuration,\n    progressPercentage,\n    formattedCurrentTime: formatTime(currentTime),\n    formattedDuration: formatTime(songDuration),\n    isShuffling,\n    repeatMode,\n    togglePlayPause,\n    handleSliderChange: handleSliderChangeExternal,\n    toggleShuffle,\n    toggleRepeat,\n    setIsPlaying, // Exposing these if direct manipulation is needed\n    setCurrentTime, // Exposing these if direct manipulation is needed\n  };\n}\n",
      "type": "registry:hook"
    }
  ],
  "type": "registry:block"
}