Skip to content
FrameworkStyle

Customize skins

Learn the public customization surface for packaged Video.js skins and when to eject

Video.js v10 comes with two pre-built skins: Default and Minimal. Start with their public CSS custom properties. When you need to change controls, layout, or interactions, eject the closest skin by copying its code into your project and making it your own.

Basic customization

Set these CSS custom properties directly on the skin:

<VideoSkin
  style={
    {
      '--media-accent-color': '#f5c518',
      '--media-border-radius': '1rem',
    } as React.CSSProperties
  }
>
  <Video src="video.mp4" />
</VideoSkin>
Property Name Description Type Example
--media-accent-color The color of slider fills and accented controls <color> #f5c518
--media-accent-text-color The color of text and icons shown on the accent color <color> black
--media-border-radius The border radius of the media player A valid border-radius value 1rem
--media-scale-unit The base sizing unit for skin spacing and icons <length> 1rem

If you omit --media-accent-text-color, the skins choose a contrasting text color from --media-accent-color using contrast-color(). You only need to set it when you want to override that result.

Set the audio color scheme

The audio skins adapt their main palette for light and dark mode using light-dark(). It uses the inherited color-scheme, so set color-scheme on the player or an ancestor:

:root {
  color-scheme: light dark;
}

The video skins keep their dark control presentation in both color schemes.

Scale with the document font size

Vanilla CSS and Tailwind skins use --media-scale-unit as the base for control spacing and icon sizes. Vanilla CSS skins also use it for font sizes. It defaults to 16px, so player sizing does not change with the document root font size or the app’s Tailwind spacing scale. Set it to 1rem on the player or an ancestor to make the UI scale with the document root font size:

.player {
  --media-scale-unit: 1rem;
}

You can also add your own class names to the skins.

Where packaged customization stops

The four properties above are the public styling controls for packaged skins. Don’t depend on the elements, classnames, or other custom properties inside a packaged skin. They may change between releases.

Packaged video skins also let you replace the poster through the renderPoster prop.

See Poster.

For anything deeper, eject instead of reaching into the packaged skin. There are no packaged-skin options for adding, removing, or rearranging controls, or replacing its built-in feedback and interactions. Seek buttons are a common example: the audio skins include 10-second skip-back and skip-forward buttons, but the video skins ship without them. To add rewind and fast-forward controls to a video skin, eject it and place a SeekButton in the control bar.

Eject a skin

Eject as soon as the packaged skin no longer matches the player you need. Once ejected, removing a control is removing a component, and changing an overlay or interaction is editing code you own.

An ejected skin is app code, so its markup, class names, and styles are yours to change. If none of the packaged skins is a useful starting point, build a custom UI from the player, container, and individual UI components instead.

Soon, we’ll have a CLI that ejects skins in your preferred framework and style. For now, we invite you to try these copy-paste-ready implementations.

To eject a skin:

  1. Choose the closest implementation below.
  2. Copy every file shown for your framework into your project.
  3. Replace the packaged skin with the copied component or player markup, then edit the files you own.

Default video skin

'use client';

import { type SVGProps, type ComponentProps, type ReactElement, type ReactNode, type CSSProperties } from 'react';
import { SpinnerIcon as SpinnerIconPrimitive, ChevronIcon as ChevronIconPrimitive, CaptionsOnIcon as CaptionsOnIconPrimitive, CaptionsOffIcon as CaptionsOffIconPrimitive, FullscreenEnterIcon as FullscreenEnterIconPrimitive, FullscreenExitIcon as FullscreenExitIconPrimitive, PipEnterIcon as PipEnterIconPrimitive, PipExitIcon as PipExitIconPrimitive, PlayIcon as PlayIconPrimitive, PauseIcon as PauseIconPrimitive, VolumeHighIcon as VolumeHighIconPrimitive, VolumeLowIcon as VolumeLowIconPrimitive, VolumeOffIcon as VolumeOffIconPrimitive, AirPlayEnterIcon as AirPlayEnterIconPrimitive, AirPlayExitIcon as AirPlayExitIconPrimitive, CastEnterIcon as CastEnterIconPrimitive, CastExitIcon as CastExitIconPrimitive, RestartIcon as RestartIconPrimitive, CheckIcon as CheckIconPrimitive, GearIcon as GearIconPrimitive } from '@videojs/react/icons';
import { SpinnerIcon as SpinnerIconPrimitive2, ChevronIcon as ChevronIconPrimitive2, CaptionsOnIcon as CaptionsOnIconPrimitive2, CaptionsOffIcon as CaptionsOffIconPrimitive2, FullscreenEnterIcon as FullscreenEnterIconPrimitive2, FullscreenExitIcon as FullscreenExitIconPrimitive2, PipEnterIcon as PipEnterIconPrimitive2, PipExitIcon as PipExitIconPrimitive2, PlayIcon as PlayIconPrimitive2, PauseIcon as PauseIconPrimitive2, VolumeHighIcon as VolumeHighIconPrimitive2, VolumeLowIcon as VolumeLowIconPrimitive2, VolumeOffIcon as VolumeOffIconPrimitive2, AirPlayEnterIcon as AirPlayEnterIconPrimitive2, AirPlayExitIcon as AirPlayExitIconPrimitive2, CastEnterIcon as CastEnterIconPrimitive2, CastExitIcon as CastExitIconPrimitive2, RestartIcon as RestartIconPrimitive2, CheckIcon as CheckIconPrimitive2, GearIcon as GearIconPrimitive2 } from '@videojs/react/icons/minimal';
import { BufferingIndicator as BufferingIndicatorPrimitive, ErrorDialog as ErrorDialogPrimitive, Container as ContainerPrimitive, Poster as PosterPrimitive, Gesture, Hotkey, SeekIndicator as SeekIndicatorPrimitive, StatusAnnouncer as StatusAnnouncerPrimitive, StatusIndicator as StatusIndicatorPrimitive, VolumeIndicator as VolumeIndicatorPrimitive, Tooltip, AirPlayButton as AirPlayButtonPrimitive, CaptionsButton as CaptionsButtonPrimitive, CastButton as CastButtonPrimitive, FullscreenButton as FullscreenButtonPrimitive, PiPButton as PiPButtonPrimitive, PlayButton as PlayButtonPrimitive, MuteButton as MuteButtonPrimitive, VolumeSlider as VolumeSliderPrimitive, VolumePopover as VolumePopoverPrimitive, TimeSlider as TimeSliderPrimitive, Slider, Menu, Text as TextPrimitive, Controls, Time } from '@videojs/react';
import { Video } from '@videojs/react/video';
import './player.css';
import { Player } from './player';
import { type VolumeSliderProps as CoreVolumeSliderProps, type SliderPreviewOverflow } from '@videojs/core';
import { audioText, captionsText, speedText, qualityText, settingsText } from '@videojs/core/i18n/text/menu';
import { type Poster } from '@videojs/react';

type ContainerProps = ContainerPrimitive.Props;

function Container({ children, className, ...props }: ContainerProps) {
  return (
    <ContainerPrimitive className={`media-container ${className ?? ''}`} {...props}>
      {children}
    </ContainerPrimitive>
  );
}

interface PosterProps extends Omit<PosterPrimitive.Props, 'children' | 'render'> {
  children?: PosterPrimitive.Props['render'];
}

function Poster({ children, className, src, ...props }: PosterProps = {}) {
  return (
    <PosterPrimitive
      render={children}
      className={(state) => `media-poster ${resolveClassName(className, state)}`}
      src={src}
      {...props}
    />
  );
}

interface DefaultVideoSkinProps extends Omit<NonNullable<ComponentProps<typeof Container>>, 'children'> {
  src: string;
  poster?: string | undefined;
  poster?: string | NonNullable<ComponentProps<typeof Poster>>['children'];
}

type BufferingIndicatorProps = Omit<BufferingIndicatorPrimitive.Props, 'children'>;

