{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "video-playlist-sidebar",
  "title": "Video Playlist Sidebar",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-react/blocks/video-playlist-sidebar/VideoPlaylistSidebar.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport {\n  Check,\n  CheckCircle2,\n  Clock,\n  Download,\n  FileCode2,\n  ListVideo,\n  Play,\n  PlayCircle,\n  Repeat,\n  Repeat1,\n  Search,\n  Shuffle,\n  SkipForward,\n} from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Progress } from '@/components/ui/progress'\nimport { Switch } from '@/components/ui/switch'\n\nexport interface VideoResource {\n  id: string\n  name: string\n  size: string\n  type: 'code' | 'figma' | 'pdf'\n}\n\nexport interface PlaylistItem {\n  id: string\n  index: number\n  title: string\n  instructor: string\n  instructorRole: string\n  instructorAvatar: string\n  duration: string\n  durationSec: number\n  category: string\n  summary: string\n  isCompleted: boolean\n  thumbnailGradient: string\n  resources: VideoResource[]\n}\n\nconst initialPlaylist: PlaylistItem[] = [\n  {\n    id: 'vid-01',\n    index: 1,\n    title: '01. Introduction to Component Registries',\n    instructor: 'Marcus Vance',\n    instructorRole: 'Principal Design Engineer',\n    instructorAvatar: 'MV',\n    duration: '14:20',\n    durationSec: 860,\n    category: 'Architecture',\n    summary: 'Understanding unbundled component distribution, registry manifests, and own-your-code paradigms.',\n    isCompleted: true,\n    thumbnailGradient: 'from-slate-800 to-zinc-950',\n    resources: [\n      { id: 'res-1a', name: 'registry-schema-spec.pdf', size: '1.2 MB', type: 'pdf' },\n      { id: 'res-1b', name: 'starter-registry.json', size: '48 KB', type: 'code' },\n    ],\n  },\n  {\n    id: 'vid-02',\n    index: 2,\n    title: '02. Headless Reka UI Primitives',\n    instructor: 'Elena Rostova',\n    instructorRole: 'Staff Frontend Engineer',\n    instructorAvatar: 'ER',\n    duration: '18:45',\n    durationSec: 1125,\n    category: 'Accessibility',\n    summary: 'Composition with asChild, polymorphic slot mechanics, and ARIA state tree management.',\n    isCompleted: true,\n    thumbnailGradient: 'from-zinc-800 to-slate-950',\n    resources: [\n      { id: 'res-2a', name: 'reka-composition-cheatsheet.pdf', size: '2.4 MB', type: 'pdf' },\n      { id: 'res-2b', name: 'accessible-dialog.vue', size: '14 KB', type: 'code' },\n    ],\n  },\n  {\n    id: 'vid-03',\n    index: 3,\n    title: '03. Building with OKLCH Tokens',\n    instructor: 'Sarah Connor',\n    instructorRole: 'Design Systems Architect',\n    instructorAvatar: 'SC',\n    duration: '22:10',\n    durationSec: 1330,\n    category: 'Color Systems',\n    summary: 'Perceptually uniform color spaces, wide-gamut Display P3 gamut mapping, and dark mode contrast.',\n    isCompleted: false,\n    thumbnailGradient: 'from-slate-850 to-zinc-950',\n    resources: [\n      { id: 'res-3a', name: 'tokens.config.css', size: '14.2 KB', type: 'code' },\n      { id: 'res-3b', name: 'oklch-palette-matrix.fig', size: '4.8 MB', type: 'figma' },\n      { id: 'res-3c', name: 'apca-contrast-guide.pdf', size: '1.1 MB', type: 'pdf' },\n    ],\n  },\n  {\n    id: 'vid-04',\n    index: 4,\n    title: '04. Micro-Interactions & Spring Physics',\n    instructor: 'Alex Chen',\n    instructorRole: 'Motion Design Lead',\n    instructorAvatar: 'AC',\n    duration: '19:35',\n    durationSec: 1175,\n    category: 'Motion & Physics',\n    summary: 'Crafting tactile UI states, gesture dampening, spring curves, and 120fps hardware acceleration.',\n    isCompleted: false,\n    thumbnailGradient: 'from-zinc-800 to-zinc-950',\n    resources: [\n      { id: 'res-4a', name: 'spring-curves-preset.ts', size: '8.4 KB', type: 'code' },\n      { id: 'res-4b', name: 'gesture-lab.fig', size: '3.6 MB', type: 'figma' },\n    ],\n  },\n  {\n    id: 'vid-05',\n    index: 5,\n    title: '05. Multi-Framework Parity Testing',\n    instructor: 'Marcus Vance',\n    instructorRole: 'Principal Design Engineer',\n    instructorAvatar: 'MV',\n    duration: '16:50',\n    durationSec: 1010,\n    category: 'Testing & Quality',\n    summary: 'Automating visual regression tests and unit tests across dual Vue 3 and React 19 packages.',\n    isCompleted: false,\n    thumbnailGradient: 'from-slate-800 to-slate-950',\n    resources: [\n      { id: 'res-5a', name: 'parity-test-matrix.ts', size: '22.0 KB', type: 'code' },\n      { id: 'res-5b', name: 'playwright.config.ts', size: '6.5 KB', type: 'code' },\n    ],\n  },\n  {\n    id: 'vid-06',\n    index: 6,\n    title: '06. CI/CD Deployment to Cloudflare',\n    instructor: 'David Kim',\n    instructorRole: 'DevOps & Cloud Engineer',\n    instructorAvatar: 'DK',\n    duration: '25:15',\n    durationSec: 1515,\n    category: 'DevOps & Edge',\n    summary: 'Automating multi-framework build chains, registry sync, and edge deployment via Wrangler.',\n    isCompleted: false,\n    thumbnailGradient: 'from-zinc-850 to-zinc-950',\n    resources: [\n      { id: 'res-6a', name: 'wrangler.toml', size: '3.4 KB', type: 'code' },\n      { id: 'res-6b', name: 'deploy-pipeline.yml', size: '5.1 KB', type: 'code' },\n    ],\n  },\n]\n\nexport interface VideoPlaylistSidebarProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nexport function VideoPlaylistSidebar({ className, ...props }: VideoPlaylistSidebarProps) {\n  const [playlist, setPlaylist] = React.useState<PlaylistItem[]>(initialPlaylist)\n  const [activeVideoId, setActiveVideoId] = React.useState<string>('vid-03')\n  const [autoPlay, setAutoPlay] = React.useState<boolean>(true)\n  const [isLooping, setIsLooping] = React.useState<boolean>(false)\n  const [isShuffled, setIsShuffled] = React.useState<boolean>(false)\n  const [searchQuery, setSearchQuery] = React.useState<string>('')\n  const [downloadedAssets, setDownloadedAssets] = React.useState<Record<string, boolean>>({})\n\n  const activeVideo = React.useMemo(() => {\n    return playlist.find((item) => item.id === activeVideoId) || playlist[0]\n  }, [playlist, activeVideoId])\n\n  const completedCount = React.useMemo(() => {\n    return playlist.filter((item) => item.isCompleted).length\n  }, [playlist])\n\n  const totalCount = playlist.length\n  const progressPercent = totalCount === 0 ? 0 : Math.round((completedCount / totalCount) * 100)\n\n  const filteredVideos = React.useMemo(() => {\n    const query = searchQuery.trim().toLowerCase()\n    if (!query) return playlist\n    return playlist.filter(\n      (item) =>\n        item.title.toLowerCase().includes(query) ||\n        item.instructor.toLowerCase().includes(query) ||\n        item.category.toLowerCase().includes(query),\n    )\n  }, [playlist, searchQuery])\n\n  const selectVideo = (id: string) => {\n    setActiveVideoId(id)\n  }\n\n  const toggleComplete = (id: string) => {\n    setPlaylist((prev) => prev.map((item) => (item.id === id ? { ...item, isCompleted: !item.isCompleted } : item)))\n  }\n\n  const nextVideo = () => {\n    const currentIndex = playlist.findIndex((item) => item.id === activeVideoId)\n    if (currentIndex === -1) return\n\n    if (isShuffled) {\n      const remaining = playlist.filter((item) => item.id !== activeVideoId)\n      if (remaining.length > 0) {\n        const randomIndex = Math.floor(Math.random() * remaining.length)\n        setActiveVideoId(remaining[randomIndex].id)\n      }\n      return\n    }\n\n    if (currentIndex < playlist.length - 1) {\n      setActiveVideoId(playlist[currentIndex + 1].id)\n    } else if (isLooping) {\n      setActiveVideoId(playlist[0].id)\n    }\n  }\n\n  const handleDownload = (assetId: string) => {\n    setDownloadedAssets((prev) => ({ ...prev, [assetId]: true }))\n    setTimeout(() => {\n      setDownloadedAssets((prev) => ({ ...prev, [assetId]: false }))\n    }, 2000)\n  }\n\n  return (\n    <div\n      data-slot=\"video-playlist-sidebar\"\n      className={cn('text-foreground mx-auto w-full max-w-2xl space-y-4', className)}\n      {...props}\n    >\n      {/* Main Playlist Control Card */}\n      <Card className=\"border-border bg-card shadow-xs\">\n        <CardHeader className=\"space-y-3 pb-3 sm:pb-4\">\n          {/* Badge row */}\n          <div className=\"flex flex-wrap items-center justify-between gap-2\">\n            <div className=\"flex items-center gap-2\">\n              <Badge variant=\"secondary\" className=\"gap-1.5 text-xs font-semibold\">\n                <ListVideo className=\"text-primary size-3.5\" />\n                <span>Video Playlist</span>\n              </Badge>\n              <Badge variant=\"outline\" className=\"text-muted-foreground gap-1 font-mono text-xs\">\n                <Clock className=\"size-3\" />\n                <span>1h 56m Total</span>\n              </Badge>\n            </div>\n            <span className=\"text-muted-foreground text-xs font-medium\">UIPKGE Masterclass Series</span>\n          </div>\n\n          {/* Title & Subtitle */}\n          <div className=\"space-y-1\">\n            <CardTitle className=\"text-foreground text-lg font-bold tracking-tight sm:text-xl\">\n              Mastering Full-Stack UI Engineering\n            </CardTitle>\n            <CardDescription className=\"text-xs\">\n              Curated playlist on headless primitives, OKLCH token systems, fluid motion physics, and registry CI\n              pipelines.\n            </CardDescription>\n          </div>\n\n          {/* Progress bar and counter */}\n          <div className=\"bg-muted/40 border-border/70 space-y-2 rounded-lg border p-3\">\n            <div className=\"flex items-center justify-between text-xs\">\n              <span className=\"text-foreground font-semibold\">\n                <span className=\"font-mono tabular-nums\">{completedCount}</span> /{' '}\n                <span className=\"font-mono tabular-nums\">{totalCount}</span> Videos Completed\n              </span>\n              <span className=\"text-primary font-mono font-bold tabular-nums\">{progressPercent}%</span>\n            </div>\n            <Progress value={progressPercent} className=\"h-2\" />\n          </div>\n\n          {/* Toolbar: Autoplay switch, Loop & Shuffle toggles */}\n          <div className=\"flex flex-wrap items-center justify-between gap-3 pt-1\">\n            {/* Autoplay next switch */}\n            <div className=\"flex items-center gap-2.5\">\n              <Switch id=\"autoplay-switch-react\" checked={autoPlay} onCheckedChange={setAutoPlay} size=\"sm\" />\n              <label\n                htmlFor=\"autoplay-switch-react\"\n                className=\"text-foreground flex cursor-pointer items-center gap-1.5 text-xs font-medium select-none\"\n              >\n                <Play className=\"text-primary fill-primary size-3.5\" />\n                <span>Autoplay Next Video</span>\n              </label>\n            </div>\n\n            {/* Loop and Shuffle Buttons */}\n            <div className=\"flex items-center gap-1.5\">\n              <Button\n                variant=\"ghost\"\n                size=\"sm\"\n                className={cn(\n                  'h-7.5 gap-1.5 px-2.5 text-xs font-medium',\n                  isLooping\n                    ? 'border-primary/30 bg-primary/10 text-primary border font-semibold'\n                    : 'text-muted-foreground hover:text-foreground',\n                )}\n                aria-pressed={isLooping}\n                aria-label=\"Toggle loop playlist\"\n                onClick={() => setIsLooping(!isLooping)}\n              >\n                {isLooping ? <Repeat1 className=\"size-3.5\" /> : <Repeat className=\"size-3.5\" />}\n                <span>Loop</span>\n              </Button>\n\n              <Button\n                variant=\"ghost\"\n                size=\"sm\"\n                className={cn(\n                  'h-7.5 gap-1.5 px-2.5 text-xs font-medium',\n                  isShuffled\n                    ? 'border-primary/30 bg-primary/10 text-primary border font-semibold'\n                    : 'text-muted-foreground hover:text-foreground',\n                )}\n                aria-pressed={isShuffled}\n                aria-label=\"Toggle shuffle playlist\"\n                onClick={() => setIsShuffled(!isShuffled)}\n              >\n                <Shuffle className=\"size-3.5\" />\n                <span>Shuffle</span>\n              </Button>\n            </div>\n          </div>\n        </CardHeader>\n      </Card>\n\n      {/* Currently Active Video Card */}\n      <Card className=\"border-primary/40 from-primary/5 via-card to-card relative overflow-hidden bg-gradient-to-br shadow-xs\">\n        <div className=\"bg-primary absolute top-0 right-0 left-0 h-0.5\" />\n        <CardHeader className=\"pb-3\">\n          <div className=\"flex flex-wrap items-center justify-between gap-2\">\n            <div className=\"flex items-center gap-2\">\n              <Badge variant=\"default\" className=\"gap-1.5 text-xs font-semibold\">\n                <span className=\"size-1.5 animate-pulse rounded-full bg-white\" />\n                <span>Now Playing</span>\n              </Badge>\n              <Badge variant=\"outline\" className=\"font-mono text-xs font-medium\">\n                Lesson {activeVideo.index} of {totalCount}\n              </Badge>\n            </div>\n            <span className=\"text-muted-foreground font-mono text-xs font-semibold tabular-nums\">\n              {activeVideo.duration}\n            </span>\n          </div>\n\n          <div className=\"space-y-1 pt-1\">\n            <CardTitle className=\"text-foreground text-base font-bold tracking-tight sm:text-lg\">\n              {activeVideo.title}\n            </CardTitle>\n            <CardDescription className=\"text-xs leading-relaxed\">{activeVideo.summary}</CardDescription>\n          </div>\n\n          {/* Instructor and Metadata */}\n          <div className=\"flex flex-wrap items-center justify-between gap-3 pt-2\">\n            <div className=\"flex items-center gap-2\">\n              <div className=\"border-primary/30 bg-primary/15 text-primary flex size-7 items-center justify-center rounded-full border text-xs font-bold\">\n                {activeVideo.instructorAvatar}\n              </div>\n              <div>\n                <p className=\"text-foreground text-xs font-semibold\">{activeVideo.instructor}</p>\n                <p className=\"text-muted-foreground text-xs\">\n                  {activeVideo.instructorRole} · {activeVideo.category}\n                </p>\n              </div>\n            </div>\n\n            {/* Video status controls */}\n            <div className=\"flex items-center gap-2\">\n              <Button\n                variant={activeVideo.isCompleted ? 'secondary' : 'outline'}\n                size=\"sm\"\n                className=\"h-7.5 gap-1.5 px-2.5 text-xs font-medium\"\n                onClick={() => toggleComplete(activeVideo.id)}\n              >\n                {activeVideo.isCompleted ? (\n                  <CheckCircle2 className=\"text-success size-3.5\" />\n                ) : (\n                  <Check className=\"size-3.5\" />\n                )}\n                <span>{activeVideo.isCompleted ? 'Completed' : 'Mark as Watched'}</span>\n              </Button>\n\n              <Button size=\"sm\" className=\"h-7.5 gap-1.5 px-2.5 text-xs font-semibold shadow-xs\" onClick={nextVideo}>\n                <span>Next</span>\n                <SkipForward className=\"size-3.5\" />\n              </Button>\n            </div>\n          </div>\n        </CardHeader>\n\n        {/* Downloadable Lesson Exercise Files */}\n        <CardContent className=\"border-border/60 bg-muted/20 border-t pt-3 pb-3.5\">\n          <div className=\"space-y-2\">\n            <div className=\"flex items-center justify-between\">\n              <p className=\"text-foreground flex items-center gap-1.5 text-xs font-bold tracking-tight uppercase\">\n                <FileCode2 className=\"text-primary size-3.5\" />\n                <span>Lesson Exercise Files</span>\n              </p>\n              <span className=\"text-muted-foreground font-mono text-xs\">{activeVideo.resources.length} Files</span>\n            </div>\n\n            <div className=\"grid grid-cols-1 gap-2 sm:grid-cols-2\">\n              {activeVideo.resources.map((res) => (\n                <div\n                  key={res.id}\n                  className=\"border-border/80 bg-card hover:bg-muted/40 flex items-center justify-between gap-2 rounded-lg border p-2 text-xs transition-colors\"\n                >\n                  <div className=\"flex min-w-0 items-center gap-2\">\n                    <span className=\"border-primary/20 bg-primary/10 text-primary flex size-6 shrink-0 items-center justify-center rounded font-mono text-xs font-bold\">\n                      {res.type === 'code' ? 'TS' : res.type === 'figma' ? 'FIG' : 'PDF'}\n                    </span>\n                    <div className=\"min-w-0\">\n                      <p className=\"text-foreground truncate text-xs font-medium\">{res.name}</p>\n                      <p className=\"text-muted-foreground font-mono text-xs\">{res.size}</p>\n                    </div>\n                  </div>\n\n                  <Button\n                    aria-label=\"Download attachment\"\n                    variant=\"outline\"\n                    size=\"sm\"\n                    className=\"h-6.5 shrink-0 gap-1 px-2 text-xs font-medium\"\n                    onClick={() => handleDownload(res.id)}\n                  >\n                    {downloadedAssets[res.id] ? (\n                      <Check className=\"text-success size-3\" />\n                    ) : (\n                      <Download className=\"size-3\" />\n                    )}\n                    <span>{downloadedAssets[res.id] ? 'Saved' : 'Get'}</span>\n                  </Button>\n                </div>\n              ))}\n            </div>\n          </div>\n        </CardContent>\n      </Card>\n\n      {/* Playlist Queue & List Header */}\n      <Card className=\"border-border bg-card shadow-xs\">\n        <CardHeader className=\"space-y-3 pb-3\">\n          <div className=\"flex flex-wrap items-center justify-between gap-2\">\n            <div className=\"flex items-center gap-2\">\n              <CardTitle className=\"text-foreground text-sm font-bold tracking-tight\">Playlist Queue</CardTitle>\n              <Badge variant=\"secondary\" className=\"font-mono text-xs\">\n                {totalCount} Videos\n              </Badge>\n            </div>\n            <span className=\"text-muted-foreground text-xs font-medium\">Click any lesson to play</span>\n          </div>\n\n          {/* Search / Filter input */}\n          <div className=\"relative\">\n            <Search className=\"text-muted-foreground absolute top-2.5 left-2.5 size-3.5\" />\n            <input\n              type=\"text\"\n              value={searchQuery}\n              onChange={(e) => setSearchQuery(e.target.value)}\n              placeholder=\"Filter playlist by title, instructor, or topic...\"\n              className=\"border-input bg-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 h-8 w-full rounded-md border pr-3 pl-8 text-xs shadow-xs outline-none focus-visible:ring-[3px]\"\n            />\n          </div>\n        </CardHeader>\n\n        {/* Video Items List */}\n        <CardContent className=\"divide-border/60 divide-y p-0\">\n          {filteredVideos.map((video) => {\n            const isActive = activeVideoId === video.id\n            return (\n              <div\n                key={video.id}\n                tabIndex={0}\n                role=\"button\"\n                aria-label={`Play ${video.title}`}\n                aria-current={isActive ? 'true' : undefined}\n                className={cn(\n                  'group relative flex cursor-pointer items-center justify-between gap-3 p-3 transition-colors duration-150',\n                  isActive ? 'border-primary/50 bg-primary/10 ring-primary/20 shadow-xs ring-1' : 'hover:bg-muted/40',\n                )}\n                onClick={() => selectVideo(video.id)}\n                onKeyDown={(e) => {\n                  if (e.key === 'Enter' || e.key === ' ') {\n                    e.preventDefault()\n                    selectVideo(video.id)\n                  }\n                }}\n              >\n                {/* Left active highlight bar */}\n                {isActive && <div className=\"bg-primary absolute top-2 bottom-2 left-0 w-1 rounded-r shadow-xs\" />}\n\n                {/* Left Section: Index / Status Indicator + Video Thumbnail */}\n                <div className=\"flex min-w-0 items-center gap-3\">\n                  {/* Play / Equalizer / Completed Status Icon */}\n                  <div className=\"flex size-6 shrink-0 items-center justify-center\">\n                    {isActive ? (\n                      /* Animated Equalizer Sound Bars */\n                      <div className=\"flex h-3.5 w-3.5 items-end justify-center gap-0.5\" title=\"Playing\">\n                        <span className=\"bg-primary h-3 w-0.5 animate-pulse rounded-full\" />\n                        <span\n                          className=\"bg-primary h-4 w-0.5 animate-pulse rounded-full\"\n                          style={{ animationDelay: '150ms' }}\n                        />\n                        <span\n                          className=\"bg-primary h-2 w-0.5 animate-pulse rounded-full\"\n                          style={{ animationDelay: '300ms' }}\n                        />\n                      </div>\n                    ) : video.isCompleted ? (\n                      <CheckCircle2 className=\"text-success size-4 shrink-0\" aria-hidden=\"true\" />\n                    ) : (\n                      <span className=\"text-muted-foreground font-mono text-xs font-semibold tabular-nums\">\n                        {video.index.toString().padStart(2, '0')}\n                      </span>\n                    )}\n                  </div>\n\n                  {/* 16:9 Video Thumbnail Preview */}\n                  <div\n                    className={cn(\n                      'relative aspect-video w-24 shrink-0 overflow-hidden rounded-md border border-zinc-800 bg-zinc-950 bg-gradient-to-br shadow-xs sm:w-28',\n                      video.thumbnailGradient,\n                    )}\n                  >\n                    {/* Center Play Watermark */}\n                    <div className=\"absolute inset-0 flex items-center justify-center\">\n                      <PlayCircle\n                        className={cn(\n                          'size-5 transition-transform group-hover:scale-110',\n                          isActive ? 'text-primary fill-primary/20' : 'text-muted-foreground/80',\n                        )}\n                      />\n                    </div>\n\n                    {/* Duration Badge (Bottom-Right) */}\n                    <div className=\"absolute right-1 bottom-1 rounded bg-black/85 px-1 py-0.5 font-mono text-xs font-semibold text-white tabular-nums backdrop-blur-xs\">\n                      {video.duration}\n                    </div>\n                  </div>\n\n                  {/* Video Title & Instructor Info */}\n                  <div className=\"min-w-0 space-y-0.5\">\n                    <p\n                      className={cn(\n                        'truncate text-xs font-semibold transition-colors sm:text-sm',\n                        isActive ? 'text-primary font-bold' : 'text-foreground group-hover:text-primary',\n                      )}\n                    >\n                      {video.title}\n                    </p>\n                    <p className=\"text-muted-foreground truncate text-xs\">\n                      <span>{video.instructor}</span>\n                      <span className=\"mx-1.5\">·</span>\n                      <span>{video.category}</span>\n                    </p>\n                  </div>\n                </div>\n\n                {/* Right Section: Status Badge & Quick Watch Toggle */}\n                <div className=\"flex shrink-0 items-center gap-2\">\n                  {isActive ? (\n                    <Badge variant=\"default\" className=\"hidden px-2 py-0.5 text-xs font-semibold sm:inline-flex\">\n                      Playing\n                    </Badge>\n                  ) : video.isCompleted ? (\n                    <Badge\n                      variant=\"outline\"\n                      className=\"text-success text-success hidden gap-1 px-1.5 py-0.5 text-xs font-medium sm:inline-flex\"\n                    >\n                      <Check className=\"size-3\" />\n                      <span>Watched</span>\n                    </Badge>\n                  ) : null}\n\n                  {/* Toggle complete button */}\n                  <Button\n                    variant=\"ghost\"\n                    size=\"icon\"\n                    className=\"text-muted-foreground hover:text-foreground size-7 shrink-0\"\n                    aria-label={video.isCompleted ? 'Mark as unwatched' : 'Mark as watched'}\n                    onClick={(e) => {\n                      e.stopPropagation()\n                      toggleComplete(video.id)\n                    }}\n                  >\n                    {video.isCompleted ? (\n                      <CheckCircle2 className=\"text-success size-4\" />\n                    ) : isActive ? (\n                      <Play className=\"text-primary size-3.5 fill-current\" />\n                    ) : (\n                      <Check className=\"text-muted-foreground/60 hover:text-foreground size-3.5\" />\n                    )}\n                  </Button>\n                </div>\n              </div>\n            )\n          })}\n\n          {filteredVideos.length === 0 && (\n            <div className=\"text-muted-foreground p-8 text-center text-xs\">No lessons match your search criteria.</div>\n          )}\n        </CardContent>\n\n        <CardFooter className=\"border-border/60 bg-muted/20 flex items-center justify-between border-t p-3 text-xs\">\n          <span className=\"text-muted-foreground\">Mastering Full-Stack UI Engineering</span>\n          <span className=\"text-muted-foreground font-mono font-medium tabular-nums\">\n            {completedCount}/{totalCount} Completed ({progressPercent}%)\n          </span>\n        </CardFooter>\n      </Card>\n    </div>\n  )\n}\n\nexport default VideoPlaylistSidebar\n",
      "type": "registry:block",
      "target": "~/components/blocks/VideoPlaylistSidebar.tsx"
    }
  ],
  "dependencies": [
    "lucide-react"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/react/badge.json",
    "https://uipkge.dev/r/react/button.json",
    "https://uipkge.dev/r/react/card.json",
    "https://uipkge.dev/r/react/progress.json",
    "https://uipkge.dev/r/react/switch.json"
  ],
  "description": "YouTube and Vimeo style video playlist drawer with active playing indicator, durations, auto-play next switch, loop/shuffle controls, and active lesson summary card.",
  "categories": [
    "media",
    "app",
    "education"
  ]
}