{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "video-player",
  "author": "Rohan Gupta (@winoffrg)",
  "description": "Modern seamless flat video player",
  "dependencies": [
    "@phosphor-icons/react",
    "@base-ui/react",
    "@radix-ui/react-toggle",
    "zustand",
    "shaka-player@^5.2.7",
    "lodash.clamp"
  ],
  "registryDependencies": [
    "https://bazza.dev/r/hit-area",
    "dropdown-menu",
    "https://limeplay.winoffrg.dev/r/player-layout.json",
    "https://limeplay.winoffrg.dev/r/media.json",
    "https://limeplay.winoffrg.dev/r/media-provider.json",
    "https://limeplay.winoffrg.dev/r/mute-control.json",
    "https://limeplay.winoffrg.dev/r/playback-control.json",
    "https://limeplay.winoffrg.dev/r/picture-in-picture-control.json",
    "https://limeplay.winoffrg.dev/r/timeline-control.json",
    "https://limeplay.winoffrg.dev/r/volume-control.json",
    "https://limeplay.winoffrg.dev/r/use-timeline.json",
    "https://limeplay.winoffrg.dev/r/use-volume.json",
    "https://limeplay.winoffrg.dev/r/use-player.json",
    "https://limeplay.winoffrg.dev/r/use-track-events.json",
    "https://limeplay.winoffrg.dev/r/utils.json",
    "https://limeplay.winoffrg.dev/r/error-screen.json",
    "https://limeplay.winoffrg.dev/r/fallback-poster.json",
    "https://limeplay.winoffrg.dev/r/timeline-labels.json",
    "https://limeplay.winoffrg.dev/r/limeplay-logo.json",
    "https://limeplay.winoffrg.dev/r/root-container.json",
    "https://limeplay.winoffrg.dev/r/captions.json",
    "https://limeplay.winoffrg.dev/r/use-captions.json",
    "https://limeplay.winoffrg.dev/r/playback-rate.json",
    "https://limeplay.winoffrg.dev/r/use-playback-rate.json",
    "https://limeplay.winoffrg.dev/r/use-playback.json",
    "https://limeplay.winoffrg.dev/r/use-picture-in-picture.json",
    "https://limeplay.winoffrg.dev/r/use-playlist.json",
    "https://limeplay.winoffrg.dev/r/use-asset.json",
    "https://limeplay.winoffrg.dev/r/use-media.json",
    "https://limeplay.winoffrg.dev/r/use-media-session.json",
    "https://limeplay.winoffrg.dev/r/use-controls-visibility.json",
    "https://limeplay.winoffrg.dev/r/use-playback-source.json"
  ],
  "files": [
    {
      "path": "registry/default/blocks/video-player/player.tsx",
      "content": "\"use client\"\n\nimport React from \"react\"\n\nimport type { PlaybackSourceControllerProps } from \"@/registry/default/hooks/use-playback-source\"\n\nimport { cn } from \"@/lib/utils\"\nimport { BottomControls } from \"@/registry/default/blocks/video-player/components/bottom-controls\"\nimport { VideoMediaSessionController } from \"@/registry/default/blocks/video-player/components/media-session-controller\"\nimport { PlayerErrorScreen } from \"@/registry/default/blocks/video-player/components/player-error-screen\"\nimport {\n  PlayerRootContainer,\n  type PlayerRootContainerLayout,\n} from \"@/registry/default/blocks/video-player/components/player-root-container\"\nimport { TopOverlayContainer } from \"@/registry/default/blocks/video-player/components/top-overlay-container\"\nimport { MediaProvider } from \"@/registry/default/blocks/video-player/lib/media-kit\"\nimport { type TAsset, useAsset } from \"@/registry/default/hooks/use-asset\"\nimport { PlaybackSourceController } from \"@/registry/default/hooks/use-playback-source\"\nimport { CaptionsContainer } from \"@/registry/default/ui/captions\"\nimport { FallbackPoster } from \"@/registry/default/ui/fallback-poster\"\nimport { LimeplayLogo } from \"@/registry/default/ui/limeplay-logo\"\nimport { Media } from \"@/registry/default/ui/media\"\nimport * as Layout from \"@/registry/default/ui/player-layout\"\n\nimport \"./styles.css\"\n\nexport interface VideoPlayerAsset extends TAsset {\n  description?: string\n  poster?: string\n  title?: string\n  year?: string\n}\n\nexport interface VideoPlayerProps extends PlaybackSourceControllerProps<VideoPlayerAsset> {\n  children?: React.ReactNode\n  className?: string\n  debug?: boolean\n  /**\n   * Controls how the player frame gets its size.\n   *\n   * `aspect` keeps the default 16:9 box. `fill` lets a height-constrained\n   * parent own the player size.\n   *\n   * @default \"aspect\"\n   */\n  layout?: PlayerRootContainerLayout\n  /**\n   * Props to pass to the underlying video element.\n   */\n  mediaProps?: Omit<React.VideoHTMLAttributes<HTMLVideoElement>, \"as\" | \"src\">\n  /**\n   * @default dark\n   */\n  theme?: \"dark\" | \"light\"\n}\n\nexport const VideoPlayer = React.forwardRef<HTMLDivElement, VideoPlayerProps>(\n  function VideoPlayer(\n    {\n      autoLoad,\n      children,\n      className,\n      debug,\n      initialIndex,\n      layout = \"aspect\",\n      loading,\n      mediaProps,\n      source,\n      sourceKey,\n      theme = \"auto\",\n    },\n    ref\n  ) {\n    return (\n      <MediaProvider debug={debug}>\n        <PlayerRootContainer\n          className={cn(className, theme === \"dark\" && \"dark\")}\n          layout={layout}\n          ref={ref}\n        >\n          <PlaybackSourceController\n            autoLoad={autoLoad}\n            initialIndex={initialIndex}\n            loading={loading}\n            source={source}\n            sourceKey={sourceKey}\n          />\n          <VideoMediaSessionController />\n          <PlayerErrorScreen\n            initialIndex={initialIndex}\n            loading={loading}\n            source={source}\n            sourceKey={sourceKey}\n          />\n          <Layout.PlayerContainer className=\"size-full min-h-0\">\n            <FallbackPoster>\n              <LimeplayLogo />\n            </FallbackPoster>\n            <CurrentAssetMedia {...(mediaProps ?? {})} />\n            {children}\n            <Layout.ControlsOverlayContainer\n              className=\"\n                bg-linear-[to_top,var(--lp-controls-fade-stops)] bg-size-[100%_80%] bg-top bg-no-repeat transition-opacity duration-300 ease-out\n                group-data-[idle=true]/root:opacity-0\n                group-data-[status=buffering]/root:opacity-100\n                group-data-[status=error]/root:opacity-100\n                group-data-[status=paused]/root:opacity-100\n              \"\n            />\n            <Layout.ControlsOverlayContainer\n              className=\"\n                bg-linear-[to_bottom,var(--lp-controls-fade-stops)] bg-size-[100%_45%] bg-bottom bg-no-repeat transition-opacity duration-300 ease-out\n                group-data-[idle=true]/root:opacity-0\n                group-data-[status=buffering]/root:opacity-100\n                group-data-[status=error]/root:opacity-100\n                group-data-[status=paused]/root:opacity-100\n              \"\n            />\n            <Layout.ControlsContainer\n              className={`\n                mx-auto w-full\n                @3xl/root:max-w-6xl\n                @5xl/root:max-w-[1728px]\n              `}\n            >\n              <TopOverlayContainer\n                className=\"\n                  px-[5%] pt-[clamp(0.75rem,7svh,4rem)] transition-[transform,opacity] duration-300 ease-[cubic-bezier(0.25,0.46,0.45,0.94)]\n                  group-data-[idle=false]/root:translate-y-0 group-data-[idle=false]/root:opacity-100\n                  group-data-[idle=true]/root:-translate-y-4 group-data-[idle=true]/root:opacity-0\n                  group-data-[status=buffering]/root:translate-y-0 group-data-[status=buffering]/root:opacity-100\n                  group-data-[status=error]/root:translate-y-0 group-data-[status=error]/root:opacity-100\n                  group-data-[status=paused]/root:translate-y-0 group-data-[status=paused]/root:opacity-100\n                  @3xl/root:px-[min(80px,10%)]\n                \"\n              />\n              <CaptionsContainer\n                className=\"\n                  px-[5%] pb-[clamp(0.5rem,4svh,1.5rem)]\n                  @3xl/root:px-[min(80px,10%)]\n                \"\n              />\n              <BottomControls\n                className=\"\n                  px-[5%] pb-[clamp(0.75rem,7svh,4rem)] transition-[transform,opacity] duration-300 ease-[cubic-bezier(0.25,0.46,0.45,0.94)]\n                  group-data-[idle=false]/root:translate-y-0 group-data-[idle=false]/root:opacity-100\n                  group-data-[idle=true]/root:translate-y-4 group-data-[idle=true]/root:opacity-0\n                  group-data-[status=buffering]/root:translate-y-0 group-data-[status=buffering]/root:opacity-100\n                  group-data-[status=error]/root:translate-y-0 group-data-[status=error]/root:opacity-100\n                  group-data-[status=paused]/root:translate-y-0 group-data-[status=paused]/root:opacity-100\n                  @3xl/root:px-[min(80px,10%)]\n                \"\n              />\n            </Layout.ControlsContainer>\n          </Layout.PlayerContainer>\n        </PlayerRootContainer>\n      </MediaProvider>\n    )\n  }\n)\n\nVideoPlayer.displayName = \"VideoPlayer\"\n\n// TODO: Fix this to FallbackPoster component with multisupport\nfunction CurrentAssetMedia({\n  className,\n  poster,\n  ...mediaProps\n}: NonNullable<VideoPlayerProps[\"mediaProps\"]>) {\n  const { currentItem } = useAsset<VideoPlayerAsset>()\n  const currentPoster = currentItem?.properties.poster\n\n  return (\n    <Media\n      {...(mediaProps as React.ComponentPropsWithoutRef<typeof Media>)}\n      as=\"video\"\n      className={cn(\"size-full bg-black object-contain\", className)}\n      poster={poster ?? currentPoster}\n    />\n  )\n}\n",
      "type": "registry:component",
      "target": "components/video-player/player.tsx"
    },
    {
      "path": "registry/default/blocks/video-player/styles.css",
      "content": ":root {\n  --lp-timeline-track-height: 4px;\n  --lp-timeline-track-height-active: 7px;\n\n  --lp-controls-fade-stops:\n    hsla(0, 0%, 91%, 0) 0%, hsla(0, 0%, 91%, 0.002) 10.6%,\n    hsla(0, 0%, 91%, 0.008) 19.7%, hsla(0, 0%, 91%, 0.019) 27.6%,\n    hsla(0, 0%, 91%, 0.035) 34.4%, hsla(0, 0%, 91%, 0.057) 40.4%,\n    hsla(0, 0%, 91%, 0.086) 45.7%, hsla(0, 0%, 91%, 0.122) 50.6%,\n    hsla(0, 0%, 91%, 0.165) 55.3%, hsla(0, 0%, 91%, 0.217) 60%,\n    hsla(0, 0%, 91%, 0.278) 65%, hsla(0, 0%, 91%, 0.349) 70.3%,\n    hsla(0, 0%, 91%, 0.43) 76.3%, hsla(0, 0%, 91%, 0.522) 83.1%,\n    hsla(0, 0%, 91%, 0.625) 90.9%, hsla(0, 0%, 91%, 0.74) 100%;\n}\n\n.dark {\n  --lp-controls-fade-stops:\n    hsla(0, 0%, 0%, 0) 0%, hsla(0, 0%, 0%, 0.009) 8.1%,\n    hsla(0, 0%, 0%, 0.035) 15.5%, hsla(0, 0%, 0%, 0.074) 22.5%,\n    hsla(0, 0%, 0%, 0.125) 29%, hsla(0, 0%, 0%, 0.184) 35.3%,\n    hsla(0, 0%, 0%, 0.25) 41.2%, hsla(0, 0%, 0%, 0.32) 47.1%,\n    hsla(0, 0%, 0%, 0.39) 52.9%, hsla(0, 0%, 0%, 0.46) 58.8%,\n    hsla(0, 0%, 0%, 0.526) 64.7%, hsla(0, 0%, 0%, 0.585) 71%,\n    hsla(0, 0%, 0%, 0.636) 77.5%, hsla(0, 0%, 0%, 0.675) 84.5%,\n    hsla(0, 0%, 0%, 0.701) 91.9%, hsla(0, 0%, 0%, 0.71) 100%;\n}\n",
      "type": "registry:style",
      "target": "components/video-player/styles.css"
    },
    {
      "path": "registry/default/blocks/video-player/components/player-root-container.tsx",
      "content": "\"use client\"\n\nimport React from \"react\"\n\nimport type { RootContainerProps } from \"@/registry/default/ui/root-container\"\n\nimport { cn } from \"@/lib/utils\"\nimport { useControlsVisibility } from \"@/registry/default/hooks/use-controls-visibility\"\nimport { RootContainer } from \"@/registry/default/ui/root-container\"\n\nexport type PlayerRootContainerLayout = \"aspect\" | \"fill\"\n\nexport interface PlayerRootContainerProps extends RootContainerProps {\n  /**\n   * Controls how the player frame gets its size.\n   *\n   * `aspect` keeps the default aspect-ratio driven player box.\n   * `fill` lets a height-constrained parent own the player box.\n   */\n  layout?: PlayerRootContainerLayout\n}\n\nexport const PlayerRootContainer = React.forwardRef<\n  HTMLDivElement,\n  PlayerRootContainerProps\n>(function PlayerRootContainer(props, ref) {\n  const { aspectRatio, className, layout = \"aspect\", ...rootProps } = props\n  const controlsVisibility = useControlsVisibility()\n\n  return (\n    <RootContainer\n      aria-label=\"Video Player\"\n      {...rootProps}\n      aspectRatio={layout === \"fill\" ? false : aspectRatio}\n      className={cn(\n        \"m-auto max-h-full min-h-0\",\n        layout === \"fill\" ? \"size-full\" : \"w-full\",\n        controlsVisibility.className,\n        className\n      )}\n      onBlur={composeEventHandlers(\n        rootProps.onBlur,\n        controlsVisibility.rootProps.onBlur\n      )}\n      onFocus={composeEventHandlers(\n        rootProps.onFocus,\n        controlsVisibility.rootProps.onFocus\n      )}\n      onPointerEnter={composeEventHandlers(\n        rootProps.onPointerEnter,\n        controlsVisibility.rootProps.onPointerEnter\n      )}\n      onPointerLeave={composeEventHandlers(\n        rootProps.onPointerLeave,\n        controlsVisibility.rootProps.onPointerLeave\n      )}\n      onPointerMove={composeEventHandlers(\n        rootProps.onPointerMove,\n        controlsVisibility.rootProps.onPointerMove\n      )}\n      onPointerOver={composeEventHandlers(\n        rootProps.onPointerOver,\n        controlsVisibility.rootProps.onPointerOver\n      )}\n      onPointerUp={composeEventHandlers(\n        rootProps.onPointerUp,\n        controlsVisibility.rootProps.onPointerUp\n      )}\n      ref={ref}\n    />\n  )\n})\n\nPlayerRootContainer.displayName = \"PlayerRootContainer\"\n\nfunction composeEventHandlers<TEvent extends React.SyntheticEvent>(\n  userHandler: ((event: TEvent) => void) | undefined,\n  internalHandler: ((event: TEvent) => void) | undefined\n) {\n  if (!userHandler) return internalHandler\n  if (!internalHandler) return userHandler\n\n  return (event: TEvent) => {\n    userHandler(event)\n    if (!event.defaultPrevented) internalHandler(event)\n  }\n}\n",
      "type": "registry:component",
      "target": "components/video-player/components/player-root-container.tsx"
    },
    {
      "path": "registry/default/blocks/video-player/components/player-error-screen.tsx",
      "content": "\"use client\"\n\nimport { RotateCwIcon } from \"lucide-react\"\nimport React from \"react\"\n\nimport type { VideoPlayerAsset } from \"@/registry/default/blocks/video-player/player\"\nimport type {\n  PlayerSource,\n  UseAssetOptions,\n} from \"@/registry/default/hooks/use-asset\"\n\nimport { Button } from \"@/registry/default/blocks/video-player/components/button\"\nimport { useAsset } from \"@/registry/default/hooks/use-asset\"\nimport { usePlaybackStore } from \"@/registry/default/hooks/use-playback\"\nimport { ErrorScreen } from \"@/registry/default/ui/error-screen\"\n\nexport interface PlayerErrorScreenProps {\n  initialIndex?: number\n  loading?: UseAssetOptions<VideoPlayerAsset>\n  source?: PlayerSource<VideoPlayerAsset>\n  sourceKey?: string\n}\n\nexport function PlayerErrorScreen({\n  initialIndex,\n  loading,\n  source,\n  sourceKey,\n}: PlayerErrorScreenProps) {\n  const error = usePlaybackStore((s) => s.error)\n  const status = usePlaybackStore((s) => s.status)\n  const { currentItem, loadAsset, loadSource } = useAsset<VideoPlayerAsset>()\n\n  const retryStream = React.useCallback(() => {\n    if (currentItem) {\n      void loadAsset(currentItem.properties)\n      return\n    }\n\n    if (source === undefined) return\n\n    loadSource(source, {\n      initialIndex,\n      loading,\n      sourceKey,\n    })\n  }, [\n    currentItem,\n    initialIndex,\n    loadAsset,\n    loading,\n    loadSource,\n    source,\n    sourceKey,\n  ])\n\n  if (status !== \"error\") return null\n\n  return (\n    <ErrorScreen className=\"z-10 rounded-lg\" error={error}>\n      {(currentItem || source !== undefined) && (\n        <Button onClick={retryStream} size=\"sm\">\n          <RotateCwIcon />\n          Retry\n        </Button>\n      )}\n    </ErrorScreen>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/video-player/components/player-error-screen.tsx"
    },
    {
      "path": "registry/default/blocks/video-player/components/top-overlay-container.tsx",
      "content": "\"use client\"\n\nimport type { VideoPlayerAsset } from \"@/registry/default/blocks/video-player/player\"\n\nimport { useAsset } from \"@/registry/default/hooks/use-asset\"\nimport { ControlsTopContainer } from \"@/registry/default/ui/player-layout\"\n\nexport interface TopOverlayContainerProps {\n  className?: string\n}\n\nexport function TopOverlayContainer({ className }: TopOverlayContainerProps) {\n  const { currentItem } = useAsset<VideoPlayerAsset>()\n  const asset = currentItem?.properties\n  const description = asset?.description\n  const title = asset?.title?.trim()\n\n  if (!title && !description) return null\n\n  return (\n    <ControlsTopContainer className={className}>\n      <div\n        className=\"\n          max-w-xs\n          @3xl/root:max-w-md\n        \"\n      >\n        {title && (\n          <h2\n            className=\"\n              line-clamp-2 text-sm/tight font-semibold tracking-normal text-balance text-foreground drop-shadow-[0_3px_18px_rgba(0,0,0,0.86)]\n              @3xl/root:text-lg/tight\n            \"\n          >\n            {title}\n          </h2>\n        )}\n        {description && (\n          <p\n            className=\"\n              mt-1 line-clamp-1 max-w-sm text-xs/relaxed text-pretty text-foreground/80 drop-shadow-[0_2px_14px_rgba(0,0,0,0.78)]\n              @3xl/root:line-clamp-2 @3xl/root:text-sm/relaxed\n            \"\n          >\n            {description}\n          </p>\n        )}\n      </div>\n    </ControlsTopContainer>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/video-player/components/top-overlay-container.tsx"
    },
    {
      "path": "registry/default/blocks/video-player/components/volume-state-control.tsx",
      "content": "\"use client\"\n\nimport {\n  SpeakerHighIcon,\n  SpeakerLowIcon,\n  SpeakerXIcon,\n} from \"@phosphor-icons/react\"\n\nimport { Button } from \"@/registry/default/blocks/video-player/components/button\"\nimport { useVolumeStore } from \"@/registry/default/hooks/use-volume\"\nimport { MuteControl } from \"@/registry/default/ui/mute-control\"\n\nexport function VolumeStateControl() {\n  const muted = useVolumeStore((state) => state.muted)\n  const volume = useVolumeStore((state) => state.level)\n\n  return (\n    <MuteControl asChild>\n      <Button className=\"cursor-pointer\" size=\"icon\" variant=\"glass\">\n        {muted || volume === 0 ? (\n          <SpeakerXIcon weight=\"fill\" />\n        ) : volume < 0.5 ? (\n          <SpeakerLowIcon weight=\"fill\" />\n        ) : (\n          <SpeakerHighIcon weight=\"fill\" />\n        )}\n      </Button>\n    </MuteControl>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/video-player/components/volume-state-control.tsx"
    },
    {
      "path": "registry/default/blocks/video-player/components/playback-state-control.tsx",
      "content": "\"use client\"\n\nimport {\n  CircleNotchIcon,\n  PauseIcon,\n  PlayIcon,\n  RepeatIcon,\n} from \"@phosphor-icons/react\"\n\nimport { Button } from \"@/registry/default/blocks/video-player/components/button\"\nimport { usePlaybackStore } from \"@/registry/default/hooks/use-playback\"\nimport { PlaybackControl } from \"@/registry/default/ui/playback-control\"\n\nexport function PlaybackStateControl() {\n  const status = usePlaybackStore((state) => state.status)\n\n  return (\n    <PlaybackControl asChild>\n      <Button className=\"cursor-pointer\" size=\"icon\" variant=\"glass\">\n        {status === \"playing\" ? (\n          <PauseIcon weight=\"fill\" />\n        ) : status === \"ended\" ? (\n          <RepeatIcon />\n        ) : status === \"buffering\" || status === \"loading\" ? (\n          <CircleNotchIcon className=\"animate-spin\" weight=\"bold\" />\n        ) : (\n          <PlayIcon weight=\"fill\" />\n        )}\n      </Button>\n    </PlaybackControl>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/video-player/components/playback-state-control.tsx"
    },
    {
      "path": "registry/default/blocks/video-player/components/volume-slider-control.tsx",
      "content": "import { cn } from \"@/lib/utils\"\nimport * as VolumeSlider from \"@/registry/default/ui/volume-control\"\n\nexport function HorizontalVolumeSliderControl() {\n  return (\n    <VolumeSliderControlRoot\n      className=\"\n        hit-area-x-[12px] hit-area-y-[2px] relative h-1 w-14 opacity-100\n        @3xl/root:hidden\n      \"\n      orientation=\"horizontal\"\n    />\n  )\n}\n\nexport function VerticalVolumeSliderControl() {\n  return (\n    <VolumeSliderControlRoot\n      className=\"\n        hit-area-x-[8px] hit-area-y-[12px] absolute bottom-full left-1/2 mb-2 hidden h-16 w-1 -translate-x-1/2 pb-1 opacity-0\n        @3xl/root:flex\n        @3xl/root:group-focus-within:opacity-100\n        @3xl/root:group-hover:opacity-100\n      \"\n      orientation=\"vertical\"\n    />\n  )\n}\n\nfunction VolumeSliderControlRoot({\n  className,\n  orientation,\n}: {\n  className?: string\n  orientation: \"horizontal\" | \"vertical\"\n}) {\n  return (\n    <VolumeSlider.Root\n      className={cn(\n        `hit-area z-10 cursor-crosshair rounded-md transition-opacity duration-300 ease-in-out`,\n        className\n      )}\n      orientation={orientation}\n    >\n      <VolumeSlider.Track>\n        <VolumeSlider.Progress />\n      </VolumeSlider.Track>\n      <VolumeSlider.Thumb className=\"size-2.5\" />\n    </VolumeSlider.Root>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/video-player/components/volume-slider-control.tsx"
    },
    {
      "path": "registry/default/blocks/video-player/components/timeline-slider-control.tsx",
      "content": "\"use client\"\n\nimport { useState } from \"react\"\n\nimport { Button } from \"@/registry/default/blocks/video-player/components/button\"\nimport { usePlayerStore } from \"@/registry/default/hooks/use-player\"\nimport { useTimelineStore } from \"@/registry/default/hooks/use-timeline\"\nimport * as TimelineSlider from \"@/registry/default/ui/timeline-control\"\nimport {\n  Duration,\n  Elapsed,\n  HoverTime,\n  LiveLatency,\n  Remaining,\n} from \"@/registry/default/ui/timeline-labels\"\n\nconst LIVE_DELAY_VISIBLE_SEC = 3\n\nexport function TimelineSliderControl() {\n  const [showRemaining, setShowRemaining] = useState(false)\n  const liveLatency = useTimelineStore((state) => state.liveLatency)\n  const isLive = useTimelineStore((state) => state.isLive)\n  const player = usePlayerStore((state) => state.instance)\n  const hasLiveLatency = liveLatency != null\n  const showGoToLive = hasLiveLatency && liveLatency >= LIVE_DELAY_VISIBLE_SEC\n  const showLiveBadge = hasLiveLatency && liveLatency < LIVE_DELAY_VISIBLE_SEC\n\n  return (\n    <div\n      className=\"\n        me-auto flex grow items-center gap-1.5 select-none\n        @3xl/root:gap-3\n      \"\n    >\n      {!isLive && <Elapsed className=\"px-2 text-xs font-medium\" />}\n      <div className=\"group/timeline relative w-full grow\">\n        <TimelineSlider.Root\n          className={`\n            group hit-area-x-[2px] hit-area-y-[14px] hit-area h-1 cursor-crosshair rounded-full transition-[height] duration-150 ease-in-out\n            active:data-[orientation=horizontal]:h-(--lp-timeline-track-height-active)\n          `}\n        >\n          <TimelineSlider.Track className=\"overflow-hidden rounded-full\">\n            <TimelineSlider.Progress className=\"rounded-s-full\" />\n            <TimelineSlider.Buffered variant=\"combined\" />\n          </TimelineSlider.Track>\n          <TimelineSlider.Thumb\n            className={`\n              absolute h-8 w-px rounded-full bg-primary/60 opacity-0 transition-opacity duration-300\n              group-hover/timeline:opacity-100\n              group-active/timeline:bg-white\n            `}\n            showWithHover\n          />\n          <TimelineSlider.Thumb\n            className={`\n              top-auto! bottom-[calc(100%+16px)] flex h-auto w-fit bg-transparent text-xs font-medium opacity-0 transition-opacity duration-300\n              group-hover/timeline:opacity-100\n            `}\n            showWithHover\n          >\n            {isLive && <>&minus;</>}\n            <HoverTime />\n            {!isLive && (\n              <>\n                &nbsp;/&nbsp;\n                <Duration className=\"text-primary/60\" />\n              </>\n            )}\n          </TimelineSlider.Thumb>\n        </TimelineSlider.Root>\n      </div>\n      {!isLive && (\n        <button\n          aria-label={\n            showRemaining ? \"Show total duration\" : \"Show remaining time\"\n          }\n          aria-pressed={showRemaining}\n          className=\"\n            inline-flex h-7 w-[6ch] cursor-pointer items-center justify-center rounded-sm\n            focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary/50 focus-visible:outline-solid\n          \"\n          onClick={() => {\n            setShowRemaining(!showRemaining)\n          }}\n          tabIndex={0}\n          type=\"button\"\n        >\n          {showRemaining ? (\n            <Remaining className=\"text-xs font-medium\" />\n          ) : (\n            <Duration className=\"text-xs font-medium\" />\n          )}\n        </button>\n      )}\n      {isLive && (\n        <>\n          {showGoToLive && player && (\n            <>\n              <LiveLatency className=\"text-xs font-medium\" />\n              <Button\n                aria-label=\"Go to live\"\n                className=\"\n                  cursor-pointer px-2\n                  @md/root:px-2.5\n                  @3xl/root:px-3\n                \"\n                onClick={() => void player.goToLive()}\n                size=\"sm\"\n                variant=\"glass\"\n              >\n                <span className=\"text-xs font-medium text-primary\">\n                  Go to live\n                </span>\n              </Button>\n            </>\n          )}\n          {showLiveBadge && (\n            <div className=\"flex items-center rounded-full bg-red-600 px-2 py-0.5 text-xs font-semibold tracking-wide\">\n              <span className=\"tracking-widest\">LIVE</span>\n            </div>\n          )}\n        </>\n      )}\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/video-player/components/timeline-slider-control.tsx"
    },
    {
      "path": "registry/default/blocks/video-player/components/bottom-controls.tsx",
      "content": "import { CaptionsStateControl } from \"@/registry/default/blocks/video-player/components/captions-state-control\"\nimport { PictureInPictureControl } from \"@/registry/default/blocks/video-player/components/pip-control\"\nimport { PlaybackStateControl } from \"@/registry/default/blocks/video-player/components/playback-state-control\"\nimport { Playlist } from \"@/registry/default/blocks/video-player/components/playlist\"\nimport { PlaylistNextControl } from \"@/registry/default/blocks/video-player/components/playlist-navigation-controls\"\nimport { TimelineSliderControl } from \"@/registry/default/blocks/video-player/components/timeline-slider-control\"\nimport { VolumeGroupControl } from \"@/registry/default/blocks/video-player/components/volume-group-control\"\nimport { CONTROLS_FORCE_VISIBLE_ATTRIBUTE } from \"@/registry/default/hooks/use-controls-visibility\"\nimport * as Layout from \"@/registry/default/ui/player-layout\"\n\nexport interface BottomControlsProps {\n  className?: string\n}\n\nexport function BottomControls({ className }: BottomControlsProps) {\n  return (\n    <Layout.ControlsBottomContainer\n      className={className}\n      {...{ [CONTROLS_FORCE_VISIBLE_ATTRIBUTE]: \"\" }}\n    >\n      <div\n        className=\"\n          grid grid-cols-[1fr_auto] items-center gap-1\n          @3xl/root:grid-cols-[1fr_3fr_1fr] @3xl/root:gap-2\n        \"\n      >\n        <div\n          className=\"\n            col-start-1 row-start-2 flex flex-row items-center gap-2 justify-self-start\n            @3xl/root:col-start-1 @3xl/root:row-start-1\n          \"\n        >\n          <PlaybackStateControl />\n          <PlaylistNextControl />\n          <VolumeGroupControl />\n        </div>\n\n        <div\n          className=\"\n            col-span-2 col-start-1 row-start-1 w-full\n            @3xl/root:col-span-1 @3xl/root:col-start-2 @3xl/root:row-start-1\n          \"\n        >\n          <TimelineSliderControl />\n        </div>\n\n        <div\n          className=\"\n            col-start-2 row-start-2 flex flex-row items-center gap-2 justify-self-end\n            @3xl/root:col-start-3 @3xl/root:row-start-1\n          \"\n        >\n          <CaptionsStateControl />\n          <PictureInPictureControl />\n          <Playlist />\n        </div>\n      </div>\n    </Layout.ControlsBottomContainer>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/video-player/components/bottom-controls.tsx"
    },
    {
      "path": "registry/default/blocks/video-player/components/media-session-controller.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport type { VideoPlayerAsset } from \"@/registry/default/blocks/video-player/player\"\n\nimport { useMediaApi } from \"@/registry/default/blocks/video-player/lib/media-kit\"\nimport { useAsset, useAssetStore } from \"@/registry/default/hooks/use-asset\"\nimport { useMediaStore } from \"@/registry/default/hooks/use-media\"\nimport {\n  canMoveToNextMediaSessionTrack,\n  canMoveToPreviousMediaSessionTrack,\n  canStartMediaSessionPlayback,\n  getMediaSessionCurrentTime,\n  getMediaSessionMetadataValue,\n  getMediaSessionPlaybackState,\n  getMediaSessionPositionState,\n  isMediaSessionPlaylistSource,\n  useMediaSessionActionHandlers,\n  useMediaSessionSync,\n} from \"@/registry/default/hooks/use-media-session\"\nimport {\n  canUsePictureInPicture,\n  usePictureInPictureStore,\n} from \"@/registry/default/hooks/use-picture-in-picture\"\nimport { usePlaybackStore } from \"@/registry/default/hooks/use-playback\"\nimport { usePlaybackRateStore } from \"@/registry/default/hooks/use-playback-rate\"\nimport { useTimelineStore } from \"@/registry/default/hooks/use-timeline\"\n\nexport function VideoMediaSessionController() {\n  const mediaApi = useMediaApi()\n  const { currentItem, hasNext, hasPrevious } = useAsset<VideoPlayerAsset>()\n  const currentTime = useTimelineStore((state) => state.currentTime)\n  const duration = useTimelineStore((state) => state.duration)\n  const pictureInPictureSupported = usePictureInPictureStore(\n    (state) => state.supported\n  )\n  const mediaElement = useMediaStore((state) => state.mediaElement)\n  const playbackRate = usePlaybackRateStore((state) => state.value)\n  const sourceType = useAssetStore((state) => state.sourceType)\n  const status = usePlaybackStore((state) => state.status)\n  const asset = currentItem?.properties ?? null\n  const active = asset !== null\n  const playlistSource = isMediaSessionPlaylistSource(sourceType)\n  const canEnterPictureInPicture =\n    active &&\n    canUsePictureInPicture({\n      mediaElement,\n      supported: pictureInPictureSupported,\n    })\n\n  const metadata = React.useMemo(\n    () => (asset ? getVideoMediaSessionMetadata(asset) : null),\n    [asset]\n  )\n  const position = React.useMemo(\n    () =>\n      status === \"loading\"\n        ? null\n        : getMediaSessionPositionState({\n            active,\n            currentTime,\n            duration,\n            playbackRate,\n          }),\n    [active, currentTime, duration, playbackRate, status]\n  )\n\n  const actions = useMediaSessionActionHandlers({\n    canEnterPictureInPicture,\n    canGoNext: playlistSource && hasNext,\n    canGoPrevious: playlistSource && hasPrevious,\n    getCurrentTime: () => {\n      const state = mediaApi.getState()\n\n      return getMediaSessionCurrentTime({\n        currentTime: state.timeline.currentTime,\n        isLive: state.timeline.isLive,\n        mediaElement: state.media.mediaElement,\n      })\n    },\n    getSeekRange: () => {\n      const state = mediaApi.getState()\n\n      return state.timeline.isLive ? state.player.instance?.seekRange() : null\n    },\n    onEnterPictureInPicture: () => {\n      void mediaApi.getState().pictureInPicture.enter()\n    },\n    onNextTrack: () => {\n      const state = mediaApi.getState()\n      if (\n        canMoveToNextMediaSessionTrack(state.asset.sourceType, state.playlist)\n      ) {\n        state.playlist.next()\n      }\n    },\n    onPause: () => {\n      mediaApi.getState().playback.pause()\n    },\n    onPlay: () => {\n      const state = mediaApi.getState()\n      if (!canStartMediaSessionPlayback(state.playback.status)) return\n\n      return state.playback.play()\n    },\n    onPreviousTrack: () => {\n      const state = mediaApi.getState()\n      if (\n        canMoveToPreviousMediaSessionTrack(\n          state.asset.sourceType,\n          state.playlist\n        )\n      ) {\n        state.playlist.previous()\n      }\n    },\n    onSeek: (time) => {\n      mediaApi.getState().timeline.seek(time)\n    },\n  })\n\n  useMediaSessionSync({\n    actions,\n    active,\n    claim: status === \"playing\",\n    metadata,\n    playbackState: getMediaSessionPlaybackState({ active, status }),\n    position,\n    positionKey: currentItem?.id ?? null,\n  })\n\n  return null\n}\n\nfunction getVideoMediaSessionMetadata(\n  asset: VideoPlayerAsset\n): MediaMetadataInit | null {\n  const title = getMediaSessionMetadataValue(asset.title)\n  const artist = getMediaSessionMetadataValue(asset.description, asset.year)\n  const poster = getMediaSessionMetadataValue(asset.poster)\n\n  if (!title && !artist && !poster) return null\n\n  return {\n    artist,\n    artwork: poster ? [{ sizes: \"512x512\", src: poster }] : [],\n    title,\n  }\n}\n",
      "type": "registry:component",
      "target": "components/video-player/components/media-session-controller.tsx"
    },
    {
      "path": "registry/default/blocks/video-player/components/button.tsx",
      "content": "import { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n  `\n    inline-flex shrink-0 cursor-pointer items-center justify-center gap-1.5 rounded-md text-xs font-medium whitespace-nowrap\n    transition-[background-color,color,box-shadow,scale,backdrop-filter] duration-150 ease-out\n    focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary/50\n    active:scale-[0.96]\n    disabled:pointer-events-none disabled:opacity-50\n    motion-reduce:transition-none\n    motion-reduce:active:scale-100\n    @md/root:gap-2 @md/root:text-sm\n    [&_svg]:pointer-events-none [&_svg]:shrink-0\n    [&_svg:not([class*='size-'])]:size-3.5\n    @md/root:[&_svg:not([class*='size-'])]:size-4\n    @3xl/root:[&_svg:not([class*='size-'])]:size-4.5\n  `,\n  {\n    defaultVariants: {\n      size: \"default\",\n      variant: \"default\",\n    },\n    variants: {\n      size: {\n        default: `\n          h-8 px-3 py-1.5\n          has-[>svg]:px-2.5\n          @md/root:h-9 @md/root:px-4 @md/root:py-2\n          @md/root:has-[>svg]:px-3\n          @3xl/root:h-10 @3xl/root:px-5\n          @3xl/root:has-[>svg]:px-4\n        `,\n        icon: `\n          size-7 rounded-lg p-1.5\n          @md/root:size-8 @md/root:p-2\n          @3xl/root:size-9\n        `,\n        lg: `\n          h-9 rounded-md px-4\n          has-[>svg]:px-3\n          @md/root:h-10 @md/root:px-6\n          @md/root:has-[>svg]:px-4\n          @3xl/root:h-11 @3xl/root:px-7\n          @3xl/root:has-[>svg]:px-5\n        `,\n        sm: `\n          h-7 gap-1.5 rounded-md px-2.5 text-xs\n          has-[>svg]:px-2\n          @md/root:h-8 @md/root:px-3 @md/root:text-sm\n          @md/root:has-[>svg]:px-2.5\n          @3xl/root:h-9 @3xl/root:px-3.5\n          @3xl/root:has-[>svg]:px-3\n        `,\n      },\n      variant: {\n        default: `\n          bg-primary text-primary-foreground\n          hover:bg-primary/90\n        `,\n        ghost: `hover:bg-accent hover:text-accent-foreground`,\n        glass: `\n          bg-transparent text-foreground\n          hover:bg-foreground/10\n          focus-visible:bg-background/20\n        `,\n        link: `\n          text-primary underline-offset-4\n          hover:underline\n        `,\n        outline: `\n          border border-input bg-background\n          hover:bg-accent hover:text-accent-foreground\n        `,\n        secondary: `\n          bg-secondary text-secondary-foreground\n          hover:bg-secondary/80\n        `,\n      },\n    },\n  }\n)\n\nexport interface ButtonProps\n  extends\n    React.ButtonHTMLAttributes<HTMLButtonElement>,\n    VariantProps<typeof buttonVariants> {\n  asChild?: boolean\n  render?: React.ReactElement\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n  (\n    { asChild = false, children, className, render, size, variant, ...props },\n    ref\n  ) => {\n    const Comp = render ? Slot : asChild ? Slot : \"button\"\n    return (\n      <Comp\n        className={cn(buttonVariants({ className, size, variant }))}\n        data-slot=\"button\"\n        ref={ref}\n        {...props}\n      >\n        {render ? React.cloneElement(render, undefined, children) : children}\n      </Comp>\n    )\n  }\n)\n\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n",
      "type": "registry:component",
      "target": "components/video-player/components/button.tsx"
    },
    {
      "path": "registry/default/blocks/video-player/lib/media-kit.ts",
      "content": "\"use client\"\n\nimport { assetFeature } from \"@/registry/default/hooks/use-asset\"\nimport { captionsFeature } from \"@/registry/default/hooks/use-captions\"\nimport { mediaFeature } from \"@/registry/default/hooks/use-media\"\nimport { pictureInPictureFeature } from \"@/registry/default/hooks/use-picture-in-picture\"\nimport { playbackFeature } from \"@/registry/default/hooks/use-playback\"\nimport { playbackRateFeature } from \"@/registry/default/hooks/use-playback-rate\"\nimport { playerFeature } from \"@/registry/default/hooks/use-player\"\nimport { playlistFeature } from \"@/registry/default/hooks/use-playlist\"\nimport { timelineFeature } from \"@/registry/default/hooks/use-timeline\"\nimport { volumeFeature } from \"@/registry/default/hooks/use-volume\"\nimport { createMediaKit } from \"@/registry/default/ui/media-provider\"\n\nexport const media = createMediaKit({\n  features: [\n    mediaFeature(),\n    playerFeature(),\n    playbackFeature(),\n    playlistFeature(),\n    volumeFeature(),\n    timelineFeature(),\n    captionsFeature(),\n    playbackRateFeature(),\n    pictureInPictureFeature(),\n    assetFeature(),\n  ] as const,\n})\n\nexport const MediaProvider = media.MediaProvider\nexport const useMediaApi = media.useMediaApi\nexport const useMediaEvents = media.useMediaEvents\n",
      "type": "registry:lib",
      "target": "components/video-player/lib/media-kit.ts"
    },
    {
      "path": "registry/default/blocks/video-player/components/playlist.tsx",
      "content": "\"use client\"\n\nimport { CardsThreeIcon, PlayIcon } from \"@phosphor-icons/react\"\nimport { useEffect, useMemo } from \"react\"\n\nimport type { VideoPlayerAsset } from \"@/registry/default/blocks/video-player/player\"\n\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\"\nimport { Button } from \"@/registry/default/blocks/video-player/components/button\"\nimport { useMediaStore } from \"@/registry/default/hooks/use-media\"\nimport { usePlayerStore } from \"@/registry/default/hooks/use-player\"\nimport { usePlaylistStore } from \"@/registry/default/hooks/use-playlist\"\n\nexport function Playlist() {\n  const currentItem = usePlaylistStore(\n    (state) =>\n      state.currentItem as null | { id: string; properties: VideoPlayerAsset }\n  )\n  const containerRef = usePlayerStore((state) => state.containerRef)\n  const setForceIdle = useMediaStore((state) => state.setForceIdle)\n  const queue = usePlaylistStore(\n    (state) => state.queue as { id: string; properties: VideoPlayerAsset }[]\n  )\n  const shuffle = usePlaylistStore((state) => state.shuffle)\n  const shuffleOrder = usePlaylistStore((state) => state.shuffleOrder)\n  const skipToId = usePlaylistStore((state) => state.skipToId)\n\n  const orderedItems = useMemo(() => {\n    if (!shuffle || shuffleOrder.length === 0) return queue\n\n    return shuffleOrder\n      .map((index) => queue[index])\n      .filter((item): item is { id: string; properties: VideoPlayerAsset } =>\n        Boolean(item)\n      )\n  }, [queue, shuffle, shuffleOrder])\n\n  useEffect(() => {\n    return () => {\n      setForceIdle(false)\n    }\n  }, [setForceIdle])\n\n  if (orderedItems.length < 2) return null\n\n  const handleAssetSelect = async (assetId: string) => {\n    await skipToId(assetId)\n  }\n\n  const dropdownCollisionProps: {\n    collisionBoundary?: Element\n    collisionPadding?: number\n  } = {\n    collisionBoundary: containerRef ?? undefined,\n    collisionPadding: 12,\n  }\n\n  return (\n    <DropdownMenu onOpenChange={setForceIdle}>\n      <Button aria-label=\"Open Playlist\" asChild size=\"icon\" variant=\"glass\">\n        <DropdownMenuTrigger>\n          <CardsThreeIcon weight=\"fill\" />\n        </DropdownMenuTrigger>\n      </Button>\n      <DropdownMenuContent\n        align=\"end\"\n        alignOffset={-12}\n        className=\"dark w-sm border border-border p-2\"\n        side=\"top\"\n        sideOffset={24}\n        {...dropdownCollisionProps}\n      >\n        <DropdownMenuGroup>\n          <DropdownMenuLabel>Playlist</DropdownMenuLabel>\n          <DropdownMenuSeparator />\n          <div className=\"space-y-1\">\n            {orderedItems.map((item) => {\n              const asset = item.properties\n              const isCurrentAsset = currentItem?.id === item.id\n\n              return (\n                <DropdownMenuItem\n                  className={`\n                    dark cursor-pointer rounded-md p-0 transition-colors\n                    focus:bg-accent/50 focus:outline-none\n                    ${\n                      isCurrentAsset\n                        ? \"border-primary/20 bg-primary/10\"\n                        : \"hover:bg-accent/50\"\n                    }\n                  `}\n                  key={item.id}\n                  onClick={() => handleAssetSelect(item.id)}\n                >\n                  <div className=\"flex w-full items-center gap-3 p-2\">\n                    <div className=\"relative aspect-video w-20 shrink-0 overflow-hidden rounded-sm\">\n                      <img\n                        alt={asset.title ?? \"Playlist item poster\"}\n                        className=\"object-cover\"\n                        loading=\"lazy\"\n                        sizes=\"80px\"\n                        src={asset.poster}\n                      />\n                      {isCurrentAsset && (\n                        <div className=\"absolute inset-0 flex items-center justify-center bg-black/40\">\n                          <PlayIcon\n                            className=\"size-6 text-white\"\n                            weight=\"fill\"\n                          />\n                        </div>\n                      )}\n                    </div>\n                    <div className=\"min-w-0 flex-1\">\n                      <div className=\"flex w-full items-center gap-2\">\n                        <div className=\"min-w-0 flex-1 truncate text-sm font-medium\">\n                          {asset.title}\n                        </div>\n                        {isCurrentAsset && (\n                          <div className=\"size-2 shrink-0 rounded-full bg-primary\" />\n                        )}\n                        <span\n                          className=\"ml-auto shrink-0 text-[11px] text-muted-foreground tabular-nums\"\n                          title={asset.year}\n                        >\n                          {asset.year}\n                        </span>\n                      </div>\n                      {asset.description && (\n                        <div className=\"line-clamp-2 text-[11px] text-muted-foreground\">\n                          {asset.description}\n                        </div>\n                      )}\n                    </div>\n                  </div>\n                </DropdownMenuItem>\n              )\n            })}\n          </div>\n        </DropdownMenuGroup>\n      </DropdownMenuContent>\n    </DropdownMenu>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/video-player/components/playlist.tsx"
    },
    {
      "path": "registry/default/blocks/video-player/components/playlist-navigation-controls.tsx",
      "content": "\"use client\"\n\nimport { SkipForwardIcon } from \"@phosphor-icons/react\"\n\nimport { Button } from \"@/registry/default/blocks/video-player/components/button\"\nimport {\n  AssetSourceType,\n  useAssetStore,\n} from \"@/registry/default/hooks/use-asset\"\nimport { usePlaylistStore } from \"@/registry/default/hooks/use-playlist\"\n\nexport function PlaylistNextControl() {\n  const next = usePlaylistStore((state) => state.next)\n  const hasPlaylist = useAssetStore(\n    (state) => state.sourceType === AssetSourceType.Playlist\n  )\n  const hasNext = usePlaylistStore((state) => {\n    if (state.repeatMode === \"all\" && state.queue.length > 0) return true\n\n    return state.getNextIndex() !== -1\n  })\n\n  if (!hasPlaylist) return null\n\n  return (\n    <Button\n      aria-label=\"Next video\"\n      disabled={!hasNext}\n      onClick={next}\n      size=\"icon\"\n      variant=\"glass\"\n    >\n      <SkipForwardIcon weight=\"fill\" />\n    </Button>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/video-player/components/playlist-navigation-controls.tsx"
    },
    {
      "path": "registry/default/blocks/video-player/components/captions-state-control.tsx",
      "content": "\"use client\"\n\nimport { ClosedCaptioningIcon } from \"@phosphor-icons/react\"\n\nimport { Button } from \"@/registry/default/blocks/video-player/components/button\"\nimport { useCaptionsStore } from \"@/registry/default/hooks/use-captions\"\nimport { CaptionsControl } from \"@/registry/default/ui/captions\"\n\nexport function CaptionsStateControl() {\n  const textTrackVisible = useCaptionsStore((state) => state.visible)\n\n  return (\n    <CaptionsControl asChild>\n      <Button className=\"cursor-pointer\" size=\"icon\" variant=\"glass\">\n        <ClosedCaptioningIcon weight={textTrackVisible ? \"fill\" : \"bold\"} />\n      </Button>\n    </CaptionsControl>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/video-player/components/captions-state-control.tsx"
    },
    {
      "path": "registry/default/blocks/video-player/components/playback-rate-control.tsx",
      "content": "\"use client\"\n\nimport React from \"react\"\n\nimport * as Select from \"@/components/ui/select\"\nimport * as PlaybackRate from \"@/registry/default/ui/playback-rate\"\n\nimport { Button } from \"./button\"\n\nexport function PlaybackRateControl() {\n  return (\n    <PlaybackRate.SelectRoot>\n      <Button\n        asChild\n        className=\"\n          border-none bg-transparent shadow-none\n          hover:bg-foreground/10\n          dark:bg-transparent dark:shadow-none\n        \"\n        variant=\"glass\"\n      >\n        <Select.SelectTrigger>\n          <Select.SelectValue />\n        </Select.SelectTrigger>\n      </Button>\n      <Select.SelectContent\n        align=\"start\"\n        className={`min-w-28 border border-border backdrop-blur-lg`}\n        side=\"top\"\n        sideOffset={12}\n        {...({\n          alignItemWithTrigger: false,\n          position: \"popper\",\n        } as React.ComponentProps<typeof Select.SelectContent>)}\n      >\n        <PlaybackRate.SelectGroup className={`tracking-wider`}>\n          <Select.SelectLabel className=\"whitespace-nowrap\">\n            Playback Rate\n          </Select.SelectLabel>\n        </PlaybackRate.SelectGroup>\n      </Select.SelectContent>\n    </PlaybackRate.SelectRoot>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/video-player/components/playback-rate-control.tsx"
    },
    {
      "path": "registry/default/blocks/video-player/ui/toggle.tsx",
      "content": "\"use client\"\n\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst toggleVariants = cva(\n  `\n    inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow]\n    hover:bg-muted\n    focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-foreground/50\n    disabled:pointer-events-none disabled:opacity-50\n    aria-invalid:border-destructive aria-invalid:ring-destructive/20\n    data-[state=on]:bg-accent data-[state=on]:text-accent-foreground\n    dark:aria-invalid:ring-destructive/40\n    [&_svg]:pointer-events-none [&_svg]:shrink-0\n    [&_svg:not([class*='size-'])]:size-4\n  `,\n  {\n    defaultVariants: {\n      size: \"default\",\n      variant: \"default\",\n    },\n    variants: {\n      size: {\n        default: \"h-9 min-w-9 px-2\",\n        lg: \"h-10 min-w-10 px-2.5\",\n        sm: \"h-8 min-w-8 px-1.5\",\n      },\n      variant: {\n        default: \"bg-transparent\",\n        glass: `\n          bg-transparent text-primary\n          hover:bg-primary/10\n          active:scale-[0.97]\n          data-[state=on]:bg-transparent\n          data-[state=on]:hover:bg-primary/10\n        `,\n        outline: `\n          border border-input bg-transparent shadow-xs\n          hover:bg-accent hover:text-accent-foreground\n        `,\n      },\n    },\n  }\n)\n\nfunction Toggle({\n  className,\n  size,\n  variant,\n  ...props\n}: React.ComponentProps<typeof TogglePrimitive.Root> &\n  VariantProps<typeof toggleVariants>) {\n  return (\n    <TogglePrimitive.Root\n      className={cn(toggleVariants({ className, size, variant }))}\n      data-slot=\"toggle\"\n      {...props}\n    />\n  )\n}\n\nexport { Toggle, toggleVariants }\n",
      "type": "registry:ui",
      "target": "components/ui/toggle.tsx"
    },
    {
      "path": "registry/default/blocks/video-player/components/volume-group-control.tsx",
      "content": "import { cn } from \"@/lib/utils\"\nimport {\n  HorizontalVolumeSliderControl,\n  VerticalVolumeSliderControl,\n} from \"@/registry/default/blocks/video-player/components/volume-slider-control\"\nimport { VolumeStateControl } from \"@/registry/default/blocks/video-player/components/volume-state-control\"\n\nexport function VolumeGroupControl() {\n  return (\n    <div\n      className={cn(`\n        group relative isolate me-2 flex min-w-24 flex-row items-center gap-1.5 rounded-md pe-3\n        @3xl/root:me-0 @3xl/root:min-w-0 @3xl/root:flex-col-reverse @3xl/root:gap-1 @3xl/root:pe-0\n      `)}\n    >\n      <div\n        aria-hidden\n        className={cn(`\n          pointer-events-none absolute inset-0 z-0 rounded-md opacity-0 transition-opacity duration-300 ease-in-out\n          group-focus-within:bg-foreground/10 group-focus-within:opacity-100\n          group-hover:bg-foreground/10 group-hover:opacity-100\n          @3xl/root:-top-22\n        `)}\n      />\n      <div className=\"relative z-10\">\n        <VolumeStateControl />\n      </div>\n      <HorizontalVolumeSliderControl />\n      <VerticalVolumeSliderControl />\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/video-player/components/volume-group-control.tsx"
    },
    {
      "path": "registry/default/blocks/video-player/components/pip-control.tsx",
      "content": "\"use client\"\n\nimport { PictureInPictureIcon } from \"@phosphor-icons/react\"\n\nimport { Button } from \"@/registry/default/blocks/video-player/components/button\"\nimport { usePictureInPictureStore } from \"@/registry/default/hooks/use-picture-in-picture\"\nimport { PictureInPictureControl as PictureInPictureControlPrimitive } from \"@/registry/default/ui/picture-in-picture-control\"\n\nexport function PictureInPictureControl() {\n  const isPictureInPictureActive = usePictureInPictureStore(\n    (state) => state.active\n  )\n\n  return (\n    <PictureInPictureControlPrimitive asChild shortcut=\"P\">\n      <Button className=\"cursor-pointer\" size=\"icon\" variant=\"glass\">\n        <PictureInPictureIcon\n          weight={isPictureInPictureActive ? \"fill\" : \"bold\"}\n        />\n      </Button>\n    </PictureInPictureControlPrimitive>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/video-player/components/pip-control.tsx"
    }
  ],
  "meta": {
    "iframeHeight": "750px",
    "props": {
      "source": "https://ad391cc0d55b44c6a86d232548adc225.mediatailor.us-east-1.amazonaws.com/v1/master/d02fedbbc5a68596164208dd24e9b48aa60dadc7/singssai/master.m3u8"
    }
  },
  "type": "registry:block"
}