function BufferingIndicator({ className, ...props }: BufferingIndicatorProps = {}) {
  return (
    <BufferingIndicatorPrimitive
      className={(state) => `media-buffering-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <SpinnerIconPrimitive className={'media-buffering-indicator-spinner-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <SpinnerIconPrimitive2 className={'media-buffering-indicator-spinner-icon'} />
        </span>
      </>
    </BufferingIndicatorPrimitive>
  );
}

type ErrorDialogProps = Omit<ErrorDialogPrimitive.PopupProps, 'children'>;

type ButtonProps = ComponentProps<'button'>;

type PlayButtonProps = Omit<PlayButtonPrimitive.Props, 'children'>;

interface ButtonTooltipProps extends Tooltip.RootProps {
  children: ReactElement;
  label?: ReactNode;
}

function ButtonTooltip({ children, label, ...props }: ButtonTooltipProps) {
  return (
    <Tooltip.Root {...props}>
      <Tooltip.Trigger render={children} />
      <Tooltip.Popup className={'media-tooltip'}>
        {label ?? <Tooltip.Label />}
        {!label && <Tooltip.Shortcut className={'media-tooltip-shortcut'} />}
      </Tooltip.Popup>
    </Tooltip.Root>
  );
}

function PlayButton({ className, ...props }: PlayButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <PlayButtonPrimitive
        render={<Button />}
        className={(state) => `media-play-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <RestartIconPrimitive className={'media-button-icon media-play-button-restart-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <RestartIconPrimitive2 className={'media-button-icon media-play-button-restart-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PlayIconPrimitive className={'media-button-icon media-play-button-play-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PlayIconPrimitive2 className={'media-button-icon media-play-button-play-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PauseIconPrimitive className={'media-button-icon media-play-button-pause-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PauseIconPrimitive2 className={'media-button-icon media-play-button-pause-icon'} />
          </span>
        </>
      </PlayButtonPrimitive>
    </ButtonTooltip>
  );
}

interface VolumePopoverProps extends Omit<VolumePopoverPrimitive.RootProps, 'children'> {
  className?: string | undefined;
  orientation?: CoreVolumeSliderProps['orientation'];
  showTooltip?: boolean;
  popupClassName?: ClassValue;
}

type MuteButtonProps = Omit<MuteButtonPrimitive.Props, 'children'>;

function MuteButton({ className, ...props }: MuteButtonProps = {}) {
  return (
    <MuteButtonPrimitive
      render={<Button />}
      className={(state) => `media-mute-button ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeOffIconPrimitive className={'media-button-icon media-mute-button-off-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeOffIconPrimitive2 className={'media-button-icon media-mute-button-off-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeLowIconPrimitive className={'media-button-icon media-mute-button-low-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeLowIconPrimitive2 className={'media-button-icon media-mute-button-low-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeHighIconPrimitive className={'media-button-icon media-mute-button-high-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeHighIconPrimitive2 className={'media-button-icon media-mute-button-high-icon'} />
        </span>
      </>
    </MuteButtonPrimitive>
  );
}

type VolumeSliderProps = Omit<VolumeSliderPrimitive.RootProps, 'children'>;

function VolumeSlider({ className, ...props }: VolumeSliderProps = {}) {
  return (
    <VolumeSliderPrimitive.Root
      className={(state) => `media-slider media-volume-slider ${resolveClassName(className, state)}`}
      thumbAlignment="edge"
      {...props}
    >
      <VolumeSliderPrimitive.Track render={<SliderTrack />}>
        <VolumeSliderPrimitive.Fill render={<SliderFill />} />
      </VolumeSliderPrimitive.Track>
      <VolumeSliderPrimitive.Thumb render={<SliderThumb />} className={'media-volume-slider-thumb'} />
    </VolumeSliderPrimitive.Root>
  );
}

interface TimeSliderProps extends Omit<TimeSliderPrimitive.RootProps, 'children'> {
  previewOverflow?: SliderPreviewOverflow | undefined;
}

function TimeSlider({ className, previewOverflow = 'visible', ...props }: TimeSliderProps = {}) {
  return (
    <TimeSliderPrimitive.Root
      className={(state) => `media-slider media-time-slider ${resolveClassName(className, state)}`}
      {...props}
    >
      <TimeSliderPrimitive.Chapters
        className={'media-time-slider-chapters'}
        renderChapter={(props) => (
          <div className={'media-time-slider-chapter'} {...props}>
            <TimeSliderPrimitive.Track render={<SliderTrack />} className={'media-time-slider-chapter-track'}>
              <TimeSliderPrimitive.Buffer render={<SliderBuffer />} />
              <TimeSliderPrimitive.Fill render={<SliderFill />} />
            </TimeSliderPrimitive.Track>
          </div>
        )}
      ></TimeSliderPrimitive.Chapters>
      <TimeSliderPrimitive.Thumb render={<SliderThumb />} className={'media-time-slider-thumb'} />
      <TimeSliderPrimitive.Preview className={'media-slider-preview'} overflow={previewOverflow}>
        <div className={'media-slider-thumbnail'}>
          <Slider.Thumbnail className={'media-slider-thumbnail-image'} />
          <>
            <span className="contents theme-minimal:hidden">
              <SpinnerIconPrimitive className={'media-slider-thumbnail-spinner-icon'} />
            </span>
            <span className="hidden theme-minimal:contents">
              <SpinnerIconPrimitive2 className={'media-slider-thumbnail-spinner-icon'} />
            </span>
          </>
        </div>
        <div className={'media-time-slider-preview-content'}>
          <TimeSliderPrimitive.ChapterTitle className={'media-time-slider-chapter-title'} />
          <TimeSliderPrimitive.Value className={'media-time-slider-value'} type="pointer" />
        </div>
      </TimeSliderPrimitive.Preview>
    </TimeSliderPrimitive.Root>
  );
}

type CaptionsButtonProps = Omit<CaptionsButtonPrimitive.Props, 'children'>;

function CaptionsButton({ className, ...props }: CaptionsButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <CaptionsButtonPrimitive
        render={<Button />}
        className={(state) => `media-captions-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <CaptionsOffIconPrimitive className={'media-button-icon media-captions-button-off-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CaptionsOffIconPrimitive2 className={'media-button-icon media-captions-button-off-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <CaptionsOnIconPrimitive className={'media-button-icon media-captions-button-on-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CaptionsOnIconPrimitive2 className={'media-button-icon media-captions-button-on-icon'} />
          </span>
        </>
      </CaptionsButtonPrimitive>
    </ButtonTooltip>
  );
}

interface SettingsMenuProps extends Menu.RootProps {
  className?: string | undefined;
}

function SettingsMenu({ children, className, ...props }: SettingsMenuProps) {
  return (
    <Menu.Root side="top" align="center" {...props}>
      <ButtonTooltip label={<TextPrimitive token={settingsText.key}>{settingsText.text}</TextPrimitive>} side="top">
        <Menu.Trigger render={<Button />} className={'media-settings-menu-trigger'}>
          <>
            <span className="contents theme-minimal:hidden">
              <GearIconPrimitive className={'media-button-icon media-settings-menu-trigger-icon'} />
            </span>
            <span className="hidden theme-minimal:contents">
              <GearIconPrimitive2 className={'media-button-icon media-settings-menu-trigger-icon'} />
            </span>
          </>
          <TextPrimitive className={'media-settings-menu-trigger-label'} token={settingsText.key}>
            {settingsText.text}
          </TextPrimitive>
        </Menu.Trigger>
      </ButtonTooltip>
      <Menu.Popup keepMounted className={(state) => `media-menu-popup ${resolveClassName(className, state)}`}>
        <Menu.Content className={'media-menu-content'}>{children}</Menu.Content>
      </Menu.Popup>
    </Menu.Root>
  );
}

type VideoSettingsMenuProps = Omit<NonNullable<ComponentProps<typeof SettingsMenu>>, 'children'>;

interface QualityMenuProps extends MenuProps {
  className?: ClassValue;
}

interface MenuChevronProps {
  back?: boolean;
  className?: ClassValue;
}

function MenuChevron({ back = false, className }: MenuChevronProps = {}) {
  return (
    <>
      <span className="contents theme-minimal:hidden">
        <ChevronIconPrimitive
          className={`${back ? 'media-menu-back-chevron' : 'media-menu-forward-chevron'} ${className ?? ''}`}
        />
      </span>
      <span className="hidden theme-minimal:contents">
        <ChevronIconPrimitive2
          className={`${back ? 'media-menu-back-chevron' : 'media-menu-forward-chevron'} ${className ?? ''}`}
        />
      </span>
    </>
  );
}

type RadioItemProps = Menu.RadioItemProps;

function RadioItem({ children, className, ...props }: RadioItemProps) {
  return (
    <Menu.RadioItem className={(state) => `media-menu-radio-item ${resolveClassName(className, state)}`} {...props}>
      {children}
      <Menu.ItemIndicator forceMount className={'media-menu-item-indicator'}>
        <>
          <span className="contents theme-minimal:hidden">
            <CheckIconPrimitive className={'media-menu-radio-item-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CheckIconPrimitive2 className={'media-menu-radio-item-icon'} />
          </span>
        </>
      </Menu.ItemIndicator>
    </Menu.RadioItem>
  );
}

function QualityMenu({ className, ...props }: QualityMenuProps = {}) {
  return (
    <Menu.Root {...props}>
      <QualityRadioGroup.Root>
        <Menu.Trigger className={'media-menu-trigger-item'}>
          <>
            <span className="contents theme-minimal:hidden">
              <SwitchesIconPrimitive className={'media-menu-trigger-item-icon'} />
            </span>
            <span className="hidden theme-minimal:contents">
              <SwitchesIconPrimitive2 className={'media-menu-trigger-item-icon'} />
            </span>
          </>
          <TextPrimitive token={qualityText.key}>{qualityText.text}</TextPrimitive>
          <span className={'media-menu-hint'}>
            <QualityRadioGroup.Value className={'media-menu-hint-label'} />
            <MenuChevron />
          </span>
        </Menu.Trigger>
        <Menu.Content className={(state) => `media-menu-content ${resolveClassName(className, state)}`}>
          <Menu.Item className={'media-menu-back-item'}>
            <MenuChevron back />
            <TextPrimitive token={qualityText.key}>{qualityText.text}</TextPrimitive>
          </Menu.Item>
          <Menu.Separator className={'media-menu-separator'} />
          <QualityRadioGroup.Options
            className={'media-menu-radio-group'}
            renderItem={(props, item) => (
              <RadioItem {...props}>
                <span>
                  <span>{item.label}</span>
                  {item.tier ? <sup className={'media-menu-tier'}>{item.tier}</sup> : null}
                </span>
                {item.badge ? <span className={'media-menu-badge'}>{item.badge}</span> : null}
              </RadioItem>
            )}
          ></QualityRadioGroup.Options>
        </Menu.Content>
      </QualityRadioGroup.Root>
    </Menu.Root>
  );
}

interface AudioTrackMenuProps extends MenuProps {
  className?: ClassValue;
}

function AudioTrackMenu({ className, ...props }: AudioTrackMenuProps = {}) {
  return (
    <Menu.Root {...props}>
      <AudioTrackRadioGroup.Root>
        <Menu.Trigger className={'media-menu-trigger-item'}>
          <>
            <span className="contents theme-minimal:hidden">
              <SpeechIconPrimitive className={'media-menu-trigger-item-icon'} />
            </span>
            <span className="hidden theme-minimal:contents">
              <SpeechIconPrimitive2 className={'media-menu-trigger-item-icon'} />
            </span>
          </>
          <TextPrimitive token={audioText.key}>{audioText.text}</TextPrimitive>
          <span className={'media-menu-hint'}>
            <AudioTrackRadioGroup.Value className={'media-menu-hint-label'} />
            <MenuChevron />
          </span>
        </Menu.Trigger>
        <Menu.Content className={(state) => `media-menu-content ${resolveClassName(className, state)}`}>
          <Menu.Item className={'media-menu-back-item'}>
            <MenuChevron back />
            <TextPrimitive token={audioText.key}>{audioText.text}</TextPrimitive>
          </Menu.Item>
          <Menu.Separator className={'media-menu-separator'} />
          <AudioTrackRadioGroup.Options
            className={'media-menu-radio-group'}
            renderItem={(props, item) => (
              <RadioItem {...props}>
                <span>{item.label}</span>
              </RadioItem>
            )}
          ></AudioTrackRadioGroup.Options>
        </Menu.Content>
      </AudioTrackRadioGroup.Root>
    </Menu.Root>
  );
}

interface PlaybackRateSubmenuProps extends MenuProps {
  className?: ClassValue;
}

function PlaybackRateSubmenu({ className, ...props }: PlaybackRateSubmenuProps = {}) {
  return (
    <Menu.Root {...props}>
      <PlaybackRateRadioGroup.Root>
        <Menu.Trigger className={'media-menu-trigger-item'}>
          <>
            <span className="contents theme-minimal:hidden">
              <SpeedIconPrimitive className={'media-menu-trigger-item-icon'} />
            </span>
            <span className="hidden theme-minimal:contents">
              <SpeedIconPrimitive2 className={'media-menu-trigger-item-icon'} />
            </span>
          </>
          <TextPrimitive token={speedText.key}>{speedText.text}</TextPrimitive>
          <span className={'media-menu-hint'}>
            <PlaybackRateRadioGroup.Value className={'media-menu-hint-label'} />
            <MenuChevron />
          </span>
        </Menu.Trigger>
        <Menu.Content className={(state) => `media-menu-content ${resolveClassName(className, state)}`}>
          <Menu.Item className={'media-menu-back-item'}>
            <MenuChevron back />
            <TextPrimitive token={speedText.key}>{speedText.text}</TextPrimitive>
          </Menu.Item>
          <Menu.Separator className={'media-menu-separator'} />
          <PlaybackRateRadioGroup.Options
            className={'media-menu-radio-group'}
            renderItem={(props, item) => (
              <RadioItem {...props}>
                <span>{item.label}</span>
              </RadioItem>
            )}
          ></PlaybackRateRadioGroup.Options>
        </Menu.Content>
      </PlaybackRateRadioGroup.Root>
    </Menu.Root>
  );
}

interface CaptionsSubmenuProps extends MenuProps {
  className?: ClassValue;
}

function CaptionsSubmenu({ className, ...props }: CaptionsSubmenuProps = {}) {
  return (
    <Menu.Root {...props}>
      <CaptionsRadioGroup.Root>
        <Menu.Trigger className={'media-menu-trigger-item'}>
          <>
            <span className="contents theme-minimal:hidden">
              <CaptionsOffIconPrimitive className={'media-menu-trigger-item-icon'} />
            </span>
            <span className="hidden theme-minimal:contents">
              <CaptionsOffIconPrimitive2 className={'media-menu-trigger-item-icon'} />
            </span>
          </>
          <TextPrimitive token={captionsText.key}>{captionsText.text}</TextPrimitive>
          <span className={'media-menu-hint'}>
            <CaptionsRadioGroup.Value className={'media-menu-hint-label'} />
            <MenuChevron />
          </span>
        </Menu.Trigger>
        <Menu.Content className={(state) => `media-menu-content ${resolveClassName(className, state)}`}>
          <Menu.Item className={'media-menu-back-item'}>
            <MenuChevron back />
            <TextPrimitive token={captionsText.key}>{captionsText.text}</TextPrimitive>
          </Menu.Item>
          <Menu.Separator className={'media-menu-separator'} />
          <CaptionsRadioGroup.Options
            className={'media-menu-radio-group'}
            renderItem={(props, item) => (
              <RadioItem {...props}>
                <span>{item.label}</span>
              </RadioItem>
            )}
          ></CaptionsRadioGroup.Options>
        </Menu.Content>
      </CaptionsRadioGroup.Root>
    </Menu.Root>
  );
}

function VideoSettingsMenu(props: VideoSettingsMenuProps = {}) {
  return (
    <SettingsMenu {...props}>
      <QualityMenu />
      <AudioTrackMenu />
      <PlaybackRateSubmenu />
      <CaptionsSubmenu />
    </SettingsMenu>
  );
}

type CastButtonProps = Omit<CastButtonPrimitive.Props, 'children'>;

function CastButton({ className, ...props }: CastButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <CastButtonPrimitive
        render={<Button />}
        className={(state) => `media-cast-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <CastEnterIconPrimitive className={'media-button-icon media-cast-button-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CastEnterIconPrimitive2 className={'media-button-icon media-cast-button-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <CastExitIconPrimitive className={'media-button-icon media-cast-button-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CastExitIconPrimitive2 className={'media-button-icon media-cast-button-exit-icon'} />
          </span>
        </>
      </CastButtonPrimitive>
    </ButtonTooltip>
  );
}

type AirPlayButtonProps = Omit<AirPlayButtonPrimitive.Props, 'children'>;

function AirPlayButton({ className, ...props }: AirPlayButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <AirPlayButtonPrimitive
        render={<Button />}
        className={(state) => `media-airplay-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <AirPlayEnterIconPrimitive className={'media-button-icon media-airplay-button-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <AirPlayEnterIconPrimitive2 className={'media-button-icon media-airplay-button-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <AirPlayExitIconPrimitive className={'media-button-icon media-airplay-button-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <AirPlayExitIconPrimitive2 className={'media-button-icon media-airplay-button-exit-icon'} />
          </span>
        </>
      </AirPlayButtonPrimitive>
    </ButtonTooltip>
  );
}

type PiPButtonProps = Omit<PiPButtonPrimitive.Props, 'children'>;

function PiPButton({ className, ...props }: PiPButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <PiPButtonPrimitive
        render={<Button />}
        className={(state) => `media-pip-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <PipEnterIconPrimitive className={'media-button-icon media-pip-button-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PipEnterIconPrimitive2 className={'media-button-icon media-pip-button-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PipExitIconPrimitive className={'media-button-icon media-pip-button-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PipExitIconPrimitive2 className={'media-button-icon media-pip-button-exit-icon'} />
          </span>
        </>
      </PiPButtonPrimitive>
    </ButtonTooltip>
  );
}

type FullscreenButtonProps = Omit<FullscreenButtonPrimitive.Props, 'children'>;

function FullscreenButton({ className, ...props }: FullscreenButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <FullscreenButtonPrimitive
        render={<Button />}
        className={(state) => `media-fullscreen-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <FullscreenEnterIconPrimitive className={'media-button-icon media-fullscreen-button-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <FullscreenEnterIconPrimitive2 className={'media-button-icon media-fullscreen-button-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <FullscreenExitIconPrimitive className={'media-button-icon media-fullscreen-button-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <FullscreenExitIconPrimitive2 className={'media-button-icon media-fullscreen-button-exit-icon'} />
          </span>
        </>
      </FullscreenButtonPrimitive>
    </ButtonTooltip>
  );
}

function DefaultVideoControls() {
  return (
    <Controls.Root>
      <Controls.Backdrop className={'media-controls__backdrop'} />
      <Controls.Content className={'media-controls media-controls'}>
        <Tooltip.Provider>
          <Controls.Group className={'media-controls-primary'}>
            <PlayButton />
            <VolumePopover className={'media-controls-volume-button'} />

            <Controls.Group className={'media-time-slider-group'}>
              <Time.Value className={'media-time-current-value'} type="current" />
              <TimeSlider />
              <Time.Value className={'media-time-remaining-value'} type="remaining" toggle />
            </Controls.Group>

            <CaptionsButton className={'media-controls-captions-button'} />
            <VideoSettingsMenu className={'media-controls-settings-button'} />
          </Controls.Group>

          <Controls.Group className={'media-controls-secondary'}>
            <CastButton />
            <AirPlayButton />
            <PiPButton />
            <FullscreenButton />
          </Controls.Group>
        </Tooltip.Provider>
      </Controls.Content>
    </Controls.Root>
  );
}

interface VideoHotkeysProps {
  disabled?: boolean | undefined;
}

interface PlaybackHotkeysProps {
  disabled?: boolean | undefined;
}

function PlaybackHotkeys({ disabled = false }: PlaybackHotkeysProps = {}) {
  return (
    <>
      <Hotkey disabled={disabled} keys="Space" action="togglePaused" />
      <Hotkey disabled={disabled} keys="k" action="togglePaused" />
      <Hotkey disabled={disabled} keys="m" action="toggleMuted" />
      <Hotkey disabled={disabled} keys="ArrowRight" action="seekStep" value={5} />
      <Hotkey disabled={disabled} keys="ArrowLeft" action="seekStep" value={-5} />
      <Hotkey disabled={disabled} keys="l" action="seekStep" value={10} />
      <Hotkey disabled={disabled} keys="j" action="seekStep" value={-10} />
      <Hotkey disabled={disabled} keys="ArrowUp" action="volumeStep" value={0.05} />
      <Hotkey disabled={disabled} keys="ArrowDown" action="volumeStep" value={-0.05} />
      <Hotkey disabled={disabled} keys="0-9" action="seekToPercent" />
      <Hotkey disabled={disabled} keys="Home" action="seekToPercent" value={0} />
      <Hotkey disabled={disabled} keys="End" action="seekToPercent" value={100} />
      <Hotkey disabled={disabled} keys=">" action="speedUp" />
      <Hotkey disabled={disabled} keys="<" action="speedDown" />
    </>
  );
}

function VideoHotkeys({ disabled = false }: VideoHotkeysProps = {}) {
  return (
    <>
      <PlaybackHotkeys disabled={disabled} />
      <Hotkey disabled={disabled} keys="f" action="toggleFullscreen" />
      <Hotkey disabled={disabled} keys="c" action="toggleSubtitles" />
      <Hotkey disabled={disabled} keys="i" action="togglePictureInPicture" />
    </>
  );
}

interface VideoGesturesProps {
  disabled?: boolean | undefined;
}

function VideoGestures({ disabled = false }: VideoGesturesProps = {}) {
  return (
    <>
      <Gesture disabled={disabled} type="tap" action="togglePaused" pointer="mouse" region="center" />
      <Gesture disabled={disabled} type="tap" action="toggleControls" pointer="touch" />
      <Gesture disabled={disabled} type="doubletap" action="seekStep" value={-10} region="left" />
      <Gesture disabled={disabled} type="doubletap" action="toggleFullscreen" region="center" />
      <Gesture disabled={disabled} type="doubletap" action="seekStep" value={10} region="right" />
    </>
  );
}

type VideoStatusIndicatorsProps = Omit<ComponentProps<'div'>, 'children'>;

type StatusAnnouncerProps = Omit<StatusAnnouncerPrimitive.Props, 'children'>;

function StatusAnnouncer({ className, ...props }: StatusAnnouncerProps = {}) {
  return (
    <StatusAnnouncerPrimitive
      className={(state) => `media-status-announcer ${resolveClassName(className, state)}`}
      {...props}
    />
  );
}

type VolumeIndicatorProps = Omit<VolumeIndicatorPrimitive.RootProps, 'children'>;

function VolumeIndicator({ className, ...props }: VolumeIndicatorProps = {}) {
  return (
    <VolumeIndicatorPrimitive.Root
      className={(state) => `media-volume-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <VolumeIndicatorPrimitive.Fill className={'media-volume-indicator-fill'}>
        <>
          <span className="contents theme-minimal:hidden">
            <VolumeHighIconPrimitive className={'media-volume-indicator-high-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <VolumeHighIconPrimitive2 className={'media-volume-indicator-high-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <VolumeLowIconPrimitive className={'media-volume-indicator-low-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <VolumeLowIconPrimitive2 className={'media-volume-indicator-low-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <VolumeOffIconPrimitive className={'media-volume-indicator-off-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <VolumeOffIconPrimitive2 className={'media-volume-indicator-off-icon'} />
          </span>
        </>
        <VolumeIndicatorPrimitive.Value className={'media-volume-indicator-value'} />
      </VolumeIndicatorPrimitive.Fill>
    </VolumeIndicatorPrimitive.Root>
  );
}

type StatusIndicatorProps = Omit<StatusIndicatorPrimitive.RootProps, 'children'>;

const TOP_STATUS_ACTIONS = ['toggleSubtitles', 'toggleFullscreen', 'togglePictureInPicture'] as const;

function StatusIndicator({ className, ...props }: StatusIndicatorProps = {}) {
  return (
    <StatusIndicatorPrimitive.Root
      actions={TOP_STATUS_ACTIONS}
      className={(state) => `media-status-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <div className={'media-status-indicator-content'}>
        <>
          <span className="contents theme-minimal:hidden">
            <CaptionsOnIconPrimitive className={'media-status-indicator-captions-on-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CaptionsOnIconPrimitive2 className={'media-status-indicator-captions-on-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <CaptionsOffIconPrimitive className={'media-status-indicator-captions-off-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CaptionsOffIconPrimitive2 className={'media-status-indicator-captions-off-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <FullscreenEnterIconPrimitive className={'media-status-indicator-fullscreen-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <FullscreenEnterIconPrimitive2 className={'media-status-indicator-fullscreen-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <FullscreenExitIconPrimitive className={'media-status-indicator-fullscreen-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <FullscreenExitIconPrimitive2 className={'media-status-indicator-fullscreen-exit-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PipEnterIconPrimitive className={'media-status-indicator-pip-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PipEnterIconPrimitive2 className={'media-status-indicator-pip-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PipExitIconPrimitive className={'media-status-indicator-pip-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PipExitIconPrimitive2 className={'media-status-indicator-pip-exit-icon'} />
          </span>
        </>
        <StatusIndicatorPrimitive.Value className={'media-status-indicator-value'} />
      </div>
    </StatusIndicatorPrimitive.Root>
  );
}

type SeekIndicatorProps = Omit<SeekIndicatorPrimitive.RootProps, 'children'>;

function SeekIndicator({ className, ...props }: SeekIndicatorProps = {}) {
  return (
    <SeekIndicatorPrimitive.Root
      className={(state) => `media-seek-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <ChevronIconPrimitive className={'media-seek-indicator-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <ChevronIconPrimitive2 className={'media-seek-indicator-icon'} />
        </span>
      </>
      <SeekIndicatorPrimitive.Value className={'media-seek-indicator-value'} />
    </SeekIndicatorPrimitive.Root>
  );
}

type PlaybackStatusIndicatorProps = Omit<StatusIndicatorPrimitive.RootProps, 'children'>;

const PLAYBACK_STATUS_ACTIONS = ['togglePaused'] as const;

function PlaybackStatusIndicator({ className, ...props }: PlaybackStatusIndicatorProps = {}) {
  return (
    <StatusIndicatorPrimitive.Root
      actions={PLAYBACK_STATUS_ACTIONS}
      className={(state) => `media-playback-status-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <PlayIconPrimitive className={'media-playback-status-indicator-play-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <PlayIconPrimitive2 className={'media-playback-status-indicator-play-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <PauseIconPrimitive className={'media-playback-status-indicator-pause-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <PauseIconPrimitive2 className={'media-playback-status-indicator-pause-icon'} />
        </span>
      </>
    </StatusIndicatorPrimitive.Root>
  );
}

function VideoStatusIndicators({ className, ...props }: VideoStatusIndicatorsProps = {}) {
  return (
    <>
      <StatusAnnouncer />
      <div className={`media-video-status-indicators ${className ?? ''}`} {...props}>
        <VolumeIndicator />
        <StatusIndicator />
        <SeekIndicator />
        <PlaybackStatusIndicator />
      </div>
    </>
  );
}

function DefaultVideoSkin({ children, className, poster, ...props }: DefaultVideoSkinProps = {}) {
  const isPosterString = typeof poster === 'string';

  return (
    <Container className={`media-skin media-skin--video ${className ?? ''}`} {...props}>
      {children}
      <Poster src={isPosterString ? poster : undefined}>{isPosterString ? undefined : poster}</Poster>
      <BufferingIndicator />
      <ErrorDialog />

      <DefaultVideoControls />

      <VideoHotkeys />
      <VideoGestures />
      <VideoStatusIndicators />
    </Container>
  );
}

const Skin = DefaultVideoSkin;

// ================================================================
// Player
// ================================================================

export interface VideoPlayerProps {
  children?: ReactNode;
  style?: CSSProperties;
  className?: string;
  renderPoster?: Poster.Props['render'];
}

export function VideoSkin({ renderPoster, ...props }: VideoSkinProps) {
  return <Skin poster={renderPoster} {...props} />;
}

// ================================================================
// Components
// ================================================================

function Button({ className, ...props }: ButtonProps) {
  return <button className={`media-button ${className ?? ''}`} {...props} />;
}

type SliderTrackProps = ComponentProps<'div'>;

function SliderTrack({ className, ...props }: SliderTrackProps) {
  return <div className={`media-slider-track ${className ?? ''}`} {...props} />;
}

type SliderFillProps = ComponentProps<'div'>;

function SliderFill({ className, ...props }: SliderFillProps) {
  return <div className={`media-slider-fill ${className ?? ''}`} {...props} />;
}

type SliderThumbProps = ComponentProps<'div'>;

function SliderThumb({ className, ...props }: SliderThumbProps) {
  return <div className={`media-slider-thumb ${className ?? ''}`} {...props} />;
}

function VolumePopover({
  className,
  popupClassName,
  showTooltip = false,
  side = 'top',
  orientation = 'vertical',
  ...props
}: VolumePopoverProps = {}) {
  return (
    <VolumePopoverPrimitive.Root openOnHover delay={200} closeDelay={100} side={side} {...props}>
      <ButtonTooltip delay={0} disabled={!showTooltip} sticky side="top">
        <VolumePopoverPrimitive.Trigger render={<MuteButton className={className} />} />
      </ButtonTooltip>
      <VolumePopoverPrimitive.Popup className={`media-volume-popover ${popupClassName}`}>
        <VolumeSlider orientation={orientation} />
      </VolumePopoverPrimitive.Popup>
    </VolumePopoverPrimitive.Root>
  );
}

type SliderBufferProps = ComponentProps<'div'>;

function SliderBuffer({ className, ...props }: SliderBufferProps) {
  return <div className={`media-slider-buffer ${className ?? ''}`} {...props} />;
}

// ================================================================
// Error Dialog
// ================================================================

function ErrorDialog({ className, ...props }: ErrorDialogProps = {}) {
  return (
    <ErrorDialogPrimitive.Root>
      <ErrorDialogPrimitive.Backdrop className={'media-dialog-backdrop'} />
      <ErrorDialogPrimitive.Popup
        className={(state) => `media-dialog-popup ${resolveClassName(className, state)}`}
        {...props}
      >
        <div className={'media-dialog-content'}>
          <ErrorDialogPrimitive.Title className={'media-dialog-title'} />
          <ErrorDialogPrimitive.Description className={'media-dialog-description'} />
        </div>
        <div className={'media-dialog-actions'}>
          <ErrorDialogPrimitive.Close render={<Button />} className={'media-dialog-close'} />
        </div>
      </ErrorDialogPrimitive.Popup>
    </ErrorDialogPrimitive.Root>
  );
}

Default audio skin

'use client';

import { StatusAnnouncer as StatusAnnouncerPrimitive, Container as ContainerPrimitive, ErrorDialog, Hotkey, Tooltip, SeekButton as SeekButtonPrimitive, MuteButton as MuteButtonPrimitive, VolumeSlider as VolumeSliderPrimitive, VolumePopover as VolumePopoverPrimitive, PlayButton as PlayButtonPrimitive, BufferingIndicator as BufferingIndicatorPrimitive, PlaybackRateButton as PlaybackRateButtonPrimitive, Menu, Text as TextPrimitive, PlaybackRateRadioGroup, TimeSlider, Controls, Time } from '@videojs/react';
import { Audio } from '@videojs/react/audio';
import './player.css';
import { Player } from './player';
import { type ComponentProps, type SVGProps, type ReactElement, type ReactNode, type CSSProperties } from 'react';
import { SeekIcon as SeekIconPrimitive, VolumeOffIcon as VolumeOffIconPrimitive, VolumeLowIcon as VolumeLowIconPrimitive, VolumeHighIcon as VolumeHighIconPrimitive, RestartIcon as RestartIconPrimitive, PlayIcon as PlayIconPrimitive, PauseIcon as PauseIconPrimitive, SpinnerIcon as SpinnerIconPrimitive, CheckIcon as CheckIconPrimitive } from '@videojs/react/icons';
import { SeekIcon as SeekIconPrimitive2, VolumeOffIcon as VolumeOffIconPrimitive2, VolumeLowIcon as VolumeLowIconPrimitive2, VolumeHighIcon as VolumeHighIconPrimitive2, RestartIcon as RestartIconPrimitive2, PlayIcon as PlayIconPrimitive2, PauseIcon as PauseIconPrimitive2, SpinnerIcon as SpinnerIconPrimitive2, CheckIcon as CheckIconPrimitive2 } from '@videojs/react/icons/minimal';
import { type VolumeSliderProps as CoreVolumeSliderProps, type MenuProps, type SliderPreviewOverflow } from '@videojs/core';
import { speedText } from '@videojs/core/i18n/text/menu';
import { type Poster } from '@videojs/react';

type ContainerProps = ContainerPrimitive.Props;

function Container({ children, className, ...props }: ContainerProps) {
  return (
    <ContainerPrimitive className={`media-container ${className ?? ''}`} {...props}>
      {children}
    </ContainerPrimitive>
  );
}

interface DefaultAudioSkinProps extends Omit<NonNullable<ComponentProps<typeof Container>>, 'children'> {
  src: string;
}

type AudioErrorDialogProps = Omit<ErrorDialog.PopupProps, 'children'>;

type ButtonProps = ComponentProps<'button'>;

function AudioErrorDialog({ className, ...props }: AudioErrorDialogProps = {}) {
  return (
    <ErrorDialog.Root>
      <ErrorDialog.Backdrop className={'media-audio-dialog-backdrop'} />
      <ErrorDialog.Popup
        className={(state) => `media-audio-dialog-popup ${resolveClassName(className, state)}`}
        {...props}
      >
        <div className={'media-audio-dialog-content'}>
          <ErrorDialog.Title className={'media-audio-dialog-title'} />
          <ErrorDialog.Description className={'media-audio-dialog-description'} />
        </div>
        <div className={'media-audio-dialog-actions'}>
          <ErrorDialog.Close render={<Button />} className={'media-audio-dialog-close'} />
        </div>
      </ErrorDialog.Popup>
    </ErrorDialog.Root>
  );
}

type AudioPlayButtonProps = Omit<ComponentProps<'div'>, 'children'>;

type BufferingIndicatorProps = Omit<BufferingIndicatorPrimitive.Props, 'children'>;

function BufferingIndicator({ className, ...props }: BufferingIndicatorProps = {}) {
  return (
    <BufferingIndicatorPrimitive
      className={(state) => `media-buffering-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <SpinnerIconPrimitive className={'media-buffering-indicator-spinner-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <SpinnerIconPrimitive2 className={'media-buffering-indicator-spinner-icon'} />
        </span>
      </>
    </BufferingIndicatorPrimitive>
  );
}

type PlayButtonProps = Omit<PlayButtonPrimitive.Props, 'children'>;

interface ButtonTooltipProps extends Tooltip.RootProps {
  children: ReactElement;
  label?: ReactNode;
}

function ButtonTooltip({ children, label, ...props }: ButtonTooltipProps) {
  return (
    <Tooltip.Root {...props}>
      <Tooltip.Trigger render={children} />
      <Tooltip.Popup className={'media-tooltip'}>
        {label ?? <Tooltip.Label />}
        {!label && <Tooltip.Shortcut className={'media-tooltip-shortcut'} />}
      </Tooltip.Popup>
    </Tooltip.Root>
  );
}

function PlayButton({ className, ...props }: PlayButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <PlayButtonPrimitive
        render={<Button />}
        className={(state) => `media-play-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <RestartIconPrimitive className={'media-button-icon media-play-button-restart-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <RestartIconPrimitive2 className={'media-button-icon media-play-button-restart-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PlayIconPrimitive className={'media-button-icon media-play-button-play-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PlayIconPrimitive2 className={'media-button-icon media-play-button-play-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PauseIconPrimitive className={'media-button-icon media-play-button-pause-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PauseIconPrimitive2 className={'media-button-icon media-play-button-pause-icon'} />
          </span>
        </>
      </PlayButtonPrimitive>
    </ButtonTooltip>
  );
}

function AudioPlayButton({ className, ...props }: AudioPlayButtonProps = {}) {
  return (
    <div className={`media-audio-play-button ${className ?? ''}`} {...props}>
      <BufferingIndicator className={'media-audio-play-button-buffering-indicator'} />
      <PlayButton />
    </div>
  );
}

type SeekButtonProps = Omit<SeekButtonPrimitive.Props, 'children'>;

function SeekButton({ className, seconds = 10, ...props }: SeekButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <SeekButtonPrimitive
        render={<Button />}
        className={(state) => `media-seek-button ${resolveClassName(className, state)}`}
        seconds={seconds}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <SeekIconPrimitive className={`media-button-icon ${seconds < 0 && 'media-seek-button-backward-icon'}`} />
          </span>
          <span className="hidden theme-minimal:contents">
            <SeekIconPrimitive2 className={`media-button-icon ${seconds < 0 && 'media-seek-button-backward-icon'}`} />
          </span>
        </>
        <span
          className={`media-seek-button-label ${seconds < 0 ? 'media-seek-button-backward-label' : 'media-seek-button-forward-label'}`}
        >
          {Math.abs(seconds)}
        </span>
      </SeekButtonPrimitive>
    </ButtonTooltip>
  );
}

interface AudioTimeSliderProps extends Omit<TimeSlider.RootProps, 'children'> {
  previewOverflow?: SliderPreviewOverflow | undefined;
}

function AudioTimeSlider({ className, previewOverflow = 'clamp', ...props }: AudioTimeSliderProps = {}) {
  return (
    <TimeSlider.Root
      className={(state) => `media-slider media-audio-time-slider ${resolveClassName(className, state)}`}
      {...props}
    >
      <TimeSlider.Track render={<SliderTrack />}>
        <TimeSlider.Buffer render={<SliderBuffer />} />
        <TimeSlider.Fill render={<SliderFill />} />
      </TimeSlider.Track>
      <TimeSlider.Thumb render={<SliderThumb />} className={'media-audio-time-slider-thumb'} />
      <TimeSlider.Preview className={'media-slider-preview'} overflow={previewOverflow}>
        <div className={'media-audio-time-slider-preview-content'}>
          <TimeSlider.Value className={'media-audio-time-slider-value'} type="pointer" />
        </div>
      </TimeSlider.Preview>
    </TimeSlider.Root>
  );
}

interface AudioSettingsMenuProps extends MenuProps {
  className?: ClassValue;
}

type PlaybackRateButtonProps = Omit<PlaybackRateButtonPrimitive.Props, 'children'>;

function PlaybackRateButton({ className, ...props }: PlaybackRateButtonProps = {}) {
  return (
    <PlaybackRateButtonPrimitive
      render={<Button />}
      className={(state) => `media-playback-rate-button ${resolveClassName(className, state)}`}
      {...props}
    />
  );
}

type RadioItemProps = Menu.RadioItemProps;

function RadioItem({ children, className, ...props }: RadioItemProps) {
  return (
    <Menu.RadioItem className={(state) => `media-menu-radio-item ${resolveClassName(className, state)}`} {...props}>
      {children}
      <Menu.ItemIndicator forceMount className={'media-menu-item-indicator'}>
        <>
          <span className="contents theme-minimal:hidden">
            <CheckIconPrimitive className={'media-menu-radio-item-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CheckIconPrimitive2 className={'media-menu-radio-item-icon'} />
          </span>
        </>
      </Menu.ItemIndicator>
    </Menu.RadioItem>
  );
}

function AudioSettingsMenu({ className, ...props }: AudioSettingsMenuProps = {}) {
  return (
    <Menu.Root side="top" align="center" boundary="viewport" {...props}>
      <PlaybackRateRadioGroup.Root>
        <ButtonTooltip label={<TextPrimitive token={speedText.key}>{speedText.text}</TextPrimitive>} side="top">
          <Menu.Trigger render={<PlaybackRateButton />} />
        </ButtonTooltip>
        <Menu.Popup
          className={(state) =>
            `media-menu-popup media-audio-settings-menu-popup ${resolveClassName(className, state)}`
          }
        >
          <Menu.Content className={'media-menu-content'}>
            <PlaybackRateRadioGroup.Options
              className={'media-menu-radio-group'}
              renderItem={(props, item) => (
                <RadioItem {...props}>
                  <span>{item.label}</span>
                </RadioItem>
              )}
            ></PlaybackRateRadioGroup.Options>
          </Menu.Content>
        </Menu.Popup>
      </PlaybackRateRadioGroup.Root>
    </Menu.Root>
  );
}

interface VolumePopoverProps extends Omit<VolumePopoverPrimitive.RootProps, 'children'> {
  className?: string | undefined;
  orientation?: CoreVolumeSliderProps['orientation'];
  showTooltip?: boolean;
  popupClassName?: ClassValue;
}

type MuteButtonProps = Omit<MuteButtonPrimitive.Props, 'children'>;

function MuteButton({ className, ...props }: MuteButtonProps = {}) {
  return (
    <MuteButtonPrimitive
      render={<Button />}
      className={(state) => `media-mute-button ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeOffIconPrimitive className={'media-button-icon media-mute-button-off-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeOffIconPrimitive2 className={'media-button-icon media-mute-button-off-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeLowIconPrimitive className={'media-button-icon media-mute-button-low-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeLowIconPrimitive2 className={'media-button-icon media-mute-button-low-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeHighIconPrimitive className={'media-button-icon media-mute-button-high-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeHighIconPrimitive2 className={'media-button-icon media-mute-button-high-icon'} />
        </span>
      </>
    </MuteButtonPrimitive>
  );
}

type VolumeSliderProps = Omit<VolumeSliderPrimitive.RootProps, 'children'>;

function VolumeSlider({ className, ...props }: VolumeSliderProps = {}) {
  return (
    <VolumeSliderPrimitive.Root
      className={(state) => `media-slider media-volume-slider ${resolveClassName(className, state)}`}
      thumbAlignment="edge"
      {...props}
    >
      <VolumeSliderPrimitive.Track render={<SliderTrack />}>
        <VolumeSliderPrimitive.Fill render={<SliderFill />} />
      </VolumeSliderPrimitive.Track>
      <VolumeSliderPrimitive.Thumb render={<SliderThumb />} className={'media-volume-slider-thumb'} />
    </VolumeSliderPrimitive.Root>
  );
}

function DefaultAudioControls() {
  return (
    <Controls.Root visibility="always">
      <Controls.Content className={'media-controls media-controls-content'}>
        <Tooltip.Provider>
          <Controls.Group className={'media-controls-start'}>
            <AudioPlayButton />
            <SeekButton className={'media-audio-seek-button'} seconds={-10} />
            <SeekButton className={'media-audio-seek-button'} seconds={10} />
          </Controls.Group>

          <Controls.Group className={'media-time-slider-group'}>
            <Time.Value className={'media-time-current-value'} type="current" />
            <AudioTimeSlider previewOverflow="visible" />
            <Time.Value className={'media-time-remaining-value'} type="remaining" toggle />
          </Controls.Group>

          <Controls.Group className={'media-controls-end'}>
            <AudioSettingsMenu />
            <VolumePopover />
          </Controls.Group>
        </Tooltip.Provider>
      </Controls.Content>
    </Controls.Root>
  );
}

interface PlaybackHotkeysProps {
  disabled?: boolean | undefined;
}

function PlaybackHotkeys({ disabled = false }: PlaybackHotkeysProps = {}) {
  return (
    <>
      <Hotkey disabled={disabled} keys="Space" action="togglePaused" />
      <Hotkey disabled={disabled} keys="k" action="togglePaused" />
      <Hotkey disabled={disabled} keys="m" action="toggleMuted" />
      <Hotkey disabled={disabled} keys="ArrowRight" action="seekStep" value={5} />
      <Hotkey disabled={disabled} keys="ArrowLeft" action="seekStep" value={-5} />
      <Hotkey disabled={disabled} keys="l" action="seekStep" value={10} />
      <Hotkey disabled={disabled} keys="j" action="seekStep" value={-10} />
      <Hotkey disabled={disabled} keys="ArrowUp" action="volumeStep" value={0.05} />
      <Hotkey disabled={disabled} keys="ArrowDown" action="volumeStep" value={-0.05} />
      <Hotkey disabled={disabled} keys="0-9" action="seekToPercent" />
      <Hotkey disabled={disabled} keys="Home" action="seekToPercent" value={0} />
      <Hotkey disabled={disabled} keys="End" action="seekToPercent" value={100} />
      <Hotkey disabled={disabled} keys=">" action="speedUp" />
      <Hotkey disabled={disabled} keys="<" action="speedDown" />
    </>
  );
}

type StatusAnnouncerProps = Omit<StatusAnnouncerPrimitive.Props, 'children'>;

function StatusAnnouncer({ className, ...props }: StatusAnnouncerProps = {}) {
  return (
    <StatusAnnouncerPrimitive
      className={(state) => `media-status-announcer ${resolveClassName(className, state)}`}
      {...props}
    />
  );
}

function DefaultAudioSkin({ children, className, ...props }: DefaultAudioSkinProps = {}) {
  return (
    <Container className={`media-skin media-skin--audio media-audio-skin ${className ?? ''}`} {...props}>
      {children}
      <AudioErrorDialog />
      <DefaultAudioControls />
      <PlaybackHotkeys />
      <StatusAnnouncer />
    </Container>
  );
}

const Skin = DefaultAudioSkin;

// ================================================================
// Player
// ================================================================

export interface AudioPlayerProps {
  children?: ReactNode;
  style?: CSSProperties;
  className?: string;
}

export function AudioSkin(props: AudioSkinProps) {
  return <Skin {...props} />;
}

// ================================================================
// Components
// ================================================================

function Button({ className, ...props }: ButtonProps) {
  return <button className={`media-button ${className ?? ''}`} {...props} />;
}

type SliderTrackProps = ComponentProps<'div'>;

function SliderTrack({ className, ...props }: SliderTrackProps) {
  return <div className={`media-slider-track ${className ?? ''}`} {...props} />;
}

type SliderBufferProps = ComponentProps<'div'>;

function SliderBuffer({ className, ...props }: SliderBufferProps) {
  return <div className={`media-slider-buffer ${className ?? ''}`} {...props} />;
}

type SliderFillProps = ComponentProps<'div'>;

function SliderFill({ className, ...props }: SliderFillProps) {
  return <div className={`media-slider-fill ${className ?? ''}`} {...props} />;
}

type SliderThumbProps = ComponentProps<'div'>;

function SliderThumb({ className, ...props }: SliderThumbProps) {
  return <div className={`media-slider-thumb ${className ?? ''}`} {...props} />;
}

function VolumePopover({
  className,
  popupClassName,
  showTooltip = false,
  side = 'top',
  orientation = 'vertical',
  ...props
}: VolumePopoverProps = {}) {
  return (
    <VolumePopoverPrimitive.Root openOnHover delay={200} closeDelay={100} side={side} {...props}>
      <ButtonTooltip delay={0} disabled={!showTooltip} sticky side="top">
        <VolumePopoverPrimitive.Trigger render={<MuteButton className={className} />} />
      </ButtonTooltip>
      <VolumePopoverPrimitive.Popup className={`media-volume-popover ${popupClassName}`}>
        <VolumeSlider orientation={orientation} />
      </VolumePopoverPrimitive.Popup>
    </VolumePopoverPrimitive.Root>
  );
}

Minimal video skin

'use client';

import { type SVGProps, type ComponentProps, type ReactElement, type ReactNode, type CSSProperties } from 'react';
import { SpinnerIcon as SpinnerIconPrimitive, ChevronIcon as ChevronIconPrimitive, CaptionsOnIcon as CaptionsOnIconPrimitive, CaptionsOffIcon as CaptionsOffIconPrimitive, FullscreenEnterIcon as FullscreenEnterIconPrimitive, FullscreenExitIcon as FullscreenExitIconPrimitive, PipEnterIcon as PipEnterIconPrimitive, PipExitIcon as PipExitIconPrimitive, PlayIcon as PlayIconPrimitive, PauseIcon as PauseIconPrimitive, VolumeHighIcon as VolumeHighIconPrimitive, VolumeLowIcon as VolumeLowIconPrimitive, VolumeOffIcon as VolumeOffIconPrimitive, AirPlayEnterIcon as AirPlayEnterIconPrimitive, AirPlayExitIcon as AirPlayExitIconPrimitive, CastEnterIcon as CastEnterIconPrimitive, CastExitIcon as CastExitIconPrimitive, RestartIcon as RestartIconPrimitive, CheckIcon as CheckIconPrimitive, GearIcon as GearIconPrimitive } from '@videojs/react/icons';
import { SpinnerIcon as SpinnerIconPrimitive2, ChevronIcon as ChevronIconPrimitive2, CaptionsOnIcon as CaptionsOnIconPrimitive2, CaptionsOffIcon as CaptionsOffIconPrimitive2, FullscreenEnterIcon as FullscreenEnterIconPrimitive2, FullscreenExitIcon as FullscreenExitIconPrimitive2, PipEnterIcon as PipEnterIconPrimitive2, PipExitIcon as PipExitIconPrimitive2, PlayIcon as PlayIconPrimitive2, PauseIcon as PauseIconPrimitive2, VolumeHighIcon as VolumeHighIconPrimitive2, VolumeLowIcon as VolumeLowIconPrimitive2, VolumeOffIcon as VolumeOffIconPrimitive2, AirPlayEnterIcon as AirPlayEnterIconPrimitive2, AirPlayExitIcon as AirPlayExitIconPrimitive2, CastEnterIcon as CastEnterIconPrimitive2, CastExitIcon as CastExitIconPrimitive2, RestartIcon as RestartIconPrimitive2, CheckIcon as CheckIconPrimitive2, GearIcon as GearIconPrimitive2 } from '@videojs/react/icons/minimal';
import { BufferingIndicator as BufferingIndicatorPrimitive, ErrorDialog as ErrorDialogPrimitive, Container as ContainerPrimitive, Poster as PosterPrimitive, Gesture, Hotkey, SeekIndicator as SeekIndicatorPrimitive, StatusAnnouncer as StatusAnnouncerPrimitive, StatusIndicator as StatusIndicatorPrimitive, VolumeIndicator as VolumeIndicatorPrimitive, Tooltip, AirPlayButton as AirPlayButtonPrimitive, CaptionsButton as CaptionsButtonPrimitive, CastButton as CastButtonPrimitive, FullscreenButton as FullscreenButtonPrimitive, PiPButton as PiPButtonPrimitive, PlayButton as PlayButtonPrimitive, MuteButton as MuteButtonPrimitive, VolumeSlider as VolumeSliderPrimitive, VolumePopover as VolumePopoverPrimitive, TimeSlider as TimeSliderPrimitive, Slider, Menu, Text as TextPrimitive, Controls, Time } from '@videojs/react';
import { Video } from '@videojs/react/video';
import './player.css';
import { Player } from './player';
import { type VolumeSliderProps as CoreVolumeSliderProps, type SliderPreviewOverflow } from '@videojs/core';
import { audioText, captionsText, speedText, qualityText, settingsText } from '@videojs/core/i18n/text/menu';
import { type Poster } from '@videojs/react';

type ContainerProps = ContainerPrimitive.Props;

function Container({ children, className, ...props }: ContainerProps) {
  return (
    <ContainerPrimitive className={`media-container ${className ?? ''}`} {...props}>
      {children}
    </ContainerPrimitive>
  );
}

interface PosterProps extends Omit<PosterPrimitive.Props, 'children' | 'render'> {
  children?: PosterPrimitive.Props['render'];
}

function Poster({ children, className, src, ...props }: PosterProps = {}) {
  return (
    <PosterPrimitive
      render={children}
      className={(state) => `media-poster ${resolveClassName(className, state)}`}
      src={src}
      {...props}
    />
  );
}

interface MinimalVideoSkinProps extends Omit<NonNullable<ComponentProps<typeof Container>>, 'children'> {
  src: string;
  poster?: string | undefined;
  poster?: string | NonNullable<ComponentProps<typeof Poster>>['children'];
}

type BufferingIndicatorProps = Omit<BufferingIndicatorPrimitive.Props, 'children'>;

function BufferingIndicator({ className, ...props }: BufferingIndicatorProps = {}) {
  return (
    <BufferingIndicatorPrimitive
      className={(state) => `media-buffering-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <SpinnerIconPrimitive className={'media-buffering-indicator-spinner-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <SpinnerIconPrimitive2 className={'media-buffering-indicator-spinner-icon'} />
        </span>
      </>
    </BufferingIndicatorPrimitive>
  );
}

type ErrorDialogProps = Omit<ErrorDialogPrimitive.PopupProps, 'children'>;

type ButtonProps = ComponentProps<'button'>;

type PlayButtonProps = Omit<PlayButtonPrimitive.Props, 'children'>;

interface ButtonTooltipProps extends Tooltip.RootProps {
  children: ReactElement;
  label?: ReactNode;
}

function ButtonTooltip({ children, label, ...props }: ButtonTooltipProps) {
  return (
    <Tooltip.Root {...props}>
      <Tooltip.Trigger render={children} />
      <Tooltip.Popup className={'media-tooltip'}>
        {label ?? <Tooltip.Label />}
        {!label && <Tooltip.Shortcut className={'media-tooltip-shortcut'} />}
      </Tooltip.Popup>
    </Tooltip.Root>
  );
}

function PlayButton({ className, ...props }: PlayButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <PlayButtonPrimitive
        render={<Button />}
        className={(state) => `media-play-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <RestartIconPrimitive className={'media-button-icon media-play-button-restart-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <RestartIconPrimitive2 className={'media-button-icon media-play-button-restart-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PlayIconPrimitive className={'media-button-icon media-play-button-play-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PlayIconPrimitive2 className={'media-button-icon media-play-button-play-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PauseIconPrimitive className={'media-button-icon media-play-button-pause-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PauseIconPrimitive2 className={'media-button-icon media-play-button-pause-icon'} />
          </span>
        </>
      </PlayButtonPrimitive>
    </ButtonTooltip>
  );
}

interface VolumePopoverProps extends Omit<VolumePopoverPrimitive.RootProps, 'children'> {
  className?: string | undefined;
  orientation?: CoreVolumeSliderProps['orientation'];
  showTooltip?: boolean;
  popupClassName?: ClassValue;
}

type MuteButtonProps = Omit<MuteButtonPrimitive.Props, 'children'>;

function MuteButton({ className, ...props }: MuteButtonProps = {}) {
  return (
    <MuteButtonPrimitive
      render={<Button />}
      className={(state) => `media-mute-button ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeOffIconPrimitive className={'media-button-icon media-mute-button-off-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeOffIconPrimitive2 className={'media-button-icon media-mute-button-off-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeLowIconPrimitive className={'media-button-icon media-mute-button-low-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeLowIconPrimitive2 className={'media-button-icon media-mute-button-low-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeHighIconPrimitive className={'media-button-icon media-mute-button-high-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeHighIconPrimitive2 className={'media-button-icon media-mute-button-high-icon'} />
        </span>
      </>
    </MuteButtonPrimitive>
  );
}

type VolumeSliderProps = Omit<VolumeSliderPrimitive.RootProps, 'children'>;

function VolumeSlider({ className, ...props }: VolumeSliderProps = {}) {
  return (
    <VolumeSliderPrimitive.Root
      className={(state) => `media-slider media-volume-slider ${resolveClassName(className, state)}`}
      thumbAlignment="edge"
      {...props}
    >
      <VolumeSliderPrimitive.Track render={<SliderTrack />}>
        <VolumeSliderPrimitive.Fill render={<SliderFill />} />
      </VolumeSliderPrimitive.Track>
      <VolumeSliderPrimitive.Thumb render={<SliderThumb />} className={'media-volume-slider-thumb'} />
    </VolumeSliderPrimitive.Root>
  );
}

interface TimeSliderProps extends Omit<TimeSliderPrimitive.RootProps, 'children'> {
  previewOverflow?: SliderPreviewOverflow | undefined;
}

function TimeSlider({ className, previewOverflow = 'visible', ...props }: TimeSliderProps = {}) {
  return (
    <TimeSliderPrimitive.Root
      className={(state) => `media-slider media-time-slider ${resolveClassName(className, state)}`}
      {...props}
    >
      <TimeSliderPrimitive.Chapters
        className={'media-time-slider-chapters'}
        renderChapter={(props) => (
          <div className={'media-time-slider-chapter'} {...props}>
            <TimeSliderPrimitive.Track render={<SliderTrack />} className={'media-time-slider-chapter-track'}>
              <TimeSliderPrimitive.Buffer render={<SliderBuffer />} />
              <TimeSliderPrimitive.Fill render={<SliderFill />} />
            </TimeSliderPrimitive.Track>
          </div>
        )}
      ></TimeSliderPrimitive.Chapters>
      <TimeSliderPrimitive.Thumb render={<SliderThumb />} className={'media-time-slider-thumb'} />
      <TimeSliderPrimitive.Preview className={'media-slider-preview'} overflow={previewOverflow}>
        <div className={'media-slider-thumbnail'}>
          <Slider.Thumbnail className={'media-slider-thumbnail-image'} />
          <>
            <span className="contents theme-minimal:hidden">
              <SpinnerIconPrimitive className={'media-slider-thumbnail-spinner-icon'} />
            </span>
            <span className="hidden theme-minimal:contents">
              <SpinnerIconPrimitive2 className={'media-slider-thumbnail-spinner-icon'} />
            </span>
          </>
        </div>
        <div className={'media-time-slider-preview-content'}>
          <TimeSliderPrimitive.ChapterTitle className={'media-time-slider-chapter-title'} />
          <TimeSliderPrimitive.Value className={'media-time-slider-value'} type="pointer" />
        </div>
      </TimeSliderPrimitive.Preview>
    </TimeSliderPrimitive.Root>
  );
}

type CaptionsButtonProps = Omit<CaptionsButtonPrimitive.Props, 'children'>;

function CaptionsButton({ className, ...props }: CaptionsButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <CaptionsButtonPrimitive
        render={<Button />}
        className={(state) => `media-captions-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <CaptionsOffIconPrimitive className={'media-button-icon media-captions-button-off-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CaptionsOffIconPrimitive2 className={'media-button-icon media-captions-button-off-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <CaptionsOnIconPrimitive className={'media-button-icon media-captions-button-on-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CaptionsOnIconPrimitive2 className={'media-button-icon media-captions-button-on-icon'} />
          </span>
        </>
      </CaptionsButtonPrimitive>
    </ButtonTooltip>
  );
}

interface SettingsMenuProps extends Menu.RootProps {
  className?: string | undefined;
}

function SettingsMenu({ children, className, ...props }: SettingsMenuProps) {
  return (
    <Menu.Root side="top" align="center" {...props}>
      <ButtonTooltip label={<TextPrimitive token={settingsText.key}>{settingsText.text}</TextPrimitive>} side="top">
        <Menu.Trigger render={<Button />} className={'media-settings-menu-trigger'}>
          <>
            <span className="contents theme-minimal:hidden">
              <GearIconPrimitive className={'media-button-icon media-settings-menu-trigger-icon'} />
            </span>
            <span className="hidden theme-minimal:contents">
              <GearIconPrimitive2 className={'media-button-icon media-settings-menu-trigger-icon'} />
            </span>
          </>
          <TextPrimitive className={'media-settings-menu-trigger-label'} token={settingsText.key}>
            {settingsText.text}
          </TextPrimitive>
        </Menu.Trigger>
      </ButtonTooltip>
      <Menu.Popup keepMounted className={(state) => `media-menu-popup ${resolveClassName(className, state)}`}>
        <Menu.Content className={'media-menu-content'}>{children}</Menu.Content>
      </Menu.Popup>
    </Menu.Root>
  );
}

type VideoSettingsMenuProps = Omit<NonNullable<ComponentProps<typeof SettingsMenu>>, 'children'>;

interface QualityMenuProps extends MenuProps {
  className?: ClassValue;
}

interface MenuChevronProps {
  back?: boolean;
  className?: ClassValue;
}

function MenuChevron({ back = false, className }: MenuChevronProps = {}) {
  return (
    <>
      <span className="contents theme-minimal:hidden">
        <ChevronIconPrimitive
          className={`${back ? 'media-menu-back-chevron' : 'media-menu-forward-chevron'} ${className ?? ''}`}
        />
      </span>
      <span className="hidden theme-minimal:contents">
        <ChevronIconPrimitive2
          className={`${back ? 'media-menu-back-chevron' : 'media-menu-forward-chevron'} ${className ?? ''}`}
        />
      </span>
    </>
  );
}

type RadioItemProps = Menu.RadioItemProps;

function RadioItem({ children, className, ...props }: RadioItemProps) {
  return (
    <Menu.RadioItem className={(state) => `media-menu-radio-item ${resolveClassName(className, state)}`} {...props}>
      {children}
      <Menu.ItemIndicator forceMount className={'media-menu-item-indicator'}>
        <>
          <span className="contents theme-minimal:hidden">
            <CheckIconPrimitive className={'media-menu-radio-item-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CheckIconPrimitive2 className={'media-menu-radio-item-icon'} />
          </span>
        </>
      </Menu.ItemIndicator>
    </Menu.RadioItem>
  );
}

function QualityMenu({ className, ...props }: QualityMenuProps = {}) {
  return (
    <Menu.Root {...props}>
      <QualityRadioGroup.Root>
        <Menu.Trigger className={'media-menu-trigger-item'}>
          <>
            <span className="contents theme-minimal:hidden">
              <SwitchesIconPrimitive className={'media-menu-trigger-item-icon'} />
            </span>
            <span className="hidden theme-minimal:contents">
              <SwitchesIconPrimitive2 className={'media-menu-trigger-item-icon'} />
            </span>
          </>
          <TextPrimitive token={qualityText.key}>{qualityText.text}</TextPrimitive>
          <span className={'media-menu-hint'}>
            <QualityRadioGroup.Value className={'media-menu-hint-label'} />
            <MenuChevron />
          </span>
        </Menu.Trigger>
        <Menu.Content className={(state) => `media-menu-content ${resolveClassName(className, state)}`}>
          <Menu.Item className={'media-menu-back-item'}>
            <MenuChevron back />
            <TextPrimitive token={qualityText.key}>{qualityText.text}</TextPrimitive>
          </Menu.Item>
          <Menu.Separator className={'media-menu-separator'} />
          <QualityRadioGroup.Options
            className={'media-menu-radio-group'}
            renderItem={(props, item) => (
              <RadioItem {...props}>
                <span>
                  <span>{item.label}</span>
                  {item.tier ? <sup className={'media-menu-tier'}>{item.tier}</sup> : null}
                </span>
                {item.badge ? <span className={'media-menu-badge'}>{item.badge}</span> : null}
              </RadioItem>
            )}
          ></QualityRadioGroup.Options>
        </Menu.Content>
      </QualityRadioGroup.Root>
    </Menu.Root>
  );
}

interface AudioTrackMenuProps extends MenuProps {
  className?: ClassValue;
}

function AudioTrackMenu({ className, ...props }: AudioTrackMenuProps = {}) {
  return (
    <Menu.Root {...props}>
      <AudioTrackRadioGroup.Root>
        <Menu.Trigger className={'media-menu-trigger-item'}>
          <>
            <span className="contents theme-minimal:hidden">
              <SpeechIconPrimitive className={'media-menu-trigger-item-icon'} />
            </span>
            <span className="hidden theme-minimal:contents">
              <SpeechIconPrimitive2 className={'media-menu-trigger-item-icon'} />
            </span>
          </>
          <TextPrimitive token={audioText.key}>{audioText.text}</TextPrimitive>
          <span className={'media-menu-hint'}>
            <AudioTrackRadioGroup.Value className={'media-menu-hint-label'} />
            <MenuChevron />
          </span>
        </Menu.Trigger>
        <Menu.Content className={(state) => `media-menu-content ${resolveClassName(className, state)}`}>
          <Menu.Item className={'media-menu-back-item'}>
            <MenuChevron back />
            <TextPrimitive token={audioText.key}>{audioText.text}</TextPrimitive>
          </Menu.Item>
          <Menu.Separator className={'media-menu-separator'} />
          <AudioTrackRadioGroup.Options
            className={'media-menu-radio-group'}
            renderItem={(props, item) => (
              <RadioItem {...props}>
                <span>{item.label}</span>
              </RadioItem>
            )}
          ></AudioTrackRadioGroup.Options>
        </Menu.Content>
      </AudioTrackRadioGroup.Root>
    </Menu.Root>
  );
}

interface PlaybackRateSubmenuProps extends MenuProps {
  className?: ClassValue;
}

function PlaybackRateSubmenu({ className, ...props }: PlaybackRateSubmenuProps = {}) {
  return (
    <Menu.Root {...props}>
      <PlaybackRateRadioGroup.Root>
        <Menu.Trigger className={'media-menu-trigger-item'}>
          <>
            <span className="contents theme-minimal:hidden">
              <SpeedIconPrimitive className={'media-menu-trigger-item-icon'} />
            </span>
            <span className="hidden theme-minimal:contents">
              <SpeedIconPrimitive2 className={'media-menu-trigger-item-icon'} />
            </span>
          </>
          <TextPrimitive token={speedText.key}>{speedText.text}</TextPrimitive>
          <span className={'media-menu-hint'}>
            <PlaybackRateRadioGroup.Value className={'media-menu-hint-label'} />
            <MenuChevron />
          </span>
        </Menu.Trigger>
        <Menu.Content className={(state) => `media-menu-content ${resolveClassName(className, state)}`}>
          <Menu.Item className={'media-menu-back-item'}>
            <MenuChevron back />
            <TextPrimitive token={speedText.key}>{speedText.text}</TextPrimitive>
          </Menu.Item>
          <Menu.Separator className={'media-menu-separator'} />
          <PlaybackRateRadioGroup.Options
            className={'media-menu-radio-group'}
            renderItem={(props, item) => (
              <RadioItem {...props}>
                <span>{item.label}</span>
              </RadioItem>
            )}
          ></PlaybackRateRadioGroup.Options>
        </Menu.Content>
      </PlaybackRateRadioGroup.Root>
    </Menu.Root>
  );
}

interface CaptionsSubmenuProps extends MenuProps {
  className?: ClassValue;
}

function CaptionsSubmenu({ className, ...props }: CaptionsSubmenuProps = {}) {
  return (
    <Menu.Root {...props}>
      <CaptionsRadioGroup.Root>
        <Menu.Trigger className={'media-menu-trigger-item'}>
          <>
            <span className="contents theme-minimal:hidden">
              <CaptionsOffIconPrimitive className={'media-menu-trigger-item-icon'} />
            </span>
            <span className="hidden theme-minimal:contents">
              <CaptionsOffIconPrimitive2 className={'media-menu-trigger-item-icon'} />
            </span>
          </>
          <TextPrimitive token={captionsText.key}>{captionsText.text}</TextPrimitive>
          <span className={'media-menu-hint'}>
            <CaptionsRadioGroup.Value className={'media-menu-hint-label'} />
            <MenuChevron />
          </span>
        </Menu.Trigger>
        <Menu.Content className={(state) => `media-menu-content ${resolveClassName(className, state)}`}>
          <Menu.Item className={'media-menu-back-item'}>
            <MenuChevron back />
            <TextPrimitive token={captionsText.key}>{captionsText.text}</TextPrimitive>
          </Menu.Item>
          <Menu.Separator className={'media-menu-separator'} />
          <CaptionsRadioGroup.Options
            className={'media-menu-radio-group'}
            renderItem={(props, item) => (
              <RadioItem {...props}>
                <span>{item.label}</span>
              </RadioItem>
            )}
          ></CaptionsRadioGroup.Options>
        </Menu.Content>
      </CaptionsRadioGroup.Root>
    </Menu.Root>
  );
}

function VideoSettingsMenu(props: VideoSettingsMenuProps = {}) {
  return (
    <SettingsMenu {...props}>
      <QualityMenu />
      <AudioTrackMenu />
      <PlaybackRateSubmenu />
      <CaptionsSubmenu />
    </SettingsMenu>
  );
}

type CastButtonProps = Omit<CastButtonPrimitive.Props, 'children'>;

function CastButton({ className, ...props }: CastButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <CastButtonPrimitive
        render={<Button />}
        className={(state) => `media-cast-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <CastEnterIconPrimitive className={'media-button-icon media-cast-button-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CastEnterIconPrimitive2 className={'media-button-icon media-cast-button-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <CastExitIconPrimitive className={'media-button-icon media-cast-button-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CastExitIconPrimitive2 className={'media-button-icon media-cast-button-exit-icon'} />
          </span>
        </>
      </CastButtonPrimitive>
    </ButtonTooltip>
  );
}

type AirPlayButtonProps = Omit<AirPlayButtonPrimitive.Props, 'children'>;

function AirPlayButton({ className, ...props }: AirPlayButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <AirPlayButtonPrimitive
        render={<Button />}
        className={(state) => `media-airplay-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <AirPlayEnterIconPrimitive className={'media-button-icon media-airplay-button-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <AirPlayEnterIconPrimitive2 className={'media-button-icon media-airplay-button-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <AirPlayExitIconPrimitive className={'media-button-icon media-airplay-button-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <AirPlayExitIconPrimitive2 className={'media-button-icon media-airplay-button-exit-icon'} />
          </span>
        </>
      </AirPlayButtonPrimitive>
    </ButtonTooltip>
  );
}

type PiPButtonProps = Omit<PiPButtonPrimitive.Props, 'children'>;

function PiPButton({ className, ...props }: PiPButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <PiPButtonPrimitive
        render={<Button />}
        className={(state) => `media-pip-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <PipEnterIconPrimitive className={'media-button-icon media-pip-button-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PipEnterIconPrimitive2 className={'media-button-icon media-pip-button-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PipExitIconPrimitive className={'media-button-icon media-pip-button-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PipExitIconPrimitive2 className={'media-button-icon media-pip-button-exit-icon'} />
          </span>
        </>
      </PiPButtonPrimitive>
    </ButtonTooltip>
  );
}

type FullscreenButtonProps = Omit<FullscreenButtonPrimitive.Props, 'children'>;

function FullscreenButton({ className, ...props }: FullscreenButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <FullscreenButtonPrimitive
        render={<Button />}
        className={(state) => `media-fullscreen-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <FullscreenEnterIconPrimitive className={'media-button-icon media-fullscreen-button-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <FullscreenEnterIconPrimitive2 className={'media-button-icon media-fullscreen-button-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <FullscreenExitIconPrimitive className={'media-button-icon media-fullscreen-button-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <FullscreenExitIconPrimitive2 className={'media-button-icon media-fullscreen-button-exit-icon'} />
          </span>
        </>
      </FullscreenButtonPrimitive>
    </ButtonTooltip>
  );
}

function MinimalVideoControls() {
  return (
    <Controls.Root>
      <Controls.Backdrop className={'media-controls__backdrop'} />
      <Controls.Content className={'media-controls media-controls'}>
        <Tooltip.Provider>
          <Controls.Group className={'media-controls-start'}>
            <PlayButton />
            <VolumePopover showTooltip side="right" orientation="horizontal" />
          </Controls.Group>

          <Controls.Group className={'media-time-slider-group'}>
            <Time.Group className={'media-time-group'}>
              <Time.Value className={'media-time-current-value'} type="current" toggle />
              <Time.Separator className={'media-time-separator'} />
              <Time.Value className={'media-time-duration-value'} type="duration" />
            </Time.Group>
            <TimeSlider previewOverflow="clamp" />
          </Controls.Group>

          <Controls.Group className={'media-controls-end'}>
            <CaptionsButton />
            <VideoSettingsMenu />
            <Controls.Group className={'media-controls-trailing'}>
              <CastButton />
              <AirPlayButton />
              <PiPButton />
              <FullscreenButton />
            </Controls.Group>
          </Controls.Group>
        </Tooltip.Provider>
      </Controls.Content>
    </Controls.Root>
  );
}

interface VideoHotkeysProps {
  disabled?: boolean | undefined;
}

interface PlaybackHotkeysProps {
  disabled?: boolean | undefined;
}

function PlaybackHotkeys({ disabled = false }: PlaybackHotkeysProps = {}) {
  return (
    <>
      <Hotkey disabled={disabled} keys="Space" action="togglePaused" />
      <Hotkey disabled={disabled} keys="k" action="togglePaused" />
      <Hotkey disabled={disabled} keys="m" action="toggleMuted" />
      <Hotkey disabled={disabled} keys="ArrowRight" action="seekStep" value={5} />
      <Hotkey disabled={disabled} keys="ArrowLeft" action="seekStep" value={-5} />
      <Hotkey disabled={disabled} keys="l" action="seekStep" value={10} />
      <Hotkey disabled={disabled} keys="j" action="seekStep" value={-10} />
      <Hotkey disabled={disabled} keys="ArrowUp" action="volumeStep" value={0.05} />
      <Hotkey disabled={disabled} keys="ArrowDown" action="volumeStep" value={-0.05} />
      <Hotkey disabled={disabled} keys="0-9" action="seekToPercent" />
      <Hotkey disabled={disabled} keys="Home" action="seekToPercent" value={0} />
      <Hotkey disabled={disabled} keys="End" action="seekToPercent" value={100} />
      <Hotkey disabled={disabled} keys=">" action="speedUp" />
      <Hotkey disabled={disabled} keys="<" action="speedDown" />
    </>
  );
}

function VideoHotkeys({ disabled = false }: VideoHotkeysProps = {}) {
  return (
    <>
      <PlaybackHotkeys disabled={disabled} />
      <Hotkey disabled={disabled} keys="f" action="toggleFullscreen" />
      <Hotkey disabled={disabled} keys="c" action="toggleSubtitles" />
      <Hotkey disabled={disabled} keys="i" action="togglePictureInPicture" />
    </>
  );
}

interface VideoGesturesProps {
  disabled?: boolean | undefined;
}

function VideoGestures({ disabled = false }: VideoGesturesProps = {}) {
  return (
    <>
      <Gesture disabled={disabled} type="tap" action="togglePaused" pointer="mouse" region="center" />
      <Gesture disabled={disabled} type="tap" action="toggleControls" pointer="touch" />
      <Gesture disabled={disabled} type="doubletap" action="seekStep" value={-10} region="left" />
      <Gesture disabled={disabled} type="doubletap" action="toggleFullscreen" region="center" />
      <Gesture disabled={disabled} type="doubletap" action="seekStep" value={10} region="right" />
    </>
  );
}

type VideoStatusIndicatorsProps = Omit<ComponentProps<'div'>, 'children'>;

type StatusAnnouncerProps = Omit<StatusAnnouncerPrimitive.Props, 'children'>;

function StatusAnnouncer({ className, ...props }: StatusAnnouncerProps = {}) {
  return (
    <StatusAnnouncerPrimitive
      className={(state) => `media-status-announcer ${resolveClassName(className, state)}`}
      {...props}
    />
  );
}

type VolumeIndicatorProps = Omit<VolumeIndicatorPrimitive.RootProps, 'children'>;

function VolumeIndicator({ className, ...props }: VolumeIndicatorProps = {}) {
  return (
    <VolumeIndicatorPrimitive.Root
      className={(state) => `media-volume-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <VolumeIndicatorPrimitive.Fill className={'media-volume-indicator-fill'}>
        <>
          <span className="contents theme-minimal:hidden">
            <VolumeHighIconPrimitive className={'media-volume-indicator-high-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <VolumeHighIconPrimitive2 className={'media-volume-indicator-high-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <VolumeLowIconPrimitive className={'media-volume-indicator-low-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <VolumeLowIconPrimitive2 className={'media-volume-indicator-low-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <VolumeOffIconPrimitive className={'media-volume-indicator-off-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <VolumeOffIconPrimitive2 className={'media-volume-indicator-off-icon'} />
          </span>
        </>
        <VolumeIndicatorPrimitive.Value className={'media-volume-indicator-value'} />
      </VolumeIndicatorPrimitive.Fill>
    </VolumeIndicatorPrimitive.Root>
  );
}

type StatusIndicatorProps = Omit<StatusIndicatorPrimitive.RootProps, 'children'>;

const TOP_STATUS_ACTIONS = ['toggleSubtitles', 'toggleFullscreen', 'togglePictureInPicture'] as const;

function StatusIndicator({ className, ...props }: StatusIndicatorProps = {}) {
  return (
    <StatusIndicatorPrimitive.Root
      actions={TOP_STATUS_ACTIONS}
      className={(state) => `media-status-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <div className={'media-status-indicator-content'}>
        <>
          <span className="contents theme-minimal:hidden">
            <CaptionsOnIconPrimitive className={'media-status-indicator-captions-on-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CaptionsOnIconPrimitive2 className={'media-status-indicator-captions-on-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <CaptionsOffIconPrimitive className={'media-status-indicator-captions-off-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CaptionsOffIconPrimitive2 className={'media-status-indicator-captions-off-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <FullscreenEnterIconPrimitive className={'media-status-indicator-fullscreen-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <FullscreenEnterIconPrimitive2 className={'media-status-indicator-fullscreen-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <FullscreenExitIconPrimitive className={'media-status-indicator-fullscreen-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <FullscreenExitIconPrimitive2 className={'media-status-indicator-fullscreen-exit-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PipEnterIconPrimitive className={'media-status-indicator-pip-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PipEnterIconPrimitive2 className={'media-status-indicator-pip-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PipExitIconPrimitive className={'media-status-indicator-pip-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PipExitIconPrimitive2 className={'media-status-indicator-pip-exit-icon'} />
          </span>
        </>
        <StatusIndicatorPrimitive.Value className={'media-status-indicator-value'} />
      </div>
    </StatusIndicatorPrimitive.Root>
  );
}

type SeekIndicatorProps = Omit<SeekIndicatorPrimitive.RootProps, 'children'>;

function SeekIndicator({ className, ...props }: SeekIndicatorProps = {}) {
  return (
    <SeekIndicatorPrimitive.Root
      className={(state) => `media-seek-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <ChevronIconPrimitive className={'media-seek-indicator-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <ChevronIconPrimitive2 className={'media-seek-indicator-icon'} />
        </span>
      </>
      <SeekIndicatorPrimitive.Value className={'media-seek-indicator-value'} />
    </SeekIndicatorPrimitive.Root>
  );
}

type PlaybackStatusIndicatorProps = Omit<StatusIndicatorPrimitive.RootProps, 'children'>;

const PLAYBACK_STATUS_ACTIONS = ['togglePaused'] as const;

function PlaybackStatusIndicator({ className, ...props }: PlaybackStatusIndicatorProps = {}) {
  return (
    <StatusIndicatorPrimitive.Root
      actions={PLAYBACK_STATUS_ACTIONS}
      className={(state) => `media-playback-status-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <PlayIconPrimitive className={'media-playback-status-indicator-play-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <PlayIconPrimitive2 className={'media-playback-status-indicator-play-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <PauseIconPrimitive className={'media-playback-status-indicator-pause-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <PauseIconPrimitive2 className={'media-playback-status-indicator-pause-icon'} />
        </span>
      </>
    </StatusIndicatorPrimitive.Root>
  );
}

function VideoStatusIndicators({ className, ...props }: VideoStatusIndicatorsProps = {}) {
  return (
    <>
      <StatusAnnouncer />
      <div className={`media-video-status-indicators ${className ?? ''}`} {...props}>
        <VolumeIndicator />
        <StatusIndicator />
        <SeekIndicator />
        <PlaybackStatusIndicator />
      </div>
    </>
  );
}

function MinimalVideoSkin({ children, className, poster, ...props }: MinimalVideoSkinProps = {}) {
  const isPosterString = typeof poster === 'string';

  return (
    <Container className={`media-skin media-skin--minimal media-skin--video ${className ?? ''}`} {...props}>
      {children}
      <Poster src={isPosterString ? poster : undefined}>{isPosterString ? undefined : poster}</Poster>
      <BufferingIndicator />
      <ErrorDialog />

      <MinimalVideoControls />

      <VideoHotkeys />
      <VideoGestures />
      <VideoStatusIndicators />
    </Container>
  );
}

const Skin = MinimalVideoSkin;

// ================================================================
// Player
// ================================================================

export interface VideoPlayerProps {
  children?: ReactNode;
  style?: CSSProperties;
  className?: string;
  renderPoster?: Poster.Props['render'];
}

export function MinimalVideoSkin({ renderPoster, ...props }: MinimalVideoSkinProps) {
  return <Skin poster={renderPoster} {...props} />;
}

// ================================================================
// Components
// ================================================================

function Button({ className, ...props }: ButtonProps) {
  return <button className={`media-button ${className ?? ''}`} {...props} />;
}

type SliderTrackProps = ComponentProps<'div'>;

function SliderTrack({ className, ...props }: SliderTrackProps) {
  return <div className={`media-slider-track ${className ?? ''}`} {...props} />;
}

type SliderFillProps = ComponentProps<'div'>;

function SliderFill({ className, ...props }: SliderFillProps) {
  return <div className={`media-slider-fill ${className ?? ''}`} {...props} />;
}

type SliderThumbProps = ComponentProps<'div'>;

function SliderThumb({ className, ...props }: SliderThumbProps) {
  return <div className={`media-slider-thumb ${className ?? ''}`} {...props} />;
}

function VolumePopover({
  className,
  popupClassName,
  showTooltip = false,
  side = 'top',
  orientation = 'vertical',
  ...props
}: VolumePopoverProps = {}) {
  return (
    <VolumePopoverPrimitive.Root openOnHover delay={200} closeDelay={100} side={side} {...props}>
      <ButtonTooltip delay={0} disabled={!showTooltip} sticky side="top">
        <VolumePopoverPrimitive.Trigger render={<MuteButton className={className} />} />
      </ButtonTooltip>
      <VolumePopoverPrimitive.Popup className={`media-volume-popover ${popupClassName}`}>
        <VolumeSlider orientation={orientation} />
      </VolumePopoverPrimitive.Popup>
    </VolumePopoverPrimitive.Root>
  );
}

type SliderBufferProps = ComponentProps<'div'>;

function SliderBuffer({ className, ...props }: SliderBufferProps) {
  return <div className={`media-slider-buffer ${className ?? ''}`} {...props} />;
}

// ================================================================
// Error Dialog
// ================================================================

function ErrorDialog({ className, ...props }: ErrorDialogProps = {}) {
  return (
    <ErrorDialogPrimitive.Root>
      <ErrorDialogPrimitive.Backdrop className={'media-dialog-backdrop'} />
      <ErrorDialogPrimitive.Popup
        className={(state) => `media-dialog-popup ${resolveClassName(className, state)}`}
        {...props}
      >
        <div className={'media-dialog-content'}>
          <ErrorDialogPrimitive.Title className={'media-dialog-title'} />
          <ErrorDialogPrimitive.Description className={'media-dialog-description'} />
        </div>
        <div className={'media-dialog-actions'}>
          <ErrorDialogPrimitive.Close render={<Button />} className={'media-dialog-close'} />
        </div>
      </ErrorDialogPrimitive.Popup>
    </ErrorDialogPrimitive.Root>
  );
}

Minimal audio skin

'use client';

import { StatusAnnouncer as StatusAnnouncerPrimitive, Container as ContainerPrimitive, ErrorDialog, Hotkey, Tooltip, SeekButton as SeekButtonPrimitive, MuteButton as MuteButtonPrimitive, VolumeSlider as VolumeSliderPrimitive, VolumePopover as VolumePopoverPrimitive, PlayButton as PlayButtonPrimitive, BufferingIndicator as BufferingIndicatorPrimitive, PlaybackRateButton as PlaybackRateButtonPrimitive, Menu, Text as TextPrimitive, PlaybackRateRadioGroup, TimeSlider, Controls, Time } from '@videojs/react';
import { Audio } from '@videojs/react/audio';
import './player.css';
import { Player } from './player';
import { type ComponentProps, type SVGProps, type ReactElement, type ReactNode, type CSSProperties } from 'react';
import { SeekIcon as SeekIconPrimitive, VolumeOffIcon as VolumeOffIconPrimitive, VolumeLowIcon as VolumeLowIconPrimitive, VolumeHighIcon as VolumeHighIconPrimitive, RestartIcon as RestartIconPrimitive, PlayIcon as PlayIconPrimitive, PauseIcon as PauseIconPrimitive, SpinnerIcon as SpinnerIconPrimitive, CheckIcon as CheckIconPrimitive } from '@videojs/react/icons';
import { SeekIcon as SeekIconPrimitive2, VolumeOffIcon as VolumeOffIconPrimitive2, VolumeLowIcon as VolumeLowIconPrimitive2, VolumeHighIcon as VolumeHighIconPrimitive2, RestartIcon as RestartIconPrimitive2, PlayIcon as PlayIconPrimitive2, PauseIcon as PauseIconPrimitive2, SpinnerIcon as SpinnerIconPrimitive2, CheckIcon as CheckIconPrimitive2 } from '@videojs/react/icons/minimal';
import { type VolumeSliderProps as CoreVolumeSliderProps, type MenuProps, type SliderPreviewOverflow } from '@videojs/core';
import { speedText } from '@videojs/core/i18n/text/menu';
import { type Poster } from '@videojs/react';

type ContainerProps = ContainerPrimitive.Props;

function Container({ children, className, ...props }: ContainerProps) {
  return (
    <ContainerPrimitive className={`media-container ${className ?? ''}`} {...props}>
      {children}
    </ContainerPrimitive>
  );
}

interface MinimalAudioSkinProps extends Omit<NonNullable<ComponentProps<typeof Container>>, 'children'> {
  src: string;
}

type AudioErrorDialogProps = Omit<ErrorDialog.PopupProps, 'children'>;

type ButtonProps = ComponentProps<'button'>;

function AudioErrorDialog({ className, ...props }: AudioErrorDialogProps = {}) {
  return (
    <ErrorDialog.Root>
      <ErrorDialog.Backdrop className={'media-audio-dialog-backdrop'} />
      <ErrorDialog.Popup
        className={(state) => `media-audio-dialog-popup ${resolveClassName(className, state)}`}
        {...props}
      >
        <div className={'media-audio-dialog-content'}>
          <ErrorDialog.Title className={'media-audio-dialog-title'} />
          <ErrorDialog.Description className={'media-audio-dialog-description'} />
        </div>
        <div className={'media-audio-dialog-actions'}>
          <ErrorDialog.Close render={<Button />} className={'media-audio-dialog-close'} />
        </div>
      </ErrorDialog.Popup>
    </ErrorDialog.Root>
  );
}

type AudioPlayButtonProps = Omit<ComponentProps<'div'>, 'children'>;

type BufferingIndicatorProps = Omit<BufferingIndicatorPrimitive.Props, 'children'>;

function BufferingIndicator({ className, ...props }: BufferingIndicatorProps = {}) {
  return (
    <BufferingIndicatorPrimitive
      className={(state) => `media-buffering-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <SpinnerIconPrimitive className={'media-buffering-indicator-spinner-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <SpinnerIconPrimitive2 className={'media-buffering-indicator-spinner-icon'} />
        </span>
      </>
    </BufferingIndicatorPrimitive>
  );
}

type PlayButtonProps = Omit<PlayButtonPrimitive.Props, 'children'>;

interface ButtonTooltipProps extends Tooltip.RootProps {
  children: ReactElement;
  label?: ReactNode;
}

function ButtonTooltip({ children, label, ...props }: ButtonTooltipProps) {
  return (
    <Tooltip.Root {...props}>
      <Tooltip.Trigger render={children} />
      <Tooltip.Popup className={'media-tooltip'}>
        {label ?? <Tooltip.Label />}
        {!label && <Tooltip.Shortcut className={'media-tooltip-shortcut'} />}
      </Tooltip.Popup>
    </Tooltip.Root>
  );
}

function PlayButton({ className, ...props }: PlayButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <PlayButtonPrimitive
        render={<Button />}
        className={(state) => `media-play-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <RestartIconPrimitive className={'media-button-icon media-play-button-restart-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <RestartIconPrimitive2 className={'media-button-icon media-play-button-restart-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PlayIconPrimitive className={'media-button-icon media-play-button-play-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PlayIconPrimitive2 className={'media-button-icon media-play-button-play-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PauseIconPrimitive className={'media-button-icon media-play-button-pause-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PauseIconPrimitive2 className={'media-button-icon media-play-button-pause-icon'} />
          </span>
        </>
      </PlayButtonPrimitive>
    </ButtonTooltip>
  );
}

function AudioPlayButton({ className, ...props }: AudioPlayButtonProps = {}) {
  return (
    <div className={`media-audio-play-button ${className ?? ''}`} {...props}>
      <BufferingIndicator className={'media-audio-play-button-buffering-indicator'} />
      <PlayButton />
    </div>
  );
}

type SeekButtonProps = Omit<SeekButtonPrimitive.Props, 'children'>;

function SeekButton({ className, seconds = 10, ...props }: SeekButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <SeekButtonPrimitive
        render={<Button />}
        className={(state) => `media-seek-button ${resolveClassName(className, state)}`}
        seconds={seconds}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <SeekIconPrimitive className={`media-button-icon ${seconds < 0 && 'media-seek-button-backward-icon'}`} />
          </span>
          <span className="hidden theme-minimal:contents">
            <SeekIconPrimitive2 className={`media-button-icon ${seconds < 0 && 'media-seek-button-backward-icon'}`} />
          </span>
        </>
        <span
          className={`media-seek-button-label ${seconds < 0 ? 'media-seek-button-backward-label' : 'media-seek-button-forward-label'}`}
        >
          {Math.abs(seconds)}
        </span>
      </SeekButtonPrimitive>
    </ButtonTooltip>
  );
}

interface AudioTimeSliderProps extends Omit<TimeSlider.RootProps, 'children'> {
  previewOverflow?: SliderPreviewOverflow | undefined;
}

function AudioTimeSlider({ className, previewOverflow = 'clamp', ...props }: AudioTimeSliderProps = {}) {
  return (
    <TimeSlider.Root
      className={(state) => `media-slider media-audio-time-slider ${resolveClassName(className, state)}`}
      {...props}
    >
      <TimeSlider.Track render={<SliderTrack />}>
        <TimeSlider.Buffer render={<SliderBuffer />} />
        <TimeSlider.Fill render={<SliderFill />} />
      </TimeSlider.Track>
      <TimeSlider.Thumb render={<SliderThumb />} className={'media-audio-time-slider-thumb'} />
      <TimeSlider.Preview className={'media-slider-preview'} overflow={previewOverflow}>
        <div className={'media-audio-time-slider-preview-content'}>
          <TimeSlider.Value className={'media-audio-time-slider-value'} type="pointer" />
        </div>
      </TimeSlider.Preview>
    </TimeSlider.Root>
  );
}

interface VolumePopoverProps extends Omit<VolumePopoverPrimitive.RootProps, 'children'> {
  className?: string | undefined;
  orientation?: CoreVolumeSliderProps['orientation'];
  showTooltip?: boolean;
  popupClassName?: ClassValue;
}

type MuteButtonProps = Omit<MuteButtonPrimitive.Props, 'children'>;

function MuteButton({ className, ...props }: MuteButtonProps = {}) {
  return (
    <MuteButtonPrimitive
      render={<Button />}
      className={(state) => `media-mute-button ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeOffIconPrimitive className={'media-button-icon media-mute-button-off-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeOffIconPrimitive2 className={'media-button-icon media-mute-button-off-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeLowIconPrimitive className={'media-button-icon media-mute-button-low-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeLowIconPrimitive2 className={'media-button-icon media-mute-button-low-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeHighIconPrimitive className={'media-button-icon media-mute-button-high-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeHighIconPrimitive2 className={'media-button-icon media-mute-button-high-icon'} />
        </span>
      </>
    </MuteButtonPrimitive>
  );
}

type VolumeSliderProps = Omit<VolumeSliderPrimitive.RootProps, 'children'>;

function VolumeSlider({ className, ...props }: VolumeSliderProps = {}) {
  return (
    <VolumeSliderPrimitive.Root
      className={(state) => `media-slider media-volume-slider ${resolveClassName(className, state)}`}
      thumbAlignment="edge"
      {...props}
    >
      <VolumeSliderPrimitive.Track render={<SliderTrack />}>
        <VolumeSliderPrimitive.Fill render={<SliderFill />} />
      </VolumeSliderPrimitive.Track>
      <VolumeSliderPrimitive.Thumb render={<SliderThumb />} className={'media-volume-slider-thumb'} />
    </VolumeSliderPrimitive.Root>
  );
}

interface AudioSettingsMenuProps extends MenuProps {
  className?: ClassValue;
}

type PlaybackRateButtonProps = Omit<PlaybackRateButtonPrimitive.Props, 'children'>;

function PlaybackRateButton({ className, ...props }: PlaybackRateButtonProps = {}) {
  return (
    <PlaybackRateButtonPrimitive
      render={<Button />}
      className={(state) => `media-playback-rate-button ${resolveClassName(className, state)}`}
      {...props}
    />
  );
}

type RadioItemProps = Menu.RadioItemProps;

function RadioItem({ children, className, ...props }: RadioItemProps) {
  return (
    <Menu.RadioItem className={(state) => `media-menu-radio-item ${resolveClassName(className, state)}`} {...props}>
      {children}
      <Menu.ItemIndicator forceMount className={'media-menu-item-indicator'}>
        <>
          <span className="contents theme-minimal:hidden">
            <CheckIconPrimitive className={'media-menu-radio-item-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CheckIconPrimitive2 className={'media-menu-radio-item-icon'} />
          </span>
        </>
      </Menu.ItemIndicator>
    </Menu.RadioItem>
  );
}

function AudioSettingsMenu({ className, ...props }: AudioSettingsMenuProps = {}) {
  return (
    <Menu.Root side="top" align="center" boundary="viewport" {...props}>
      <PlaybackRateRadioGroup.Root>
        <ButtonTooltip label={<TextPrimitive token={speedText.key}>{speedText.text}</TextPrimitive>} side="top">
          <Menu.Trigger render={<PlaybackRateButton />} />
        </ButtonTooltip>
        <Menu.Popup
          className={(state) =>
            `media-menu-popup media-audio-settings-menu-popup ${resolveClassName(className, state)}`
          }
        >
          <Menu.Content className={'media-menu-content'}>
            <PlaybackRateRadioGroup.Options
              className={'media-menu-radio-group'}
              renderItem={(props, item) => (
                <RadioItem {...props}>
                  <span>{item.label}</span>
                </RadioItem>
              )}
            ></PlaybackRateRadioGroup.Options>
          </Menu.Content>
        </Menu.Popup>
      </PlaybackRateRadioGroup.Root>
    </Menu.Root>
  );
}

function MinimalAudioControls() {
  return (
    <Controls.Root visibility="always">
      <Controls.Content className={'media-controls media-controls-content'}>
        <Tooltip.Provider>
          <Controls.Group className={'media-controls-start'}>
            <AudioPlayButton />
            <SeekButton seconds={-10} />
            <SeekButton seconds={10} />
          </Controls.Group>

          <Controls.Group className={'media-time-slider-group'}>
            <Time.Group className={'media-time-group'}>
              <Time.Value className={'media-time-current-value'} type="current" toggle />
              <Time.Separator className={'media-time-separator'} />
              <Time.Value className={'media-time-duration-value'} type="duration" />
            </Time.Group>
            <AudioTimeSlider />
          </Controls.Group>

          <Controls.Group className={'media-controls-end'}>
            <VolumePopover
              popupClassName={'media-audio-volume-popover'}
              showTooltip
              side="left"
              orientation="horizontal"
            />
            <AudioSettingsMenu />
          </Controls.Group>
        </Tooltip.Provider>
      </Controls.Content>
    </Controls.Root>
  );
}

interface PlaybackHotkeysProps {
  disabled?: boolean | undefined;
}

function PlaybackHotkeys({ disabled = false }: PlaybackHotkeysProps = {}) {
  return (
    <>
      <Hotkey disabled={disabled} keys="Space" action="togglePaused" />
      <Hotkey disabled={disabled} keys="k" action="togglePaused" />
      <Hotkey disabled={disabled} keys="m" action="toggleMuted" />
      <Hotkey disabled={disabled} keys="ArrowRight" action="seekStep" value={5} />
      <Hotkey disabled={disabled} keys="ArrowLeft" action="seekStep" value={-5} />
      <Hotkey disabled={disabled} keys="l" action="seekStep" value={10} />
      <Hotkey disabled={disabled} keys="j" action="seekStep" value={-10} />
      <Hotkey disabled={disabled} keys="ArrowUp" action="volumeStep" value={0.05} />
      <Hotkey disabled={disabled} keys="ArrowDown" action="volumeStep" value={-0.05} />
      <Hotkey disabled={disabled} keys="0-9" action="seekToPercent" />
      <Hotkey disabled={disabled} keys="Home" action="seekToPercent" value={0} />
      <Hotkey disabled={disabled} keys="End" action="seekToPercent" value={100} />
      <Hotkey disabled={disabled} keys=">" action="speedUp" />
      <Hotkey disabled={disabled} keys="<" action="speedDown" />
    </>
  );
}

type StatusAnnouncerProps = Omit<StatusAnnouncerPrimitive.Props, 'children'>;

function StatusAnnouncer({ className, ...props }: StatusAnnouncerProps = {}) {
  return (
    <StatusAnnouncerPrimitive
      className={(state) => `media-status-announcer ${resolveClassName(className, state)}`}
      {...props}
    />
  );
}

function MinimalAudioSkin({ children, className, ...props }: MinimalAudioSkinProps = {}) {
  return (
    <Container
      className={`media-skin media-skin--minimal media-skin--audio media-audio-skin ${className ?? ''}`}
      {...props}
    >
      {children}
      <AudioErrorDialog />
      <MinimalAudioControls />
      <PlaybackHotkeys />
      <StatusAnnouncer />
    </Container>
  );
}

const Skin = MinimalAudioSkin;

// ================================================================
// Player
// ================================================================

export interface AudioPlayerProps {
  children?: ReactNode;
  style?: CSSProperties;
  className?: string;
}

export function MinimalAudioSkin(props: MinimalAudioSkinProps) {
  return <Skin {...props} />;
}

// ================================================================
// Components
// ================================================================

function Button({ className, ...props }: ButtonProps) {
  return <button className={`media-button ${className ?? ''}`} {...props} />;
}

type SliderTrackProps = ComponentProps<'div'>;

function SliderTrack({ className, ...props }: SliderTrackProps) {
  return <div className={`media-slider-track ${className ?? ''}`} {...props} />;
}

type SliderBufferProps = ComponentProps<'div'>;

function SliderBuffer({ className, ...props }: SliderBufferProps) {
  return <div className={`media-slider-buffer ${className ?? ''}`} {...props} />;
}

type SliderFillProps = ComponentProps<'div'>;

function SliderFill({ className, ...props }: SliderFillProps) {
  return <div className={`media-slider-fill ${className ?? ''}`} {...props} />;
}

type SliderThumbProps = ComponentProps<'div'>;

function SliderThumb({ className, ...props }: SliderThumbProps) {
  return <div className={`media-slider-thumb ${className ?? ''}`} {...props} />;
}

function VolumePopover({
  className,
  popupClassName,
  showTooltip = false,
  side = 'top',
  orientation = 'vertical',
  ...props
}: VolumePopoverProps = {}) {
  return (
    <VolumePopoverPrimitive.Root openOnHover delay={200} closeDelay={100} side={side} {...props}>
      <ButtonTooltip delay={0} disabled={!showTooltip} sticky side="top">
        <VolumePopoverPrimitive.Trigger render={<MuteButton className={className} />} />
      </ButtonTooltip>
      <VolumePopoverPrimitive.Popup className={`media-volume-popover ${popupClassName}`}>
        <VolumeSlider orientation={orientation} />
      </VolumePopoverPrimitive.Popup>
    </VolumePopoverPrimitive.Root>
  );
}

Default live video skin

The live skins swap the duration and current-time displays for a Live button. They build on the live presets, so the player reports live-edge state.

'use client';

import { type SVGProps, type ComponentProps, type ReactElement, type ReactNode, type CSSProperties } from 'react';
import { SpinnerIcon as SpinnerIconPrimitive, CaptionsOnIcon as CaptionsOnIconPrimitive, CaptionsOffIcon as CaptionsOffIconPrimitive, FullscreenEnterIcon as FullscreenEnterIconPrimitive, FullscreenExitIcon as FullscreenExitIconPrimitive, PipEnterIcon as PipEnterIconPrimitive, PipExitIcon as PipExitIconPrimitive, PlayIcon as PlayIconPrimitive, PauseIcon as PauseIconPrimitive, VolumeHighIcon as VolumeHighIconPrimitive, VolumeLowIcon as VolumeLowIconPrimitive, VolumeOffIcon as VolumeOffIconPrimitive, AirPlayEnterIcon as AirPlayEnterIconPrimitive, AirPlayExitIcon as AirPlayExitIconPrimitive, CastEnterIcon as CastEnterIconPrimitive, CastExitIcon as CastExitIconPrimitive, RestartIcon as RestartIconPrimitive, CheckIcon as CheckIconPrimitive } from '@videojs/react/icons';
import { SpinnerIcon as SpinnerIconPrimitive2, CaptionsOnIcon as CaptionsOnIconPrimitive2, CaptionsOffIcon as CaptionsOffIconPrimitive2, FullscreenEnterIcon as FullscreenEnterIconPrimitive2, FullscreenExitIcon as FullscreenExitIconPrimitive2, PipEnterIcon as PipEnterIconPrimitive2, PipExitIcon as PipExitIconPrimitive2, PlayIcon as PlayIconPrimitive2, PauseIcon as PauseIconPrimitive2, VolumeHighIcon as VolumeHighIconPrimitive2, VolumeLowIcon as VolumeLowIconPrimitive2, VolumeOffIcon as VolumeOffIconPrimitive2, AirPlayEnterIcon as AirPlayEnterIconPrimitive2, AirPlayExitIcon as AirPlayExitIconPrimitive2, CastEnterIcon as CastEnterIconPrimitive2, CastExitIcon as CastExitIconPrimitive2, RestartIcon as RestartIconPrimitive2, CheckIcon as CheckIconPrimitive2 } from '@videojs/react/icons/minimal';
import { BufferingIndicator as BufferingIndicatorPrimitive, ErrorDialog as ErrorDialogPrimitive, Container as ContainerPrimitive, Poster as PosterPrimitive, Gesture, Hotkey, StatusAnnouncer as StatusAnnouncerPrimitive, StatusIndicator as StatusIndicatorPrimitive, VolumeIndicator as VolumeIndicatorPrimitive, Tooltip, AirPlayButton as AirPlayButtonPrimitive, CastButton as CastButtonPrimitive, FullscreenButton as FullscreenButtonPrimitive, LiveButton as LiveButtonPrimitive, PiPButton as PiPButtonPrimitive, PlayButton as PlayButtonPrimitive, MuteButton as MuteButtonPrimitive, VolumeSlider as VolumeSliderPrimitive, VolumePopover as VolumePopoverPrimitive, Menu, CaptionsRadioGroup, Controls } from '@videojs/react';
import { HlsJsVideo } from '@videojs/react/media/hlsjs-video';
import './player.css';
import { Player } from './player';
import { type VolumeSliderProps as CoreVolumeSliderProps, type MenuProps } from '@videojs/core';
import { type Poster } from '@videojs/react';

type ContainerProps = ContainerPrimitive.Props;

function Container({ children, className, ...props }: ContainerProps) {
  return (
    <ContainerPrimitive className={`media-container ${className ?? ''}`} {...props}>
      {children}
    </ContainerPrimitive>
  );
}

interface PosterProps extends Omit<PosterPrimitive.Props, 'children' | 'render'> {
  children?: PosterPrimitive.Props['render'];
}

function Poster({ children, className, src, ...props }: PosterProps = {}) {
  return (
    <PosterPrimitive
      render={children}
      className={(state) => `media-poster ${resolveClassName(className, state)}`}
      src={src}
      {...props}
    />
  );
}

interface DefaultLiveVideoSkinProps extends Omit<NonNullable<ComponentProps<typeof Container>>, 'children'> {
  src: string;
  poster?: string | undefined;
  poster?: string | NonNullable<ComponentProps<typeof Poster>>['children'];
}

type BufferingIndicatorProps = Omit<BufferingIndicatorPrimitive.Props, 'children'>;

function BufferingIndicator({ className, ...props }: BufferingIndicatorProps = {}) {
  return (
    <BufferingIndicatorPrimitive
      className={(state) => `media-buffering-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <SpinnerIconPrimitive className={'media-buffering-indicator-spinner-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <SpinnerIconPrimitive2 className={'media-buffering-indicator-spinner-icon'} />
        </span>
      </>
    </BufferingIndicatorPrimitive>
  );
}

type ErrorDialogProps = Omit<ErrorDialogPrimitive.PopupProps, 'children'>;

type ButtonProps = ComponentProps<'button'>;

type PlayButtonProps = Omit<PlayButtonPrimitive.Props, 'children'>;

interface ButtonTooltipProps extends Tooltip.RootProps {
  children: ReactElement;
  label?: ReactNode;
}

function ButtonTooltip({ children, label, ...props }: ButtonTooltipProps) {
  return (
    <Tooltip.Root {...props}>
      <Tooltip.Trigger render={children} />
      <Tooltip.Popup className={'media-tooltip'}>
        {label ?? <Tooltip.Label />}
        {!label && <Tooltip.Shortcut className={'media-tooltip-shortcut'} />}
      </Tooltip.Popup>
    </Tooltip.Root>
  );
}

function PlayButton({ className, ...props }: PlayButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <PlayButtonPrimitive
        render={<Button />}
        className={(state) => `media-play-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <RestartIconPrimitive className={'media-button-icon media-play-button-restart-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <RestartIconPrimitive2 className={'media-button-icon media-play-button-restart-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PlayIconPrimitive className={'media-button-icon media-play-button-play-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PlayIconPrimitive2 className={'media-button-icon media-play-button-play-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PauseIconPrimitive className={'media-button-icon media-play-button-pause-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PauseIconPrimitive2 className={'media-button-icon media-play-button-pause-icon'} />
          </span>
        </>
      </PlayButtonPrimitive>
    </ButtonTooltip>
  );
}

type LiveButtonProps = Omit<LiveButtonPrimitive.Props, 'children'>;

function LiveButton({ className, ...props }: LiveButtonProps = {}) {
  return (
    <LiveButtonPrimitive
      render={<Button />}
      className={(state) => `media-live-button ${resolveClassName(className, state)}`}
      {...props}
    />
  );
}

interface VolumePopoverProps extends Omit<VolumePopoverPrimitive.RootProps, 'children'> {
  className?: string | undefined;
  orientation?: CoreVolumeSliderProps['orientation'];
  showTooltip?: boolean;
  popupClassName?: ClassValue;
}

type MuteButtonProps = Omit<MuteButtonPrimitive.Props, 'children'>;

function MuteButton({ className, ...props }: MuteButtonProps = {}) {
  return (
    <MuteButtonPrimitive
      render={<Button />}
      className={(state) => `media-mute-button ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeOffIconPrimitive className={'media-button-icon media-mute-button-off-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeOffIconPrimitive2 className={'media-button-icon media-mute-button-off-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeLowIconPrimitive className={'media-button-icon media-mute-button-low-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeLowIconPrimitive2 className={'media-button-icon media-mute-button-low-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeHighIconPrimitive className={'media-button-icon media-mute-button-high-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeHighIconPrimitive2 className={'media-button-icon media-mute-button-high-icon'} />
        </span>
      </>
    </MuteButtonPrimitive>
  );
}

type VolumeSliderProps = Omit<VolumeSliderPrimitive.RootProps, 'children'>;

function VolumeSlider({ className, ...props }: VolumeSliderProps = {}) {
  return (
    <VolumeSliderPrimitive.Root
      className={(state) => `media-slider media-volume-slider ${resolveClassName(className, state)}`}
      thumbAlignment="edge"
      {...props}
    >
      <VolumeSliderPrimitive.Track render={<SliderTrack />}>
        <VolumeSliderPrimitive.Fill render={<SliderFill />} />
      </VolumeSliderPrimitive.Track>
      <VolumeSliderPrimitive.Thumb render={<SliderThumb />} className={'media-volume-slider-thumb'} />
    </VolumeSliderPrimitive.Root>
  );
}

interface CaptionsMenuProps extends MenuProps {
  className?: ClassValue;
  triggerClassName?: ClassValue;
}

type RadioItemProps = Menu.RadioItemProps;

function RadioItem({ children, className, ...props }: RadioItemProps) {
  return (
    <Menu.RadioItem className={(state) => `media-menu-radio-item ${resolveClassName(className, state)}`} {...props}>
      {children}
      <Menu.ItemIndicator forceMount className={'media-menu-item-indicator'}>
        <>
          <span className="contents theme-minimal:hidden">
            <CheckIconPrimitive className={'media-menu-radio-item-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CheckIconPrimitive2 className={'media-menu-radio-item-icon'} />
          </span>
        </>
      </Menu.ItemIndicator>
    </Menu.RadioItem>
  );
}

function CaptionsMenu({ className, triggerClassName, ...props }: CaptionsMenuProps = {}) {
  return (
    <Menu.Root side="top" align="center" boundary="viewport" {...props}>
      <CaptionsRadioGroup.Root>
        <ButtonTooltip side="top">
          <Menu.Trigger
            render={<Button />}
            aria-label="Enable captions"
            className={`media-captions-button ${triggerClassName}`}
          >
            <>
              <span className="contents theme-minimal:hidden">
                <CaptionsOffIconPrimitive className={'media-button-icon media-captions-button-off-icon'} />
              </span>
              <span className="hidden theme-minimal:contents">
                <CaptionsOffIconPrimitive2 className={'media-button-icon media-captions-button-off-icon'} />
              </span>
            </>
            <>
              <span className="contents theme-minimal:hidden">
                <CaptionsOnIconPrimitive className={'media-button-icon media-captions-button-on-icon'} />
              </span>
              <span className="hidden theme-minimal:contents">
                <CaptionsOnIconPrimitive2 className={'media-button-icon media-captions-button-on-icon'} />
              </span>
            </>
          </Menu.Trigger>
        </ButtonTooltip>
        <Menu.Popup className={(state) => `media-menu-popup ${resolveClassName(className, state)}`}>
          <Menu.Content className={'media-menu-content'}>
            <CaptionsRadioGroup.Options
              className={'media-menu-radio-group'}
              renderItem={(props, item) => (
                <RadioItem {...props}>
                  <span>{item.label}</span>
                </RadioItem>
              )}
            ></CaptionsRadioGroup.Options>
          </Menu.Content>
        </Menu.Popup>
      </CaptionsRadioGroup.Root>
    </Menu.Root>
  );
}

type CastButtonProps = Omit<CastButtonPrimitive.Props, 'children'>;

function CastButton({ className, ...props }: CastButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <CastButtonPrimitive
        render={<Button />}
        className={(state) => `media-cast-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <CastEnterIconPrimitive className={'media-button-icon media-cast-button-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CastEnterIconPrimitive2 className={'media-button-icon media-cast-button-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <CastExitIconPrimitive className={'media-button-icon media-cast-button-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CastExitIconPrimitive2 className={'media-button-icon media-cast-button-exit-icon'} />
          </span>
        </>
      </CastButtonPrimitive>
    </ButtonTooltip>
  );
}

type AirPlayButtonProps = Omit<AirPlayButtonPrimitive.Props, 'children'>;

function AirPlayButton({ className, ...props }: AirPlayButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <AirPlayButtonPrimitive
        render={<Button />}
        className={(state) => `media-airplay-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <AirPlayEnterIconPrimitive className={'media-button-icon media-airplay-button-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <AirPlayEnterIconPrimitive2 className={'media-button-icon media-airplay-button-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <AirPlayExitIconPrimitive className={'media-button-icon media-airplay-button-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <AirPlayExitIconPrimitive2 className={'media-button-icon media-airplay-button-exit-icon'} />
          </span>
        </>
      </AirPlayButtonPrimitive>
    </ButtonTooltip>
  );
}

type PiPButtonProps = Omit<PiPButtonPrimitive.Props, 'children'>;

function PiPButton({ className, ...props }: PiPButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <PiPButtonPrimitive
        render={<Button />}
        className={(state) => `media-pip-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <PipEnterIconPrimitive className={'media-button-icon media-pip-button-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PipEnterIconPrimitive2 className={'media-button-icon media-pip-button-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PipExitIconPrimitive className={'media-button-icon media-pip-button-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PipExitIconPrimitive2 className={'media-button-icon media-pip-button-exit-icon'} />
          </span>
        </>
      </PiPButtonPrimitive>
    </ButtonTooltip>
  );
}

type FullscreenButtonProps = Omit<FullscreenButtonPrimitive.Props, 'children'>;

function FullscreenButton({ className, ...props }: FullscreenButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <FullscreenButtonPrimitive
        render={<Button />}
        className={(state) => `media-fullscreen-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <FullscreenEnterIconPrimitive className={'media-button-icon media-fullscreen-button-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <FullscreenEnterIconPrimitive2 className={'media-button-icon media-fullscreen-button-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <FullscreenExitIconPrimitive className={'media-button-icon media-fullscreen-button-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <FullscreenExitIconPrimitive2 className={'media-button-icon media-fullscreen-button-exit-icon'} />
          </span>
        </>
      </FullscreenButtonPrimitive>
    </ButtonTooltip>
  );
}

function DefaultLiveVideoControls() {
  return (
    <Controls.Root>
      <Controls.Backdrop className={'media-controls__backdrop'} />
      <Controls.Content className={'media-controls media-controls'}>
        <Tooltip.Provider>
          <Controls.Group className={'media-controls-primary'}>
            <PlayButton />
            <LiveButton />
            <div aria-hidden="true" className={'media-controls-spacer'} />
            <VolumePopover />
            <CaptionsMenu triggerClassName={'media-controls-captions-menu'} />
            <CastButton />
            <AirPlayButton />
            <PiPButton />
            <FullscreenButton />
          </Controls.Group>
        </Tooltip.Provider>
      </Controls.Content>
    </Controls.Root>
  );
}

interface LiveVideoHotkeysProps {
  disabled?: boolean | undefined;
}

interface LivePlaybackHotkeysProps {
  disabled?: boolean | undefined;
}

function LivePlaybackHotkeys({ disabled = false }: LivePlaybackHotkeysProps = {}) {
  return (
    <>
      <Hotkey disabled={disabled} keys="Space" action="togglePaused" />
      <Hotkey disabled={disabled} keys="k" action="togglePaused" />
      <Hotkey disabled={disabled} keys="m" action="toggleMuted" />
      <Hotkey disabled={disabled} keys="ArrowUp" action="volumeStep" value={0.05} />
      <Hotkey disabled={disabled} keys="ArrowDown" action="volumeStep" value={-0.05} />
    </>
  );
}

function LiveVideoHotkeys({ disabled = false }: LiveVideoHotkeysProps = {}) {
  return (
    <>
      <LivePlaybackHotkeys disabled={disabled} />
      <Hotkey disabled={disabled} keys="f" action="toggleFullscreen" />
      <Hotkey disabled={disabled} keys="c" action="toggleSubtitles" />
      <Hotkey disabled={disabled} keys="i" action="togglePictureInPicture" />
    </>
  );
}

interface LiveVideoGesturesProps {
  disabled?: boolean | undefined;
}

function LiveVideoGestures({ disabled = false }: LiveVideoGesturesProps = {}) {
  return (
    <>
      <Gesture disabled={disabled} type="tap" action="togglePaused" pointer="mouse" region="center" />
      <Gesture disabled={disabled} type="tap" action="toggleControls" pointer="touch" />
      <Gesture disabled={disabled} type="doubletap" action="toggleFullscreen" region="center" />
    </>
  );
}

type LiveVideoStatusIndicatorsProps = Omit<ComponentProps<'div'>, 'children'>;

type StatusAnnouncerProps = Omit<StatusAnnouncerPrimitive.Props, 'children'>;

function StatusAnnouncer({ className, ...props }: StatusAnnouncerProps = {}) {
  return (
    <StatusAnnouncerPrimitive
      className={(state) => `media-status-announcer ${resolveClassName(className, state)}`}
      {...props}
    />
  );
}

type VolumeIndicatorProps = Omit<VolumeIndicatorPrimitive.RootProps, 'children'>;

function VolumeIndicator({ className, ...props }: VolumeIndicatorProps = {}) {
  return (
    <VolumeIndicatorPrimitive.Root
      className={(state) => `media-volume-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <VolumeIndicatorPrimitive.Fill className={'media-volume-indicator-fill'}>
        <>
          <span className="contents theme-minimal:hidden">
            <VolumeHighIconPrimitive className={'media-volume-indicator-high-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <VolumeHighIconPrimitive2 className={'media-volume-indicator-high-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <VolumeLowIconPrimitive className={'media-volume-indicator-low-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <VolumeLowIconPrimitive2 className={'media-volume-indicator-low-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <VolumeOffIconPrimitive className={'media-volume-indicator-off-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <VolumeOffIconPrimitive2 className={'media-volume-indicator-off-icon'} />
          </span>
        </>
        <VolumeIndicatorPrimitive.Value className={'media-volume-indicator-value'} />
      </VolumeIndicatorPrimitive.Fill>
    </VolumeIndicatorPrimitive.Root>
  );
}

type StatusIndicatorProps = Omit<StatusIndicatorPrimitive.RootProps, 'children'>;

const TOP_STATUS_ACTIONS = ['toggleSubtitles', 'toggleFullscreen', 'togglePictureInPicture'] as const;

function StatusIndicator({ className, ...props }: StatusIndicatorProps = {}) {
  return (
    <StatusIndicatorPrimitive.Root
      actions={TOP_STATUS_ACTIONS}
      className={(state) => `media-status-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <div className={'media-status-indicator-content'}>
        <>
          <span className="contents theme-minimal:hidden">
            <CaptionsOnIconPrimitive className={'media-status-indicator-captions-on-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CaptionsOnIconPrimitive2 className={'media-status-indicator-captions-on-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <CaptionsOffIconPrimitive className={'media-status-indicator-captions-off-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CaptionsOffIconPrimitive2 className={'media-status-indicator-captions-off-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <FullscreenEnterIconPrimitive className={'media-status-indicator-fullscreen-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <FullscreenEnterIconPrimitive2 className={'media-status-indicator-fullscreen-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <FullscreenExitIconPrimitive className={'media-status-indicator-fullscreen-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <FullscreenExitIconPrimitive2 className={'media-status-indicator-fullscreen-exit-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PipEnterIconPrimitive className={'media-status-indicator-pip-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PipEnterIconPrimitive2 className={'media-status-indicator-pip-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PipExitIconPrimitive className={'media-status-indicator-pip-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PipExitIconPrimitive2 className={'media-status-indicator-pip-exit-icon'} />
          </span>
        </>
        <StatusIndicatorPrimitive.Value className={'media-status-indicator-value'} />
      </div>
    </StatusIndicatorPrimitive.Root>
  );
}

type PlaybackStatusIndicatorProps = Omit<StatusIndicatorPrimitive.RootProps, 'children'>;

const PLAYBACK_STATUS_ACTIONS = ['togglePaused'] as const;

function PlaybackStatusIndicator({ className, ...props }: PlaybackStatusIndicatorProps = {}) {
  return (
    <StatusIndicatorPrimitive.Root
      actions={PLAYBACK_STATUS_ACTIONS}
      className={(state) => `media-playback-status-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <PlayIconPrimitive className={'media-playback-status-indicator-play-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <PlayIconPrimitive2 className={'media-playback-status-indicator-play-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <PauseIconPrimitive className={'media-playback-status-indicator-pause-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <PauseIconPrimitive2 className={'media-playback-status-indicator-pause-icon'} />
        </span>
      </>
    </StatusIndicatorPrimitive.Root>
  );
}

function LiveVideoStatusIndicators({ className, ...props }: LiveVideoStatusIndicatorsProps = {}) {
  return (
    <>
      <StatusAnnouncer />
      <div className={`media-video-status-indicators ${className ?? ''}`} {...props}>
        <VolumeIndicator />
        <StatusIndicator />
        <PlaybackStatusIndicator />
      </div>
    </>
  );
}

function DefaultLiveVideoSkin({ children, className, poster, ...props }: DefaultLiveVideoSkinProps = {}) {
  const isPosterString = typeof poster === 'string';

  return (
    <Container className={`media-skin media-skin--live-video ${className ?? ''}`} {...props}>
      {children}
      <Poster src={isPosterString ? poster : undefined}>{isPosterString ? undefined : poster}</Poster>
      <BufferingIndicator />
      <ErrorDialog />
      <DefaultLiveVideoControls />
      <LiveVideoHotkeys />
      <LiveVideoGestures />
      <LiveVideoStatusIndicators />
    </Container>
  );
}

const Skin = DefaultLiveVideoSkin;

// ================================================================
// Player
// ================================================================

export interface LiveVideoPlayerProps {
  children?: ReactNode;
  style?: CSSProperties;
  className?: string;
  renderPoster?: Poster.Props['render'];
}

export function LiveVideoSkin({ renderPoster, ...props }: LiveVideoSkinProps) {
  return <Skin poster={renderPoster} {...props} />;
}

// ================================================================
// Components
// ================================================================

function Button({ className, ...props }: ButtonProps) {
  return <button className={`media-button ${className ?? ''}`} {...props} />;
}

type SliderTrackProps = ComponentProps<'div'>;

function SliderTrack({ className, ...props }: SliderTrackProps) {
  return <div className={`media-slider-track ${className ?? ''}`} {...props} />;
}

type SliderFillProps = ComponentProps<'div'>;

function SliderFill({ className, ...props }: SliderFillProps) {
  return <div className={`media-slider-fill ${className ?? ''}`} {...props} />;
}

type SliderThumbProps = ComponentProps<'div'>;

function SliderThumb({ className, ...props }: SliderThumbProps) {
  return <div className={`media-slider-thumb ${className ?? ''}`} {...props} />;
}

function VolumePopover({
  className,
  popupClassName,
  showTooltip = false,
  side = 'top',
  orientation = 'vertical',
  ...props
}: VolumePopoverProps = {}) {
  return (
    <VolumePopoverPrimitive.Root openOnHover delay={200} closeDelay={100} side={side} {...props}>
      <ButtonTooltip delay={0} disabled={!showTooltip} sticky side="top">
        <VolumePopoverPrimitive.Trigger render={<MuteButton className={className} />} />
      </ButtonTooltip>
      <VolumePopoverPrimitive.Popup className={`media-volume-popover ${popupClassName}`}>
        <VolumeSlider orientation={orientation} />
      </VolumePopoverPrimitive.Popup>
    </VolumePopoverPrimitive.Root>
  );
}

// ================================================================
// Error Dialog
// ================================================================

function ErrorDialog({ className, ...props }: ErrorDialogProps = {}) {
  return (
    <ErrorDialogPrimitive.Root>
      <ErrorDialogPrimitive.Backdrop className={'media-dialog-backdrop'} />
      <ErrorDialogPrimitive.Popup
        className={(state) => `media-dialog-popup ${resolveClassName(className, state)}`}
        {...props}
      >
        <div className={'media-dialog-content'}>
          <ErrorDialogPrimitive.Title className={'media-dialog-title'} />
          <ErrorDialogPrimitive.Description className={'media-dialog-description'} />
        </div>
        <div className={'media-dialog-actions'}>
          <ErrorDialogPrimitive.Close render={<Button />} className={'media-dialog-close'} />
        </div>
      </ErrorDialogPrimitive.Popup>
    </ErrorDialogPrimitive.Root>
  );
}

Default live audio skin

'use client';

import { StatusAnnouncer as StatusAnnouncerPrimitive, Container as ContainerPrimitive, ErrorDialog, Hotkey, LiveButton as LiveButtonPrimitive, Tooltip, MuteButton as MuteButtonPrimitive, VolumeSlider as VolumeSliderPrimitive, VolumePopover as VolumePopoverPrimitive, PlayButton as PlayButtonPrimitive, BufferingIndicator as BufferingIndicatorPrimitive, Controls } from '@videojs/react';
import { MuxAudio } from '@videojs/react/media/mux-audio';
import './player.css';
import { Player } from './player';
import { type ComponentProps, type ReactElement, type ReactNode, type SVGProps, type CSSProperties } from 'react';
import { VolumeOffIcon as VolumeOffIconPrimitive, VolumeLowIcon as VolumeLowIconPrimitive, VolumeHighIcon as VolumeHighIconPrimitive, RestartIcon as RestartIconPrimitive, PlayIcon as PlayIconPrimitive, PauseIcon as PauseIconPrimitive, SpinnerIcon as SpinnerIconPrimitive } from '@videojs/react/icons';
import { VolumeOffIcon as VolumeOffIconPrimitive2, VolumeLowIcon as VolumeLowIconPrimitive2, VolumeHighIcon as VolumeHighIconPrimitive2, RestartIcon as RestartIconPrimitive2, PlayIcon as PlayIconPrimitive2, PauseIcon as PauseIconPrimitive2, SpinnerIcon as SpinnerIconPrimitive2 } from '@videojs/react/icons/minimal';
import { type VolumeSliderProps as CoreVolumeSliderProps } from '@videojs/core';
import { type Poster } from '@videojs/react';

type ContainerProps = ContainerPrimitive.Props;

function Container({ children, className, ...props }: ContainerProps) {
  return (
    <ContainerPrimitive className={`media-container ${className ?? ''}`} {...props}>
      {children}
    </ContainerPrimitive>
  );
}

interface DefaultLiveAudioSkinProps extends Omit<NonNullable<ComponentProps<typeof Container>>, 'children'> {
  src: string;
}

type AudioErrorDialogProps = Omit<ErrorDialog.PopupProps, 'children'>;

type ButtonProps = ComponentProps<'button'>;

function AudioErrorDialog({ className, ...props }: AudioErrorDialogProps = {}) {
  return (
    <ErrorDialog.Root>
      <ErrorDialog.Backdrop className={'media-audio-dialog-backdrop'} />
      <ErrorDialog.Popup
        className={(state) => `media-audio-dialog-popup ${resolveClassName(className, state)}`}
        {...props}
      >
        <div className={'media-audio-dialog-content'}>
          <ErrorDialog.Title className={'media-audio-dialog-title'} />
          <ErrorDialog.Description className={'media-audio-dialog-description'} />
        </div>
        <div className={'media-audio-dialog-actions'}>
          <ErrorDialog.Close render={<Button />} className={'media-audio-dialog-close'} />
        </div>
      </ErrorDialog.Popup>
    </ErrorDialog.Root>
  );
}

type AudioPlayButtonProps = Omit<ComponentProps<'div'>, 'children'>;

type BufferingIndicatorProps = Omit<BufferingIndicatorPrimitive.Props, 'children'>;

function BufferingIndicator({ className, ...props }: BufferingIndicatorProps = {}) {
  return (
    <BufferingIndicatorPrimitive
      className={(state) => `media-buffering-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <SpinnerIconPrimitive className={'media-buffering-indicator-spinner-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <SpinnerIconPrimitive2 className={'media-buffering-indicator-spinner-icon'} />
        </span>
      </>
    </BufferingIndicatorPrimitive>
  );
}

type PlayButtonProps = Omit<PlayButtonPrimitive.Props, 'children'>;

interface ButtonTooltipProps extends Tooltip.RootProps {
  children: ReactElement;
  label?: ReactNode;
}

function ButtonTooltip({ children, label, ...props }: ButtonTooltipProps) {
  return (
    <Tooltip.Root {...props}>
      <Tooltip.Trigger render={children} />
      <Tooltip.Popup className={'media-tooltip'}>
        {label ?? <Tooltip.Label />}
        {!label && <Tooltip.Shortcut className={'media-tooltip-shortcut'} />}
      </Tooltip.Popup>
    </Tooltip.Root>
  );
}

function PlayButton({ className, ...props }: PlayButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <PlayButtonPrimitive
        render={<Button />}
        className={(state) => `media-play-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <RestartIconPrimitive className={'media-button-icon media-play-button-restart-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <RestartIconPrimitive2 className={'media-button-icon media-play-button-restart-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PlayIconPrimitive className={'media-button-icon media-play-button-play-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PlayIconPrimitive2 className={'media-button-icon media-play-button-play-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PauseIconPrimitive className={'media-button-icon media-play-button-pause-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PauseIconPrimitive2 className={'media-button-icon media-play-button-pause-icon'} />
          </span>
        </>
      </PlayButtonPrimitive>
    </ButtonTooltip>
  );
}

function AudioPlayButton({ className, ...props }: AudioPlayButtonProps = {}) {
  return (
    <div className={`media-audio-play-button ${className ?? ''}`} {...props}>
      <BufferingIndicator className={'media-audio-play-button-buffering-indicator'} />
      <PlayButton />
    </div>
  );
}

type LiveButtonProps = Omit<LiveButtonPrimitive.Props, 'children'>;

function LiveButton({ className, ...props }: LiveButtonProps = {}) {
  return (
    <LiveButtonPrimitive
      render={<Button />}
      className={(state) => `media-live-button ${resolveClassName(className, state)}`}
      {...props}
    />
  );
}

interface VolumePopoverProps extends Omit<VolumePopoverPrimitive.RootProps, 'children'> {
  className?: string | undefined;
  orientation?: CoreVolumeSliderProps['orientation'];
  showTooltip?: boolean;
  popupClassName?: ClassValue;
}

type MuteButtonProps = Omit<MuteButtonPrimitive.Props, 'children'>;

function MuteButton({ className, ...props }: MuteButtonProps = {}) {
  return (
    <MuteButtonPrimitive
      render={<Button />}
      className={(state) => `media-mute-button ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeOffIconPrimitive className={'media-button-icon media-mute-button-off-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeOffIconPrimitive2 className={'media-button-icon media-mute-button-off-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeLowIconPrimitive className={'media-button-icon media-mute-button-low-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeLowIconPrimitive2 className={'media-button-icon media-mute-button-low-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeHighIconPrimitive className={'media-button-icon media-mute-button-high-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeHighIconPrimitive2 className={'media-button-icon media-mute-button-high-icon'} />
        </span>
      </>
    </MuteButtonPrimitive>
  );
}

type VolumeSliderProps = Omit<VolumeSliderPrimitive.RootProps, 'children'>;

function VolumeSlider({ className, ...props }: VolumeSliderProps = {}) {
  return (
    <VolumeSliderPrimitive.Root
      className={(state) => `media-slider media-volume-slider ${resolveClassName(className, state)}`}
      thumbAlignment="edge"
      {...props}
    >
      <VolumeSliderPrimitive.Track render={<SliderTrack />}>
        <VolumeSliderPrimitive.Fill render={<SliderFill />} />
      </VolumeSliderPrimitive.Track>
      <VolumeSliderPrimitive.Thumb render={<SliderThumb />} className={'media-volume-slider-thumb'} />
    </VolumeSliderPrimitive.Root>
  );
}

function DefaultLiveAudioControls() {
  return (
    <Controls.Root visibility="always">
      <Controls.Content className={'media-controls media-controls-content'}>
        <Tooltip.Provider>
          <Controls.Group className={'media-controls-start'}>
            <AudioPlayButton />
            <LiveButton />
          </Controls.Group>

          <div aria-hidden="true" className={'media-controls-spacer'} />

          <Controls.Group className={'media-controls-end'}>
            <VolumePopover />
          </Controls.Group>
        </Tooltip.Provider>
      </Controls.Content>
    </Controls.Root>
  );
}

interface LivePlaybackHotkeysProps {
  disabled?: boolean | undefined;
}

function LivePlaybackHotkeys({ disabled = false }: LivePlaybackHotkeysProps = {}) {
  return (
    <>
      <Hotkey disabled={disabled} keys="Space" action="togglePaused" />
      <Hotkey disabled={disabled} keys="k" action="togglePaused" />
      <Hotkey disabled={disabled} keys="m" action="toggleMuted" />
      <Hotkey disabled={disabled} keys="ArrowUp" action="volumeStep" value={0.05} />
      <Hotkey disabled={disabled} keys="ArrowDown" action="volumeStep" value={-0.05} />
    </>
  );
}

type StatusAnnouncerProps = Omit<StatusAnnouncerPrimitive.Props, 'children'>;

function StatusAnnouncer({ className, ...props }: StatusAnnouncerProps = {}) {
  return (
    <StatusAnnouncerPrimitive
      className={(state) => `media-status-announcer ${resolveClassName(className, state)}`}
      {...props}
    />
  );
}

function DefaultLiveAudioSkin({ children, className, ...props }: DefaultLiveAudioSkinProps = {}) {
  return (
    <Container className={`media-skin media-skin--live-audio media-audio-skin ${className ?? ''}`} {...props}>
      {children}
      <AudioErrorDialog />
      <DefaultLiveAudioControls />
      <LivePlaybackHotkeys />
      <StatusAnnouncer />
    </Container>
  );
}

const Skin = DefaultLiveAudioSkin;

// ================================================================
// Player
// ================================================================

export interface LiveAudioPlayerProps {
  children?: ReactNode;
  style?: CSSProperties;
  className?: string;
}

export function LiveAudioSkin(props: LiveAudioSkinProps) {
  return <Skin {...props} />;
}

// ================================================================
// Components
// ================================================================

function Button({ className, ...props }: ButtonProps) {
  return <button className={`media-button ${className ?? ''}`} {...props} />;
}

type SliderTrackProps = ComponentProps<'div'>;

function SliderTrack({ className, ...props }: SliderTrackProps) {
  return <div className={`media-slider-track ${className ?? ''}`} {...props} />;
}

type SliderFillProps = ComponentProps<'div'>;

function SliderFill({ className, ...props }: SliderFillProps) {
  return <div className={`media-slider-fill ${className ?? ''}`} {...props} />;
}

type SliderThumbProps = ComponentProps<'div'>;

function SliderThumb({ className, ...props }: SliderThumbProps) {
  return <div className={`media-slider-thumb ${className ?? ''}`} {...props} />;
}

function VolumePopover({
  className,
  popupClassName,
  showTooltip = false,
  side = 'top',
  orientation = 'vertical',
  ...props
}: VolumePopoverProps = {}) {
  return (
    <VolumePopoverPrimitive.Root openOnHover delay={200} closeDelay={100} side={side} {...props}>
      <ButtonTooltip delay={0} disabled={!showTooltip} sticky side="top">
        <VolumePopoverPrimitive.Trigger render={<MuteButton className={className} />} />
      </ButtonTooltip>
      <VolumePopoverPrimitive.Popup className={`media-volume-popover ${popupClassName}`}>
        <VolumeSlider orientation={orientation} />
      </VolumePopoverPrimitive.Popup>
    </VolumePopoverPrimitive.Root>
  );
}

Minimal live video skin

'use client';

import { type SVGProps, type ComponentProps, type ReactElement, type ReactNode, type CSSProperties } from 'react';
import { SpinnerIcon as SpinnerIconPrimitive, CaptionsOnIcon as CaptionsOnIconPrimitive, CaptionsOffIcon as CaptionsOffIconPrimitive, FullscreenEnterIcon as FullscreenEnterIconPrimitive, FullscreenExitIcon as FullscreenExitIconPrimitive, PipEnterIcon as PipEnterIconPrimitive, PipExitIcon as PipExitIconPrimitive, PlayIcon as PlayIconPrimitive, PauseIcon as PauseIconPrimitive, VolumeHighIcon as VolumeHighIconPrimitive, VolumeLowIcon as VolumeLowIconPrimitive, VolumeOffIcon as VolumeOffIconPrimitive, AirPlayEnterIcon as AirPlayEnterIconPrimitive, AirPlayExitIcon as AirPlayExitIconPrimitive, CastEnterIcon as CastEnterIconPrimitive, CastExitIcon as CastExitIconPrimitive, RestartIcon as RestartIconPrimitive, CheckIcon as CheckIconPrimitive } from '@videojs/react/icons';
import { SpinnerIcon as SpinnerIconPrimitive2, CaptionsOnIcon as CaptionsOnIconPrimitive2, CaptionsOffIcon as CaptionsOffIconPrimitive2, FullscreenEnterIcon as FullscreenEnterIconPrimitive2, FullscreenExitIcon as FullscreenExitIconPrimitive2, PipEnterIcon as PipEnterIconPrimitive2, PipExitIcon as PipExitIconPrimitive2, PlayIcon as PlayIconPrimitive2, PauseIcon as PauseIconPrimitive2, VolumeHighIcon as VolumeHighIconPrimitive2, VolumeLowIcon as VolumeLowIconPrimitive2, VolumeOffIcon as VolumeOffIconPrimitive2, AirPlayEnterIcon as AirPlayEnterIconPrimitive2, AirPlayExitIcon as AirPlayExitIconPrimitive2, CastEnterIcon as CastEnterIconPrimitive2, CastExitIcon as CastExitIconPrimitive2, RestartIcon as RestartIconPrimitive2, CheckIcon as CheckIconPrimitive2 } from '@videojs/react/icons/minimal';
import { BufferingIndicator as BufferingIndicatorPrimitive, ErrorDialog as ErrorDialogPrimitive, Container as ContainerPrimitive, Poster as PosterPrimitive, Gesture, Hotkey, StatusAnnouncer as StatusAnnouncerPrimitive, StatusIndicator as StatusIndicatorPrimitive, VolumeIndicator as VolumeIndicatorPrimitive, Tooltip, AirPlayButton as AirPlayButtonPrimitive, CastButton as CastButtonPrimitive, FullscreenButton as FullscreenButtonPrimitive, LiveButton as LiveButtonPrimitive, PiPButton as PiPButtonPrimitive, PlayButton as PlayButtonPrimitive, MuteButton as MuteButtonPrimitive, VolumeSlider as VolumeSliderPrimitive, VolumePopover as VolumePopoverPrimitive, Menu, CaptionsRadioGroup, Controls } from '@videojs/react';
import { HlsJsVideo } from '@videojs/react/media/hlsjs-video';
import './player.css';
import { Player } from './player';
import { type VolumeSliderProps as CoreVolumeSliderProps, type MenuProps } from '@videojs/core';
import { type Poster } from '@videojs/react';

type ContainerProps = ContainerPrimitive.Props;

function Container({ children, className, ...props }: ContainerProps) {
  return (
    <ContainerPrimitive className={`media-container ${className ?? ''}`} {...props}>
      {children}
    </ContainerPrimitive>
  );
}

interface PosterProps extends Omit<PosterPrimitive.Props, 'children' | 'render'> {
  children?: PosterPrimitive.Props['render'];
}

function Poster({ children, className, src, ...props }: PosterProps = {}) {
  return (
    <PosterPrimitive
      render={children}
      className={(state) => `media-poster ${resolveClassName(className, state)}`}
      src={src}
      {...props}
    />
  );
}

interface MinimalLiveVideoSkinProps extends Omit<NonNullable<ComponentProps<typeof Container>>, 'children'> {
  src: string;
  poster?: string | undefined;
  poster?: string | NonNullable<ComponentProps<typeof Poster>>['children'];
}

type BufferingIndicatorProps = Omit<BufferingIndicatorPrimitive.Props, 'children'>;

function BufferingIndicator({ className, ...props }: BufferingIndicatorProps = {}) {
  return (
    <BufferingIndicatorPrimitive
      className={(state) => `media-buffering-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <SpinnerIconPrimitive className={'media-buffering-indicator-spinner-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <SpinnerIconPrimitive2 className={'media-buffering-indicator-spinner-icon'} />
        </span>
      </>
    </BufferingIndicatorPrimitive>
  );
}

type ErrorDialogProps = Omit<ErrorDialogPrimitive.PopupProps, 'children'>;

type ButtonProps = ComponentProps<'button'>;

type PlayButtonProps = Omit<PlayButtonPrimitive.Props, 'children'>;

interface ButtonTooltipProps extends Tooltip.RootProps {
  children: ReactElement;
  label?: ReactNode;
}

function ButtonTooltip({ children, label, ...props }: ButtonTooltipProps) {
  return (
    <Tooltip.Root {...props}>
      <Tooltip.Trigger render={children} />
      <Tooltip.Popup className={'media-tooltip'}>
        {label ?? <Tooltip.Label />}
        {!label && <Tooltip.Shortcut className={'media-tooltip-shortcut'} />}
      </Tooltip.Popup>
    </Tooltip.Root>
  );
}

function PlayButton({ className, ...props }: PlayButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <PlayButtonPrimitive
        render={<Button />}
        className={(state) => `media-play-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <RestartIconPrimitive className={'media-button-icon media-play-button-restart-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <RestartIconPrimitive2 className={'media-button-icon media-play-button-restart-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PlayIconPrimitive className={'media-button-icon media-play-button-play-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PlayIconPrimitive2 className={'media-button-icon media-play-button-play-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PauseIconPrimitive className={'media-button-icon media-play-button-pause-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PauseIconPrimitive2 className={'media-button-icon media-play-button-pause-icon'} />
          </span>
        </>
      </PlayButtonPrimitive>
    </ButtonTooltip>
  );
}

type LiveButtonProps = Omit<LiveButtonPrimitive.Props, 'children'>;

function LiveButton({ className, ...props }: LiveButtonProps = {}) {
  return (
    <LiveButtonPrimitive
      render={<Button />}
      className={(state) => `media-live-button ${resolveClassName(className, state)}`}
      {...props}
    />
  );
}

interface VolumePopoverProps extends Omit<VolumePopoverPrimitive.RootProps, 'children'> {
  className?: string | undefined;
  orientation?: CoreVolumeSliderProps['orientation'];
  showTooltip?: boolean;
  popupClassName?: ClassValue;
}

type MuteButtonProps = Omit<MuteButtonPrimitive.Props, 'children'>;

function MuteButton({ className, ...props }: MuteButtonProps = {}) {
  return (
    <MuteButtonPrimitive
      render={<Button />}
      className={(state) => `media-mute-button ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeOffIconPrimitive className={'media-button-icon media-mute-button-off-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeOffIconPrimitive2 className={'media-button-icon media-mute-button-off-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeLowIconPrimitive className={'media-button-icon media-mute-button-low-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeLowIconPrimitive2 className={'media-button-icon media-mute-button-low-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeHighIconPrimitive className={'media-button-icon media-mute-button-high-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeHighIconPrimitive2 className={'media-button-icon media-mute-button-high-icon'} />
        </span>
      </>
    </MuteButtonPrimitive>
  );
}

type VolumeSliderProps = Omit<VolumeSliderPrimitive.RootProps, 'children'>;

function VolumeSlider({ className, ...props }: VolumeSliderProps = {}) {
  return (
    <VolumeSliderPrimitive.Root
      className={(state) => `media-slider media-volume-slider ${resolveClassName(className, state)}`}
      thumbAlignment="edge"
      {...props}
    >
      <VolumeSliderPrimitive.Track render={<SliderTrack />}>
        <VolumeSliderPrimitive.Fill render={<SliderFill />} />
      </VolumeSliderPrimitive.Track>
      <VolumeSliderPrimitive.Thumb render={<SliderThumb />} className={'media-volume-slider-thumb'} />
    </VolumeSliderPrimitive.Root>
  );
}

interface CaptionsMenuProps extends MenuProps {
  className?: ClassValue;
  triggerClassName?: ClassValue;
}

type RadioItemProps = Menu.RadioItemProps;

function RadioItem({ children, className, ...props }: RadioItemProps) {
  return (
    <Menu.RadioItem className={(state) => `media-menu-radio-item ${resolveClassName(className, state)}`} {...props}>
      {children}
      <Menu.ItemIndicator forceMount className={'media-menu-item-indicator'}>
        <>
          <span className="contents theme-minimal:hidden">
            <CheckIconPrimitive className={'media-menu-radio-item-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CheckIconPrimitive2 className={'media-menu-radio-item-icon'} />
          </span>
        </>
      </Menu.ItemIndicator>
    </Menu.RadioItem>
  );
}

function CaptionsMenu({ className, triggerClassName, ...props }: CaptionsMenuProps = {}) {
  return (
    <Menu.Root side="top" align="center" boundary="viewport" {...props}>
      <CaptionsRadioGroup.Root>
        <ButtonTooltip side="top">
          <Menu.Trigger
            render={<Button />}
            aria-label="Enable captions"
            className={`media-captions-button ${triggerClassName}`}
          >
            <>
              <span className="contents theme-minimal:hidden">
                <CaptionsOffIconPrimitive className={'media-button-icon media-captions-button-off-icon'} />
              </span>
              <span className="hidden theme-minimal:contents">
                <CaptionsOffIconPrimitive2 className={'media-button-icon media-captions-button-off-icon'} />
              </span>
            </>
            <>
              <span className="contents theme-minimal:hidden">
                <CaptionsOnIconPrimitive className={'media-button-icon media-captions-button-on-icon'} />
              </span>
              <span className="hidden theme-minimal:contents">
                <CaptionsOnIconPrimitive2 className={'media-button-icon media-captions-button-on-icon'} />
              </span>
            </>
          </Menu.Trigger>
        </ButtonTooltip>
        <Menu.Popup className={(state) => `media-menu-popup ${resolveClassName(className, state)}`}>
          <Menu.Content className={'media-menu-content'}>
            <CaptionsRadioGroup.Options
              className={'media-menu-radio-group'}
              renderItem={(props, item) => (
                <RadioItem {...props}>
                  <span>{item.label}</span>
                </RadioItem>
              )}
            ></CaptionsRadioGroup.Options>
          </Menu.Content>
        </Menu.Popup>
      </CaptionsRadioGroup.Root>
    </Menu.Root>
  );
}

type CastButtonProps = Omit<CastButtonPrimitive.Props, 'children'>;

function CastButton({ className, ...props }: CastButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <CastButtonPrimitive
        render={<Button />}
        className={(state) => `media-cast-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <CastEnterIconPrimitive className={'media-button-icon media-cast-button-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CastEnterIconPrimitive2 className={'media-button-icon media-cast-button-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <CastExitIconPrimitive className={'media-button-icon media-cast-button-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CastExitIconPrimitive2 className={'media-button-icon media-cast-button-exit-icon'} />
          </span>
        </>
      </CastButtonPrimitive>
    </ButtonTooltip>
  );
}

type AirPlayButtonProps = Omit<AirPlayButtonPrimitive.Props, 'children'>;

function AirPlayButton({ className, ...props }: AirPlayButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <AirPlayButtonPrimitive
        render={<Button />}
        className={(state) => `media-airplay-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <AirPlayEnterIconPrimitive className={'media-button-icon media-airplay-button-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <AirPlayEnterIconPrimitive2 className={'media-button-icon media-airplay-button-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <AirPlayExitIconPrimitive className={'media-button-icon media-airplay-button-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <AirPlayExitIconPrimitive2 className={'media-button-icon media-airplay-button-exit-icon'} />
          </span>
        </>
      </AirPlayButtonPrimitive>
    </ButtonTooltip>
  );
}

type PiPButtonProps = Omit<PiPButtonPrimitive.Props, 'children'>;

function PiPButton({ className, ...props }: PiPButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <PiPButtonPrimitive
        render={<Button />}
        className={(state) => `media-pip-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <PipEnterIconPrimitive className={'media-button-icon media-pip-button-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PipEnterIconPrimitive2 className={'media-button-icon media-pip-button-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PipExitIconPrimitive className={'media-button-icon media-pip-button-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PipExitIconPrimitive2 className={'media-button-icon media-pip-button-exit-icon'} />
          </span>
        </>
      </PiPButtonPrimitive>
    </ButtonTooltip>
  );
}

type FullscreenButtonProps = Omit<FullscreenButtonPrimitive.Props, 'children'>;

function FullscreenButton({ className, ...props }: FullscreenButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <FullscreenButtonPrimitive
        render={<Button />}
        className={(state) => `media-fullscreen-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <FullscreenEnterIconPrimitive className={'media-button-icon media-fullscreen-button-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <FullscreenEnterIconPrimitive2 className={'media-button-icon media-fullscreen-button-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <FullscreenExitIconPrimitive className={'media-button-icon media-fullscreen-button-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <FullscreenExitIconPrimitive2 className={'media-button-icon media-fullscreen-button-exit-icon'} />
          </span>
        </>
      </FullscreenButtonPrimitive>
    </ButtonTooltip>
  );
}

function MinimalLiveVideoControls() {
  return (
    <Controls.Root>
      <Controls.Backdrop className={'media-controls__backdrop'} />
      <Controls.Content className={'media-controls media-controls'}>
        <Tooltip.Provider>
          <Controls.Group className={'media-controls-start'}>
            <PlayButton />
            <LiveButton />
            <VolumePopover showTooltip side="right" orientation="horizontal" />
          </Controls.Group>

          <div aria-hidden="true" className={'media-controls-spacer'} />

          <Controls.Group className={'media-controls-end'}>
            <CaptionsMenu />
            <CastButton />
            <AirPlayButton />
            <PiPButton />
            <FullscreenButton />
          </Controls.Group>
        </Tooltip.Provider>
      </Controls.Content>
    </Controls.Root>
  );
}

interface LiveVideoHotkeysProps {
  disabled?: boolean | undefined;
}

interface LivePlaybackHotkeysProps {
  disabled?: boolean | undefined;
}

function LivePlaybackHotkeys({ disabled = false }: LivePlaybackHotkeysProps = {}) {
  return (
    <>
      <Hotkey disabled={disabled} keys="Space" action="togglePaused" />
      <Hotkey disabled={disabled} keys="k" action="togglePaused" />
      <Hotkey disabled={disabled} keys="m" action="toggleMuted" />
      <Hotkey disabled={disabled} keys="ArrowUp" action="volumeStep" value={0.05} />
      <Hotkey disabled={disabled} keys="ArrowDown" action="volumeStep" value={-0.05} />
    </>
  );
}

function LiveVideoHotkeys({ disabled = false }: LiveVideoHotkeysProps = {}) {
  return (
    <>
      <LivePlaybackHotkeys disabled={disabled} />
      <Hotkey disabled={disabled} keys="f" action="toggleFullscreen" />
      <Hotkey disabled={disabled} keys="c" action="toggleSubtitles" />
      <Hotkey disabled={disabled} keys="i" action="togglePictureInPicture" />
    </>
  );
}

interface LiveVideoGesturesProps {
  disabled?: boolean | undefined;
}

function LiveVideoGestures({ disabled = false }: LiveVideoGesturesProps = {}) {
  return (
    <>
      <Gesture disabled={disabled} type="tap" action="togglePaused" pointer="mouse" region="center" />
      <Gesture disabled={disabled} type="tap" action="toggleControls" pointer="touch" />
      <Gesture disabled={disabled} type="doubletap" action="toggleFullscreen" region="center" />
    </>
  );
}

type LiveVideoStatusIndicatorsProps = Omit<ComponentProps<'div'>, 'children'>;

type StatusAnnouncerProps = Omit<StatusAnnouncerPrimitive.Props, 'children'>;

function StatusAnnouncer({ className, ...props }: StatusAnnouncerProps = {}) {
  return (
    <StatusAnnouncerPrimitive
      className={(state) => `media-status-announcer ${resolveClassName(className, state)}`}
      {...props}
    />
  );
}

type VolumeIndicatorProps = Omit<VolumeIndicatorPrimitive.RootProps, 'children'>;

function VolumeIndicator({ className, ...props }: VolumeIndicatorProps = {}) {
  return (
    <VolumeIndicatorPrimitive.Root
      className={(state) => `media-volume-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <VolumeIndicatorPrimitive.Fill className={'media-volume-indicator-fill'}>
        <>
          <span className="contents theme-minimal:hidden">
            <VolumeHighIconPrimitive className={'media-volume-indicator-high-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <VolumeHighIconPrimitive2 className={'media-volume-indicator-high-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <VolumeLowIconPrimitive className={'media-volume-indicator-low-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <VolumeLowIconPrimitive2 className={'media-volume-indicator-low-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <VolumeOffIconPrimitive className={'media-volume-indicator-off-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <VolumeOffIconPrimitive2 className={'media-volume-indicator-off-icon'} />
          </span>
        </>
        <VolumeIndicatorPrimitive.Value className={'media-volume-indicator-value'} />
      </VolumeIndicatorPrimitive.Fill>
    </VolumeIndicatorPrimitive.Root>
  );
}

type StatusIndicatorProps = Omit<StatusIndicatorPrimitive.RootProps, 'children'>;

const TOP_STATUS_ACTIONS = ['toggleSubtitles', 'toggleFullscreen', 'togglePictureInPicture'] as const;

function StatusIndicator({ className, ...props }: StatusIndicatorProps = {}) {
  return (
    <StatusIndicatorPrimitive.Root
      actions={TOP_STATUS_ACTIONS}
      className={(state) => `media-status-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <div className={'media-status-indicator-content'}>
        <>
          <span className="contents theme-minimal:hidden">
            <CaptionsOnIconPrimitive className={'media-status-indicator-captions-on-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CaptionsOnIconPrimitive2 className={'media-status-indicator-captions-on-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <CaptionsOffIconPrimitive className={'media-status-indicator-captions-off-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <CaptionsOffIconPrimitive2 className={'media-status-indicator-captions-off-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <FullscreenEnterIconPrimitive className={'media-status-indicator-fullscreen-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <FullscreenEnterIconPrimitive2 className={'media-status-indicator-fullscreen-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <FullscreenExitIconPrimitive className={'media-status-indicator-fullscreen-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <FullscreenExitIconPrimitive2 className={'media-status-indicator-fullscreen-exit-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PipEnterIconPrimitive className={'media-status-indicator-pip-enter-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PipEnterIconPrimitive2 className={'media-status-indicator-pip-enter-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PipExitIconPrimitive className={'media-status-indicator-pip-exit-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PipExitIconPrimitive2 className={'media-status-indicator-pip-exit-icon'} />
          </span>
        </>
        <StatusIndicatorPrimitive.Value className={'media-status-indicator-value'} />
      </div>
    </StatusIndicatorPrimitive.Root>
  );
}

type PlaybackStatusIndicatorProps = Omit<StatusIndicatorPrimitive.RootProps, 'children'>;

const PLAYBACK_STATUS_ACTIONS = ['togglePaused'] as const;

function PlaybackStatusIndicator({ className, ...props }: PlaybackStatusIndicatorProps = {}) {
  return (
    <StatusIndicatorPrimitive.Root
      actions={PLAYBACK_STATUS_ACTIONS}
      className={(state) => `media-playback-status-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <PlayIconPrimitive className={'media-playback-status-indicator-play-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <PlayIconPrimitive2 className={'media-playback-status-indicator-play-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <PauseIconPrimitive className={'media-playback-status-indicator-pause-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <PauseIconPrimitive2 className={'media-playback-status-indicator-pause-icon'} />
        </span>
      </>
    </StatusIndicatorPrimitive.Root>
  );
}

function LiveVideoStatusIndicators({ className, ...props }: LiveVideoStatusIndicatorsProps = {}) {
  return (
    <>
      <StatusAnnouncer />
      <div className={`media-video-status-indicators ${className ?? ''}`} {...props}>
        <VolumeIndicator />
        <StatusIndicator />
        <PlaybackStatusIndicator />
      </div>
    </>
  );
}

function MinimalLiveVideoSkin({ children, className, poster, ...props }: MinimalLiveVideoSkinProps = {}) {
  const isPosterString = typeof poster === 'string';

  return (
    <Container className={`media-skin media-skin--minimal media-skin--live-video ${className ?? ''}`} {...props}>
      {children}
      <Poster src={isPosterString ? poster : undefined}>{isPosterString ? undefined : poster}</Poster>
      <BufferingIndicator />
      <ErrorDialog />
      <MinimalLiveVideoControls />
      <LiveVideoHotkeys />
      <LiveVideoGestures />
      <LiveVideoStatusIndicators />
    </Container>
  );
}

const Skin = MinimalLiveVideoSkin;

// ================================================================
// Player
// ================================================================

export interface LiveVideoPlayerProps {
  children?: ReactNode;
  style?: CSSProperties;
  className?: string;
  renderPoster?: Poster.Props['render'];
}

export function MinimalLiveVideoSkin({ renderPoster, ...props }: MinimalLiveVideoSkinProps) {
  return <Skin poster={renderPoster} {...props} />;
}

// ================================================================
// Components
// ================================================================

function Button({ className, ...props }: ButtonProps) {
  return <button className={`media-button ${className ?? ''}`} {...props} />;
}

type SliderTrackProps = ComponentProps<'div'>;

function SliderTrack({ className, ...props }: SliderTrackProps) {
  return <div className={`media-slider-track ${className ?? ''}`} {...props} />;
}

type SliderFillProps = ComponentProps<'div'>;

function SliderFill({ className, ...props }: SliderFillProps) {
  return <div className={`media-slider-fill ${className ?? ''}`} {...props} />;
}

type SliderThumbProps = ComponentProps<'div'>;

function SliderThumb({ className, ...props }: SliderThumbProps) {
  return <div className={`media-slider-thumb ${className ?? ''}`} {...props} />;
}

function VolumePopover({
  className,
  popupClassName,
  showTooltip = false,
  side = 'top',
  orientation = 'vertical',
  ...props
}: VolumePopoverProps = {}) {
  return (
    <VolumePopoverPrimitive.Root openOnHover delay={200} closeDelay={100} side={side} {...props}>
      <ButtonTooltip delay={0} disabled={!showTooltip} sticky side="top">
        <VolumePopoverPrimitive.Trigger render={<MuteButton className={className} />} />
      </ButtonTooltip>
      <VolumePopoverPrimitive.Popup className={`media-volume-popover ${popupClassName}`}>
        <VolumeSlider orientation={orientation} />
      </VolumePopoverPrimitive.Popup>
    </VolumePopoverPrimitive.Root>
  );
}

// ================================================================
// Error Dialog
// ================================================================

function ErrorDialog({ className, ...props }: ErrorDialogProps = {}) {
  return (
    <ErrorDialogPrimitive.Root>
      <ErrorDialogPrimitive.Backdrop className={'media-dialog-backdrop'} />
      <ErrorDialogPrimitive.Popup
        className={(state) => `media-dialog-popup ${resolveClassName(className, state)}`}
        {...props}
      >
        <div className={'media-dialog-content'}>
          <ErrorDialogPrimitive.Title className={'media-dialog-title'} />
          <ErrorDialogPrimitive.Description className={'media-dialog-description'} />
        </div>
        <div className={'media-dialog-actions'}>
          <ErrorDialogPrimitive.Close render={<Button />} className={'media-dialog-close'} />
        </div>
      </ErrorDialogPrimitive.Popup>
    </ErrorDialogPrimitive.Root>
  );
}

Minimal live audio skin

'use client';

import { StatusAnnouncer as StatusAnnouncerPrimitive, Container as ContainerPrimitive, ErrorDialog, Hotkey, LiveButton as LiveButtonPrimitive, Tooltip, MuteButton as MuteButtonPrimitive, VolumeSlider as VolumeSliderPrimitive, VolumePopover as VolumePopoverPrimitive, PlayButton as PlayButtonPrimitive, BufferingIndicator as BufferingIndicatorPrimitive, Controls } from '@videojs/react';
import { MuxAudio } from '@videojs/react/media/mux-audio';
import './player.css';
import { Player } from './player';
import { type ComponentProps, type ReactElement, type ReactNode, type SVGProps, type CSSProperties } from 'react';
import { VolumeOffIcon as VolumeOffIconPrimitive, VolumeLowIcon as VolumeLowIconPrimitive, VolumeHighIcon as VolumeHighIconPrimitive, RestartIcon as RestartIconPrimitive, PlayIcon as PlayIconPrimitive, PauseIcon as PauseIconPrimitive, SpinnerIcon as SpinnerIconPrimitive } from '@videojs/react/icons';
import { VolumeOffIcon as VolumeOffIconPrimitive2, VolumeLowIcon as VolumeLowIconPrimitive2, VolumeHighIcon as VolumeHighIconPrimitive2, RestartIcon as RestartIconPrimitive2, PlayIcon as PlayIconPrimitive2, PauseIcon as PauseIconPrimitive2, SpinnerIcon as SpinnerIconPrimitive2 } from '@videojs/react/icons/minimal';
import { type VolumeSliderProps as CoreVolumeSliderProps } from '@videojs/core';
import { type Poster } from '@videojs/react';

type ContainerProps = ContainerPrimitive.Props;

function Container({ children, className, ...props }: ContainerProps) {
  return (
    <ContainerPrimitive className={`media-container ${className ?? ''}`} {...props}>
      {children}
    </ContainerPrimitive>
  );
}

interface MinimalLiveAudioSkinProps extends Omit<NonNullable<ComponentProps<typeof Container>>, 'children'> {
  src: string;
}

type AudioErrorDialogProps = Omit<ErrorDialog.PopupProps, 'children'>;

type ButtonProps = ComponentProps<'button'>;

function AudioErrorDialog({ className, ...props }: AudioErrorDialogProps = {}) {
  return (
    <ErrorDialog.Root>
      <ErrorDialog.Backdrop className={'media-audio-dialog-backdrop'} />
      <ErrorDialog.Popup
        className={(state) => `media-audio-dialog-popup ${resolveClassName(className, state)}`}
        {...props}
      >
        <div className={'media-audio-dialog-content'}>
          <ErrorDialog.Title className={'media-audio-dialog-title'} />
          <ErrorDialog.Description className={'media-audio-dialog-description'} />
        </div>
        <div className={'media-audio-dialog-actions'}>
          <ErrorDialog.Close render={<Button />} className={'media-audio-dialog-close'} />
        </div>
      </ErrorDialog.Popup>
    </ErrorDialog.Root>
  );
}

type AudioPlayButtonProps = Omit<ComponentProps<'div'>, 'children'>;

type BufferingIndicatorProps = Omit<BufferingIndicatorPrimitive.Props, 'children'>;

function BufferingIndicator({ className, ...props }: BufferingIndicatorProps = {}) {
  return (
    <BufferingIndicatorPrimitive
      className={(state) => `media-buffering-indicator ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <SpinnerIconPrimitive className={'media-buffering-indicator-spinner-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <SpinnerIconPrimitive2 className={'media-buffering-indicator-spinner-icon'} />
        </span>
      </>
    </BufferingIndicatorPrimitive>
  );
}

type PlayButtonProps = Omit<PlayButtonPrimitive.Props, 'children'>;

interface ButtonTooltipProps extends Tooltip.RootProps {
  children: ReactElement;
  label?: ReactNode;
}

function ButtonTooltip({ children, label, ...props }: ButtonTooltipProps) {
  return (
    <Tooltip.Root {...props}>
      <Tooltip.Trigger render={children} />
      <Tooltip.Popup className={'media-tooltip'}>
        {label ?? <Tooltip.Label />}
        {!label && <Tooltip.Shortcut className={'media-tooltip-shortcut'} />}
      </Tooltip.Popup>
    </Tooltip.Root>
  );
}

function PlayButton({ className, ...props }: PlayButtonProps = {}) {
  return (
    <ButtonTooltip side="top">
      <PlayButtonPrimitive
        render={<Button />}
        className={(state) => `media-play-button ${resolveClassName(className, state)}`}
        {...props}
      >
        <>
          <span className="contents theme-minimal:hidden">
            <RestartIconPrimitive className={'media-button-icon media-play-button-restart-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <RestartIconPrimitive2 className={'media-button-icon media-play-button-restart-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PlayIconPrimitive className={'media-button-icon media-play-button-play-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PlayIconPrimitive2 className={'media-button-icon media-play-button-play-icon'} />
          </span>
        </>
        <>
          <span className="contents theme-minimal:hidden">
            <PauseIconPrimitive className={'media-button-icon media-play-button-pause-icon'} />
          </span>
          <span className="hidden theme-minimal:contents">
            <PauseIconPrimitive2 className={'media-button-icon media-play-button-pause-icon'} />
          </span>
        </>
      </PlayButtonPrimitive>
    </ButtonTooltip>
  );
}

function AudioPlayButton({ className, ...props }: AudioPlayButtonProps = {}) {
  return (
    <div className={`media-audio-play-button ${className ?? ''}`} {...props}>
      <BufferingIndicator className={'media-audio-play-button-buffering-indicator'} />
      <PlayButton />
    </div>
  );
}

type LiveButtonProps = Omit<LiveButtonPrimitive.Props, 'children'>;

function LiveButton({ className, ...props }: LiveButtonProps = {}) {
  return (
    <LiveButtonPrimitive
      render={<Button />}
      className={(state) => `media-live-button ${resolveClassName(className, state)}`}
      {...props}
    />
  );
}

interface VolumePopoverProps extends Omit<VolumePopoverPrimitive.RootProps, 'children'> {
  className?: string | undefined;
  orientation?: CoreVolumeSliderProps['orientation'];
  showTooltip?: boolean;
  popupClassName?: ClassValue;
}

type MuteButtonProps = Omit<MuteButtonPrimitive.Props, 'children'>;

function MuteButton({ className, ...props }: MuteButtonProps = {}) {
  return (
    <MuteButtonPrimitive
      render={<Button />}
      className={(state) => `media-mute-button ${resolveClassName(className, state)}`}
      {...props}
    >
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeOffIconPrimitive className={'media-button-icon media-mute-button-off-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeOffIconPrimitive2 className={'media-button-icon media-mute-button-off-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeLowIconPrimitive className={'media-button-icon media-mute-button-low-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeLowIconPrimitive2 className={'media-button-icon media-mute-button-low-icon'} />
        </span>
      </>
      <>
        <span className="contents theme-minimal:hidden">
          <VolumeHighIconPrimitive className={'media-button-icon media-mute-button-high-icon'} />
        </span>
        <span className="hidden theme-minimal:contents">
          <VolumeHighIconPrimitive2 className={'media-button-icon media-mute-button-high-icon'} />
        </span>
      </>
    </MuteButtonPrimitive>
  );
}

type VolumeSliderProps = Omit<VolumeSliderPrimitive.RootProps, 'children'>;

function VolumeSlider({ className, ...props }: VolumeSliderProps = {}) {
  return (
    <VolumeSliderPrimitive.Root
      className={(state) => `media-slider media-volume-slider ${resolveClassName(className, state)}`}
      thumbAlignment="edge"
      {...props}
    >
      <VolumeSliderPrimitive.Track render={<SliderTrack />}>
        <VolumeSliderPrimitive.Fill render={<SliderFill />} />
      </VolumeSliderPrimitive.Track>
      <VolumeSliderPrimitive.Thumb render={<SliderThumb />} className={'media-volume-slider-thumb'} />
    </VolumeSliderPrimitive.Root>
  );
}

function MinimalLiveAudioControls() {
  return (
    <Controls.Root visibility="always">
      <Controls.Content className={'media-controls media-controls-content'}>
        <Tooltip.Provider>
          <Controls.Group className={'media-controls-start'}>
            <AudioPlayButton />
            <LiveButton />
          </Controls.Group>

          <div aria-hidden="true" className={'media-controls-spacer'} />

          <Controls.Group className={'media-controls-end'}>
            <VolumePopover
              popupClassName={'media-audio-volume-popover'}
              showTooltip
              side="left"
              orientation="horizontal"
            />
          </Controls.Group>
        </Tooltip.Provider>
      </Controls.Content>
    </Controls.Root>
  );
}

interface LivePlaybackHotkeysProps {
  disabled?: boolean | undefined;
}

function LivePlaybackHotkeys({ disabled = false }: LivePlaybackHotkeysProps = {}) {
  return (
    <>
      <Hotkey disabled={disabled} keys="Space" action="togglePaused" />
      <Hotkey disabled={disabled} keys="k" action="togglePaused" />
      <Hotkey disabled={disabled} keys="m" action="toggleMuted" />
      <Hotkey disabled={disabled} keys="ArrowUp" action="volumeStep" value={0.05} />
      <Hotkey disabled={disabled} keys="ArrowDown" action="volumeStep" value={-0.05} />
    </>
  );
}

type StatusAnnouncerProps = Omit<StatusAnnouncerPrimitive.Props, 'children'>;

function StatusAnnouncer({ className, ...props }: StatusAnnouncerProps = {}) {
  return (
    <StatusAnnouncerPrimitive
      className={(state) => `media-status-announcer ${resolveClassName(className, state)}`}
      {...props}
    />
  );
}

function MinimalLiveAudioSkin({ children, className, ...props }: MinimalLiveAudioSkinProps = {}) {
  return (
    <Container
      className={`media-skin media-skin--minimal media-skin--live-audio media-audio-skin ${className ?? ''}`}
      {...props}
    >
      {children}
      <AudioErrorDialog />
      <MinimalLiveAudioControls />
      <LivePlaybackHotkeys />
      <StatusAnnouncer />
    </Container>
  );
}

const Skin = MinimalLiveAudioSkin;

// ================================================================
// Player
// ================================================================

export interface LiveAudioPlayerProps {
  children?: ReactNode;
  style?: CSSProperties;
  className?: string;
}

export function MinimalLiveAudioSkin(props: MinimalLiveAudioSkinProps) {
  return <Skin {...props} />;
}

// ================================================================
// Components
// ================================================================

function Button({ className, ...props }: ButtonProps) {
  return <button className={`media-button ${className ?? ''}`} {...props} />;
}

type SliderTrackProps = ComponentProps<'div'>;

function SliderTrack({ className, ...props }: SliderTrackProps) {
  return <div className={`media-slider-track ${className ?? ''}`} {...props} />;
}

type SliderFillProps = ComponentProps<'div'>;

function SliderFill({ className, ...props }: SliderFillProps) {
  return <div className={`media-slider-fill ${className ?? ''}`} {...props} />;
}

type SliderThumbProps = ComponentProps<'div'>;

function SliderThumb({ className, ...props }: SliderThumbProps) {
  return <div className={`media-slider-thumb ${className ?? ''}`} {...props} />;
}

function VolumePopover({
  className,
  popupClassName,
  showTooltip = false,
  side = 'top',
  orientation = 'vertical',
  ...props
}: VolumePopoverProps = {}) {
  return (
    <VolumePopoverPrimitive.Root openOnHover delay={200} closeDelay={100} side={side} {...props}>
      <ButtonTooltip delay={0} disabled={!showTooltip} sticky side="top">
        <VolumePopoverPrimitive.Trigger render={<MuteButton className={className} />} />
      </ButtonTooltip>
      <VolumePopoverPrimitive.Popup className={`media-volume-popover ${popupClassName}`}>
        <VolumeSlider orientation={orientation} />
      </VolumePopoverPrimitive.Popup>
    </VolumePopoverPrimitive.Root>
  );
}