{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "video",
  "title": "Video",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-react/components/video/Video.tsx",
      "content": "import * as React from 'react'\nimport { Maximize, Minimize, Pause, Play, RotateCcw, RotateCw, Volume2, VolumeX } from 'lucide-react'\nimport { cn } from '@/lib/utils'\n\nexport interface VideoProps extends React.HTMLAttributes<HTMLDivElement> {\n  /** Video source URL. */\n  src: string\n  /** Poster image shown before playback. */\n  poster?: string\n  /** Autoplay on mount. Note: browsers may block autoplay with sound. */\n  autoplay?: boolean\n  /** Loop playback. */\n  loop?: boolean\n  /** Muted audio. */\n  muted?: boolean\n  /** Use native browser controls instead of the custom overlay. Default false. */\n  nativeControls?: boolean\n  /** Initial playback rate. Default 1. */\n  playbackRate?: number\n  /** Aspect ratio class or inline style value. Default '16/9'. */\n  aspectRatio?: string\n}\n\nconst Video = React.forwardRef<HTMLDivElement, VideoProps>(\n  (\n    {\n      src,\n      poster,\n      autoplay = false,\n      loop = false,\n      muted = false,\n      nativeControls = false,\n      playbackRate = 1,\n      aspectRatio = '16/9',\n      className,\n      ...props\n    },\n    ref,\n  ) => {\n    const videoRef = React.useRef<HTMLVideoElement | null>(null)\n    const containerRef = React.useRef<HTMLDivElement | null>(null)\n\n    const [playing, setPlaying] = React.useState(false)\n    const [current, setCurrent] = React.useState(0)\n    const [duration, setDuration] = React.useState(0)\n    const [volume, setVolume] = React.useState(muted ? 0 : 1)\n    const [isMuted, setIsMuted] = React.useState(muted)\n    const [isFullscreen, setIsFullscreen] = React.useState(false)\n    const [showControls, setShowControls] = React.useState(true)\n    const hideTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n\n    React.useImperativeHandle(ref, () => containerRef.current as HTMLDivElement)\n\n    const togglePlay = React.useCallback(() => {\n      const v = videoRef.current\n      if (!v) return\n      if (v.paused) v.play()\n      else v.pause()\n    }, [])\n\n    const onPlay = React.useCallback(() => setPlaying(true), [])\n    const onPause = React.useCallback(() => setPlaying(false), [])\n    const onTimeUpdate = React.useCallback(() => {\n      const v = videoRef.current\n      if (!v) return\n      setCurrent(v.currentTime)\n    }, [])\n    const onLoadedMetadata = React.useCallback(() => {\n      const v = videoRef.current\n      if (!v) return\n      setDuration(v.duration || 0)\n      v.playbackRate = playbackRate\n      v.volume = volume\n    }, [playbackRate, volume])\n    const onVolumeChange = React.useCallback(() => {\n      const v = videoRef.current\n      if (!v) return\n      setVolume(v.volume)\n      setIsMuted(v.muted)\n    }, [])\n\n    const seek = (e: React.ChangeEvent<HTMLInputElement>) => {\n      const v = videoRef.current\n      if (!v) return\n      v.currentTime = Number(e.target.value)\n    }\n\n    const setVolumeInput = (e: React.ChangeEvent<HTMLInputElement>) => {\n      const v = videoRef.current\n      if (!v) return\n      v.volume = Number(e.target.value)\n      v.muted = Number(e.target.value) === 0\n    }\n\n    const toggleMute = () => {\n      const v = videoRef.current\n      if (!v) return\n      v.muted = !v.muted\n    }\n\n    const skip = (seconds: number) => {\n      const v = videoRef.current\n      if (!v) return\n      v.currentTime = Math.min(Math.max(v.currentTime + seconds, 0), v.duration || 0)\n    }\n\n    const toggleFullscreen = () => {\n      const el = containerRef.current\n      if (!el) return\n      if (document.fullscreenElement) {\n        document.exitFullscreen()\n      } else {\n        el.requestFullscreen()\n      }\n    }\n\n    const onFullscreenChange = React.useCallback(() => {\n      setIsFullscreen(!!document.fullscreenElement)\n    }, [])\n\n    const onMouseMove = () => {\n      setShowControls(true)\n      if (hideTimerRef.current) clearTimeout(hideTimerRef.current)\n      hideTimerRef.current = setTimeout(() => {\n        setPlaying((p) => {\n          if (p) setShowControls(false)\n          return p\n        })\n      }, 2500)\n    }\n\n    const onMouseLeave = () => {\n      if (hideTimerRef.current) clearTimeout(hideTimerRef.current)\n      if (videoRef.current && !videoRef.current.paused) setShowControls(false)\n    }\n\n    const formatTime = (s: number): string => {\n      if (!s || !isFinite(s)) return '0:00'\n      const m = Math.floor(s / 60)\n      const sec = Math.floor(s % 60)\n      return `${m}:${sec.toString().padStart(2, '0')}`\n    }\n\n    const progress = duration > 0 ? (current / duration) * 100 : 0\n\n    React.useEffect(() => {\n      document.addEventListener('fullscreenchange', onFullscreenChange)\n      return () => {\n        document.removeEventListener('fullscreenchange', onFullscreenChange)\n        if (hideTimerRef.current) clearTimeout(hideTimerRef.current)\n      }\n    }, [onFullscreenChange])\n\n    React.useEffect(() => {\n      const v = videoRef.current\n      if (v) v.playbackRate = playbackRate\n    }, [playbackRate])\n\n    React.useEffect(() => {\n      const v = videoRef.current\n      if (v) v.muted = muted\n    }, [muted])\n\n    return (\n      <div\n        ref={containerRef}\n        data-uipkge=\"\"\n        data-slot=\"video\"\n        className={cn('group relative overflow-hidden rounded-lg bg-black', className)}\n        style={{ aspectRatio }}\n        onMouseMove={onMouseMove}\n        onMouseLeave={onMouseLeave}\n        {...props}\n      >\n        <video\n          ref={videoRef}\n          data-slot=\"video-element\"\n          className=\"size-full object-contain\"\n          src={src}\n          poster={poster}\n          autoPlay={autoplay}\n          loop={loop}\n          muted={muted}\n          controls={nativeControls}\n          playsInline\n          onPlay={onPlay}\n          onPause={onPause}\n          onTimeUpdate={onTimeUpdate}\n          onLoadedMetadata={onLoadedMetadata}\n          onVolumeChange={onVolumeChange}\n          onClick={togglePlay}\n        />\n\n        {!nativeControls && (\n          <div\n            data-slot=\"video-controls\"\n            className={cn(\n              'absolute inset-0 flex flex-col justify-between transition-opacity duration-200',\n              showControls || !playing ? 'opacity-100' : 'opacity-0',\n            )}\n          >\n            {/* Top spacer / click target */}\n            <div className=\"flex-1\" onClick={togglePlay} />\n\n            {/* Bottom controls bar */}\n            <div className=\"bg-gradient-to-t from-black/80 to-transparent px-3 pt-6 pb-2\">\n              {/* Progress bar */}\n              <div className=\"mb-2 flex items-center gap-2\">\n                <span className=\"text-xs text-white/80 tabular-nums\">{formatTime(current)}</span>\n                <input\n                  type=\"range\"\n                  min=\"0\"\n                  max={duration || 0}\n                  step=\"0.1\"\n                  value={current}\n                  className=\"accent-primary h-1 flex-1 cursor-pointer appearance-none rounded-full bg-white/30 [&::-webkit-slider-thumb]:size-3 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-primary\"\n                  onChange={seek}\n                />\n                <span className=\"text-xs text-white/80 tabular-nums\">{formatTime(duration)}</span>\n              </div>\n\n              {/* Buttons row */}\n              <div className=\"flex items-center gap-2\">\n                <button\n                  type=\"button\"\n                  className=\"text-white/90 transition-colors hover:text-white\"\n                  aria-label={playing ? 'Pause' : 'Play'}\n                  onClick={togglePlay}\n                >\n                  {playing ? <Pause className=\"size-5\" /> : <Play className=\"size-5\" />}\n                </button>\n                <button\n                  type=\"button\"\n                  className=\"text-white/90 transition-colors hover:text-white\"\n                  aria-label=\"Rewind 10s\"\n                  onClick={() => skip(-10)}\n                >\n                  <RotateCcw className=\"size-4\" />\n                </button>\n                <button\n                  type=\"button\"\n                  className=\"text-white/90 transition-colors hover:text-white\"\n                  aria-label=\"Forward 10s\"\n                  onClick={() => skip(10)}\n                >\n                  <RotateCw className=\"size-4\" />\n                </button>\n\n                {/* Volume */}\n                <div className=\"group/volume flex items-center gap-1.5\">\n                  <button\n                    type=\"button\"\n                    className=\"text-white/90 transition-colors hover:text-white\"\n                    aria-label={isMuted ? 'Unmute' : 'Mute'}\n                    onClick={toggleMute}\n                  >\n                    {isMuted || volume === 0 ? <VolumeX className=\"size-4\" /> : <Volume2 className=\"size-4\" />}\n                  </button>\n                  <input\n                    type=\"range\"\n                    min=\"0\"\n                    max=\"1\"\n                    step=\"0.05\"\n                    value={isMuted ? 0 : volume}\n                    className=\"accent-primary h-1 w-0 cursor-pointer appearance-none rounded-full bg-white/30 transition-all duration-200 group-hover/volume:w-16 [&::-webkit-slider-thumb]:size-3 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-primary\"\n                    onChange={setVolumeInput}\n                  />\n                </div>\n\n                <div className=\"flex-1\" />\n\n                <button\n                  type=\"button\"\n                  className=\"text-white/90 transition-colors hover:text-white\"\n                  aria-label={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}\n                  onClick={toggleFullscreen}\n                >\n                  {isFullscreen ? <Minimize className=\"size-4\" /> : <Maximize className=\"size-4\" />}\n                </button>\n              </div>\n            </div>\n          </div>\n        )}\n      </div>\n    )\n  },\n)\nVideo.displayName = 'Video'\n\nexport { Video }\n",
      "type": "registry:ui",
      "target": "~/components/ui/video/Video.tsx"
    },
    {
      "path": "packages/registry-react/components/video/index.ts",
      "content": "export { Video, type VideoProps } from './Video'\n",
      "type": "registry:ui",
      "target": "~/components/ui/video/index.ts"
    }
  ],
  "dependencies": [
    "lucide-react"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Video player wrapper with a custom controls overlay on a native video element. Supports src, poster, autoplay, loop, muted, custom or native controls, playback rate, fullscreen, time display, a progress bar, play/pause, skip, and volume control.",
  "categories": [
    "media",
    "display"
  ]
}