{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "audio",
  "title": "Default Audio Skin",
  "description": "A complete on-demand audio skin with playback, seeking, volume, speed, and feedback controls.",
  "dependencies": [
    "@videojs/core@10.0.0-beta.32",
    "@videojs/react@10.0.0-beta.32",
    "react"
  ],
  "registryDependencies": [
    "@videojs/_button-tooltip",
    "@videojs/_resolve-class-name",
    "@videojs/_style-theme",
    "@videojs/buffering-indicator",
    "@videojs/button",
    "@videojs/container",
    "@videojs/play-button",
    "@videojs/playback-rate-button",
    "@videojs/radio-item",
    "@videojs/seek-button",
    "@videojs/slider",
    "@videojs/status-announcer",
    "@videojs/volume-popover"
  ],
  "files": [
    {
      "path": "skins/files/audio/skins/audio/controls.tsx",
      "content": "/** @jsxImportSource react */\n\nimport { Controls, Time, Tooltip } from '@videojs/react';\n\nimport { cn } from '@/components/videojs/lib/resolve-class-name';\nimport { ButtonTooltip } from '@/components/videojs/ui/button-tooltip';\nimport { SeekButton } from '@/components/videojs/ui/seek-button';\nimport { VolumePopover } from '@/components/videojs/ui/volume-popover';\n\nimport { AudioPlayButton } from './play-button';\nimport { AudioSettingsMenu } from './settings-menu';\nimport { AudioTimeSlider } from './time-slider';\n\nexport function DefaultAudioControls() {\n  return (\n    <Controls.Root visibility=\"always\">\n      <Controls.Content\n        className={cn(\n          'audio-controls',\n          'relative z-20 flex items-center rounded-media-controls bg-media-controls p-1 text-media-controls-foreground text-shadow-media',\n          'surface-media after:surface-media-inset'\n        )}\n      >\n        <Tooltip.Provider>\n          <Controls.Group className={'flex items-center gap-px'}>\n            <AudioPlayButton />\n            <ButtonTooltip boundary=\"viewport\" side=\"top\">\n              <SeekButton className={'media-max-compact:hidden'} seconds={-10} />\n            </ButtonTooltip>\n            <ButtonTooltip boundary=\"viewport\" side=\"top\">\n              <SeekButton className={'media-max-compact:hidden'} seconds={10} />\n            </ButtonTooltip>\n          </Controls.Group>\n\n          <Controls.Group className={'@container/audio-time-controls flex min-w-0 flex-1 items-center gap-2.5 px-3'}>\n            <Time.Value className={'tabular-nums'} type=\"current\" />\n            <AudioTimeSlider previewOverflow=\"visible\" />\n            <Time.Value\n              className={cn(\n                'cursor-pointer rounded-sm tabular-nums focus-ring-media',\n                'transition-[outline-color,outline-offset] duration-media-fast ease-out',\n                'focus-visible:outline-media-ring focus-visible:outline-offset-2',\n                '@max-[16rem]/audio-time-controls:hidden'\n              )}\n              type=\"remaining\"\n              toggle\n            />\n          </Controls.Group>\n\n          <Controls.Group className={'flex items-center gap-px'}>\n            <AudioSettingsMenu />\n            <VolumePopover boundary=\"viewport\" />\n          </Controls.Group>\n        </Tooltip.Provider>\n      </Controls.Content>\n    </Controls.Root>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/videojs/skins/audio/controls.tsx"
    },
    {
      "path": "skins/files/audio/skins/audio/error-dialog.tsx",
      "content": "/** @jsxImportSource react */\n\nimport { ErrorDialog } from '@videojs/react';\n\nimport { Button } from '@/components/videojs/ui/button';\n\nexport function AudioErrorDialog() {\n  return (\n    <ErrorDialog.Root>\n      <ErrorDialog.Backdrop className={'hidden'} />\n      <ErrorDialog.Popup\n        className={\n          'absolute inset-0 z-50 flex h-full max-h-none w-full translate-none flex-row items-center rounded-media-pill py-0 pe-1 outline-hidden not-data-open:hidden bg-media-background text-media-controls-foreground backdrop-filter-media-dialog transition-[opacity,filter] duration-media-slower ease-out media-transitioning:blur-media-hidden-popup media-transitioning:opacity-0 gap-3 px-5'\n        }\n      >\n        <div className={'flex min-h-0 flex-1 flex-row items-center gap-2 overflow-visible'}>\n          <ErrorDialog.Title className={'m-0 text-media font-semibold leading-tight'} />\n          <ErrorDialog.Description className={'m-0 opacity-70 wrap-anywhere'} />\n        </div>\n        <div className={'flex shrink-0 gap-2'}>\n          <ErrorDialog.Close\n            render={<Button />}\n            className={\n              'h-media-control w-auto flex-none bg-media-primary! px-3 font-medium text-media-primary-foreground!'\n            }\n          />\n        </div>\n      </ErrorDialog.Popup>\n    </ErrorDialog.Root>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/videojs/skins/audio/error-dialog.tsx"
    },
    {
      "path": "skins/files/audio/skins/audio/play-button.tsx",
      "content": "/** @jsxImportSource react */\n\nimport type { ComponentProps } from 'react';\n\nimport { cn } from '@/components/videojs/lib/resolve-class-name';\nimport { BufferingIndicator } from '@/components/videojs/ui/buffering-indicator';\nimport { ButtonTooltip } from '@/components/videojs/ui/button-tooltip';\nimport { PlayButton } from '@/components/videojs/ui/play-button';\n\nexport type AudioPlayButtonProps = Omit<ComponentProps<'div'>, 'children'>;\n\nexport function AudioPlayButton({ className, ...props }: AudioPlayButtonProps = {}) {\n  return (\n    <div className={cn('relative inline-flex', className)} {...props}>\n      <BufferingIndicator\n        className={'z-20 rounded-media-control text-inherit! before:hidden data-visible:bg-media-controls'}\n      />\n      <ButtonTooltip boundary=\"viewport\" side=\"top\">\n        <PlayButton />\n      </ButtonTooltip>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/videojs/skins/audio/play-button.tsx"
    },
    {
      "path": "skins/files/audio/skins/audio/settings-menu.tsx",
      "content": "/** @jsxImportSource react */\n\nimport { Menu as MenuPrimitive } from '@videojs/react';\nimport { PlaybackRateRadioGroup } from '@videojs/react/ui/playback-rate-radio-group';\n\nimport { cn } from '@/components/videojs/lib/resolve-class-name';\nimport { ButtonTooltip } from '@/components/videojs/ui/button-tooltip';\nimport { PlaybackRateButton } from '@/components/videojs/ui/playback-rate-button';\nimport { RadioItem } from '@/components/videojs/ui/radio-item';\n\nexport interface AudioSettingsMenuProps extends Omit<MenuPrimitive.RootProps, 'children'> {\n  className?: MenuPrimitive.TriggerProps['className'];\n}\n\nexport function AudioSettingsMenu({ className, ...props }: AudioSettingsMenuProps = {}) {\n  return (\n    <MenuPrimitive.Root side=\"top\" align=\"center\" boundary=\"viewport\" {...props}>\n      <PlaybackRateRadioGroup.Root>\n        <ButtonTooltip boundary=\"viewport\" side=\"top\">\n          <MenuPrimitive.Trigger render={<PlaybackRateButton />} className={className} />\n        </ButtonTooltip>\n        <MenuPrimitive.Popup\n          className={cn(\n            'm-0 overflow-visible border-0 text-inherit',\n            'media-transitioning:opacity-0 media-transitioning:blur-media-hidden-popup media-transitioning:scale-media-hidden-popup',\n            'data-starting-style:[transform:translate(var(--media-popup-translate-x-distance,0),var(--media-popup-translate-y-distance,0))]',\n            'data-ending-style:transform-none',\n            'data-[side=top]:origin-bottom data-[side=bottom]:origin-top data-[side=left]:origin-right data-[side=right]:origin-left',\n            'data-[side=top]:[--media-popup-translate-y-distance:var(--media-popup-translate-distance)]',\n            'data-[side=bottom]:[--media-popup-translate-y-distance:calc(var(--media-popup-translate-distance)*-1)]',\n            'data-[side=left]:[--media-popup-translate-x-distance:var(--media-popup-translate-distance)]',\n            'data-[side=right]:[--media-popup-translate-x-distance:calc(var(--media-popup-translate-distance)*-1)]',\n            'bg-media-popover text-media-popover-foreground surface-media after:surface-media-inset',\n            'm-0 min-w-44 max-w-(--media-menu-available-width) overflow-hidden! rounded-media-popup border-0 p-1 [--media-popup-side-offset:var(--media-popover-side-offset)]',\n            'max-h-[min(var(--media-menu-available-height,--spacing(56)),--spacing(56))] overscroll-none',\n            'h-(--media-menu-height) w-(--media-menu-width)',\n            'transition-media-popup media-transitioning:transition-media-popup',\n            'min-w-0!'\n          )}\n        >\n          <MenuPrimitive.Content\n            className={\n              '[anchor-scope:--media-menu-item-highlight-anchor] media-anchored:before:anchor-media-highlight media-anchored:has-data-[highlighted=]:before:duration-0 absolute max-h-[inherit] overflow-auto overscroll-none outline-hidden not-data-submenu:flex not-data-submenu:flex-col not-data-submenu:gap-0.5 transition-[translate,filter] duration-media-menu ease-out not-data-submenu:inset-x-1 not-data-submenu:top-1 not-data-submenu:data-[child-open]:-translate-x-full not-data-submenu:data-[child-open]:[&:dir(rtl)]:translate-x-full not-data-submenu:data-[child-open]:blur-media-hidden not-data-submenu:data-[child-open]:before:hidden data-submenu:inset-x-0 data-submenu:top-0 data-submenu:z-10 data-submenu:[max-height:inherit] data-submenu:p-1 data-submenu:media-transitioning:pointer-events-none data-submenu:media-transitioning:overflow-hidden data-submenu:media-transitioning:translate-x-full data-submenu:media-transitioning:[&:dir(rtl)]:-translate-x-full data-submenu:media-transitioning:blur-media-hidden'\n            }\n          >\n            <PlaybackRateRadioGroup.Options\n              className={\n                'flex [max-height:inherit] flex-col gap-0.5 [anchor-scope:--media-menu-item-highlight-anchor] media-anchored:before:anchor-media-highlight media-anchored:has-data-[highlighted=]:before:duration-0'\n              }\n              renderItem={(props, item) => (\n                <RadioItem {...props}>\n                  <span>{item.label}</span>\n                </RadioItem>\n              )}\n            ></PlaybackRateRadioGroup.Options>\n          </MenuPrimitive.Content>\n        </MenuPrimitive.Popup>\n      </PlaybackRateRadioGroup.Root>\n    </MenuPrimitive.Root>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/videojs/skins/audio/settings-menu.tsx"
    },
    {
      "path": "skins/files/audio/skins/audio/skin.tsx",
      "content": "/** @jsxImportSource react */\n\nimport '../../styles/theme.css';\nimport type { ComponentProps, ReactNode } from 'react';\n\nimport { cn } from '@/components/videojs/lib/resolve-class-name';\nimport { Container } from '@/components/videojs/ui/container';\nimport { StatusAnnouncer } from '@/components/videojs/ui/status-announcer';\n\nimport { PlaybackHotkeys } from '../shared/playback-hotkeys';\nimport { DefaultAudioControls } from './controls';\nimport { AudioErrorDialog } from './error-dialog';\n\nexport interface DefaultAudioSkinProps extends Omit<NonNullable<ComponentProps<typeof Container>>, 'children'> {\n  children?: ReactNode;\n}\n\nexport function DefaultAudioSkin({ children, className, ...props }: DefaultAudioSkinProps = {}) {\n  return (\n    <Container\n      className={cn('h-auto! overflow-visible! bg-transparent! [container-type:inline-size]! after:hidden', className)}\n      data-theme=\"default\"\n      data-preset=\"audio\"\n      {...props}\n    >\n      {children}\n      <AudioErrorDialog />\n      <DefaultAudioControls />\n      <PlaybackHotkeys />\n      <StatusAnnouncer />\n    </Container>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/videojs/skins/audio/skin.tsx"
    },
    {
      "path": "skins/files/audio/skins/audio/time-slider.tsx",
      "content": "/** @jsxImportSource react */\nimport type { SliderPreviewOverflow } from '@videojs/core';\nimport { TimeSlider } from '@videojs/react';\n\nimport { cn, resolveClassName } from '@/components/videojs/lib/resolve-class-name';\nimport { SliderBuffer, SliderFill, SliderThumb, SliderTrack } from '@/components/videojs/ui/slider';\n\nexport interface AudioTimeSliderProps extends Omit<TimeSlider.RootProps, 'children'> {\n  previewOverflow?: SliderPreviewOverflow | undefined;\n}\n\nexport function AudioTimeSlider({ className, previewOverflow = 'clamp', ...props }: AudioTimeSliderProps = {}) {\n  return (\n    <TimeSlider.Root\n      className={(state) =>\n        cn(\n          'group/slider relative flex flex-1 cursor-pointer items-center justify-center outline-hidden',\n          'rounded-media-pill',\n          'data-[orientation=horizontal]:[height:var(--media-slider-height,--spacing(8))] data-[orientation=horizontal]:min-w-20',\n          'data-[orientation=vertical]:h-20 data-[orientation=vertical]:w-8 data-[orientation=vertical]:min-w-0',\n          'audio-time-slider',\n          resolveClassName(className, state)\n        )\n      }\n      {...props}\n    >\n      <TimeSlider.Track render={<SliderTrack />}>\n        <TimeSlider.Buffer render={<SliderBuffer />} />\n        <TimeSlider.Fill render={<SliderFill />} />\n      </TimeSlider.Track>\n      <TimeSlider.Thumb\n        render={<SliderThumb />}\n        className={\n          'opacity-0 data-interactive:opacity-100 pointer-fine:group-hover/slider:scale-100 pointer-fine:group-hover/slider:opacity-100'\n        }\n      />\n      <TimeSlider.Preview\n        className={\n          'group/preview relative h-1 [--media-slider-preview-max-height:var(--media-slider-preview-max-width)] media-wide:[--media-slider-preview-max-width:min(--spacing(48),100cqi)] before:pointer-events-none before:absolute before:z-1 before:-translate-1/2 before:scale-50 before:opacity-0 before:transition-[opacity,scale] before:duration-media-slow before:ease-out data-pointing:not-data-dragging:before:scale-100 data-pointing:not-data-dragging:before:opacity-100 min-w-(--media-slider-preview-max-width) [--media-slider-preview-max-width:min(--spacing(36),100cqi)] before:top-1/2 before:left-1/2 before:size-1 before:rounded-media-control before:bg-current'\n        }\n        overflow={previewOverflow}\n      >\n        <div\n          className={cn(\n            'absolute max-w-(--media-slider-preview-max-width) -translate-x-1/2 translate-y-media-hidden-preview-offset scale-media-hidden-preview opacity-0',\n            'origin-bottom blur-media-hidden',\n            'transition-[filter,opacity,scale] duration-media-base ease-out',\n            'group-data-pointing/preview:scale-100 group-data-pointing/preview:opacity-100 group-data-pointing/preview:filter-none',\n            'group-has-focus-visible/slider:scale-100 group-has-focus-visible/slider:opacity-100 group-has-focus-visible/slider:filter-none',\n            'bg-media-popover text-media-popover-foreground surface-media after:surface-media-inset',\n            'whitespace-nowrap rounded-media-control py-1 text-media [--media-popup-side-offset:var(--media-tooltip-side-offset)]',\n            'data-open:flex data-open:items-center data-open:gap-1',\n            'px-2.5',\n            'bottom-[calc(100%+var(--media-slider-preview-label-offset))] tabular-nums',\n            'left-1/2'\n          )}\n        >\n          <TimeSlider.Value className={'tabular-nums'} type=\"pointer\" />\n        </div>\n      </TimeSlider.Preview>\n    </TimeSlider.Root>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/videojs/skins/audio/time-slider.tsx"
    },
    {
      "path": "skins/files/audio/skins/shared/playback-hotkeys.tsx",
      "content": "/** @jsxImportSource react */\n\nimport { Hotkey } from '@videojs/react';\n\nexport interface PlaybackHotkeysProps {\n  disabled?: boolean | undefined;\n}\n\nexport function PlaybackHotkeys({ disabled = false }: PlaybackHotkeysProps = {}) {\n  return (\n    <>\n      <Hotkey disabled={disabled} keys=\"Space\" action=\"togglePaused\" />\n      <Hotkey disabled={disabled} keys=\"k\" action=\"togglePaused\" />\n      <Hotkey disabled={disabled} keys=\"m\" action=\"toggleMuted\" />\n      <Hotkey disabled={disabled} keys=\"ArrowRight\" action=\"seekStep\" />\n      <Hotkey disabled={disabled} keys=\"ArrowLeft\" action=\"seekStep\" />\n      <Hotkey disabled={disabled} keys=\"l\" action=\"seekStep\" />\n      <Hotkey disabled={disabled} keys=\"j\" action=\"seekStep\" />\n      <Hotkey disabled={disabled} keys=\"ArrowUp\" action=\"volumeStep\" />\n      <Hotkey disabled={disabled} keys=\"ArrowDown\" action=\"volumeStep\" />\n      <Hotkey disabled={disabled} keys=\"0-9\" action=\"seekToPercent\" />\n      <Hotkey disabled={disabled} keys=\"Home\" action=\"seekToPercent\" value={0} />\n      <Hotkey disabled={disabled} keys=\"End\" action=\"seekToPercent\" value={100} />\n      <Hotkey disabled={disabled} keys=\">\" action=\"speedUp\" />\n      <Hotkey disabled={disabled} keys=\"<\" action=\"speedDown\" />\n    </>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/videojs/skins/shared/playback-hotkeys.tsx"
    }
  ],
  "meta": {
    "framework": "react",
    "style": "tailwind",
    "role": "skin",
    "styling": "tailwind",
    "preset": "audio",
    "media": "audio",
    "theme": "default",
    "public": true
  },
  "docs": "Requires `@videojs/react@10.0.0-beta.32`, which is installed with this item.\n\n```tsx\nimport { HlsAudio } from '@videojs/react/media/hls-audio';\nimport { AudioPlayer } from '@videojs/react/audio';\n\nimport { DefaultAudioSkin } from '@/components/videojs/skins/audio/skin';\n\nexport function Player({ src }: { src: string }) {\n  return (\n    <AudioPlayer>\n      <DefaultAudioSkin className=\"aspect-video w-full\">\n        <HlsAudio src={src} />\n      </DefaultAudioSkin>\n    </AudioPlayer>\n  );\n}\n```",
  "categories": [
    "media",
    "skins",
    "audio"
  ],
  "type": "registry:block"
}