UNPKG

@livepeer/react

Version:

React primitives for video apps.

1 lines 85 kB
{"version":3,"sources":["../../src/broadcast.tsx","../../src/broadcast/AudioEnabled.tsx","../../src/shared/primitive.tsx","../../src/shared/utils.ts","../../src/broadcast/context.tsx","../../src/broadcast/Broadcast.tsx","../../src/shared/context.tsx","../../src/broadcast/Controls.tsx","../../src/broadcast/Enabled.tsx","../../src/broadcast/Screenshare.tsx","../../src/broadcast/SourceSelect.tsx","../../src/shared/Select.tsx","../../src/broadcast/StatusIndicator.tsx","../../src/broadcast/Video.tsx","../../src/broadcast/VideoEnabled.tsx","../../src/shared/Container.tsx","../../src/shared/ErrorIndicator.tsx","../../src/shared/Fullscreen.tsx","../../src/shared/LoadingIndicator.tsx","../../src/shared/PictureInPictureTrigger.tsx","../../src/shared/Portal.tsx","../../src/shared/Slider.tsx","../../src/shared/Time.tsx"],"sourcesContent":["export {\n AudioEnabledIndicator,\n type AudioEnabledIndicatorProps,\n AudioEnabledTrigger,\n type AudioEnabledTriggerProps,\n} from \"./broadcast/AudioEnabled\";\nexport { type BroadcastProps, Root } from \"./broadcast/Broadcast\";\nexport { Controls, type ControlsProps } from \"./broadcast/Controls\";\nexport type {\n BroadcastContextValue,\n BroadcastScopedProps,\n} from \"./broadcast/context\";\nexport {\n BroadcastProvider,\n createBroadcastScope,\n useBroadcastContext,\n} from \"./broadcast/context\";\nexport {\n EnabledIndicator,\n type EnabledIndicatorProps,\n EnabledTrigger,\n type EnabledTriggerProps,\n} from \"./broadcast/Enabled\";\nexport {\n ScreenshareIndicator,\n type ScreenshareIndicatorProps,\n ScreenshareTrigger,\n type ScreenshareTriggerProps,\n} from \"./broadcast/Screenshare\";\nexport { SourceSelect, type SourceSelectProps } from \"./broadcast/SourceSelect\";\nexport {\n StatusIndicator,\n type StatusIndicatorProps,\n} from \"./broadcast/StatusIndicator\";\nexport { Video, type VideoProps } from \"./broadcast/Video\";\nexport {\n VideoEnabledIndicator,\n type VideoEnabledIndicatorProps,\n VideoEnabledTrigger,\n type VideoEnabledTriggerProps,\n} from \"./broadcast/VideoEnabled\";\nexport { Container, type ContainerProps } from \"./shared/Container\";\nexport type { MediaContextValue, MediaScopedProps } from \"./shared/context\";\nexport {\n createMediaScope,\n MediaProvider,\n useMediaContext,\n useStore,\n} from \"./shared/context\";\nexport {\n ErrorIndicator,\n type ErrorIndicatorProps,\n} from \"./shared/ErrorIndicator\";\nexport {\n FullscreenIndicator,\n type FullscreenIndicatorProps,\n FullscreenTrigger,\n type FullscreenTriggerProps,\n} from \"./shared/Fullscreen\";\nexport {\n LoadingIndicator,\n type LoadingIndicatorProps,\n} from \"./shared/LoadingIndicator\";\nexport {\n PictureInPictureTrigger,\n type PictureInPictureTriggerProps,\n} from \"./shared/PictureInPictureTrigger\";\nexport { Portal, type PortalProps } from \"./shared/Portal\";\nexport {\n SelectArrow,\n type SelectArrowProps,\n SelectContent,\n type SelectContentProps,\n SelectGroup,\n type SelectGroupProps,\n SelectIcon,\n type SelectIconProps,\n SelectItem,\n SelectItemIndicator,\n type SelectItemIndicatorProps,\n type SelectItemProps,\n SelectItemText,\n type SelectItemTextProps,\n SelectLabel,\n type SelectLabelProps,\n SelectPortal,\n type SelectPortalProps,\n type SelectProps,\n SelectRoot,\n SelectScrollDownButton,\n type SelectScrollDownButtonProps,\n SelectScrollUpButton,\n type SelectScrollUpButtonProps,\n SelectSeparator,\n type SelectSeparatorProps,\n SelectTrigger,\n type SelectTriggerProps,\n SelectValue,\n type SelectValueProps,\n SelectViewport,\n type SelectViewportProps,\n} from \"./shared/Select\";\nexport {\n Range,\n type RangeProps,\n type SliderProps,\n Thumb,\n type ThumbProps,\n Track,\n type TrackProps,\n} from \"./shared/Slider\";\nexport { Time, type TimeProps } from \"./shared/Time\";\n","\"use client\";\n\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\nimport { Presence } from \"@radix-ui/react-presence\";\nimport React, { useMemo } from \"react\";\nimport { useStore } from \"zustand\";\nimport { useShallow } from \"zustand/react/shallow\";\nimport * as Radix from \"../shared/primitive\";\nimport { noPropagate } from \"../shared/utils\";\nimport { type BroadcastScopedProps, useBroadcastContext } from \"./context\";\n\n/**\n * AudioEnabledTrigger\n */\n\nconst AUDIO_ENABLED_TRIGGER_NAME = \"AudioEnabledTrigger\";\n\ntype AudioEnabledTriggerElement = React.ElementRef<\n typeof Radix.Primitive.button\n>;\n\ninterface AudioEnabledTriggerProps\n extends Radix.ComponentPropsWithoutRef<typeof Radix.Primitive.button> {}\n\nconst AudioEnabledTrigger = React.forwardRef<\n AudioEnabledTriggerElement,\n AudioEnabledTriggerProps\n>((props: BroadcastScopedProps<AudioEnabledTriggerProps>, forwardedRef) => {\n const { __scopeBroadcast, ...audioEnabledProps } = props;\n\n const broadcastContext = useBroadcastContext(\n AUDIO_ENABLED_TRIGGER_NAME,\n __scopeBroadcast,\n );\n\n const { audio, title, toggleAudio } = useStore(\n broadcastContext.store,\n useShallow(({ audio, aria, __controlsFunctions }) => ({\n audio,\n title: aria.audioTrigger,\n toggleAudio: __controlsFunctions.toggleAudio,\n })),\n );\n\n return (\n <Radix.Primitive.button\n type=\"button\"\n aria-pressed={audio}\n aria-label={title}\n title={title}\n {...audioEnabledProps}\n onClick={composeEventHandlers(props.onClick, noPropagate(toggleAudio))}\n ref={forwardedRef}\n data-livepeer-controls-audio-enabled-trigger=\"\"\n data-enabled={String(audio)}\n />\n );\n});\n\nAudioEnabledTrigger.displayName = AUDIO_ENABLED_TRIGGER_NAME;\n\n/**\n * AudioEnabledIndicator\n */\n\nconst AUDIO_ENABLED_INDICATOR_NAME = \"AudioEnabledIndicator\";\n\ntype AudioEnabledIndicatorElement = React.ElementRef<\n typeof Radix.Primitive.div\n>;\n\ninterface AudioEnabledIndicatorProps\n extends Radix.ComponentPropsWithoutRef<typeof Radix.Primitive.div> {\n /**\n * Used to force mounting when more control is needed. Useful when\n * controlling animation with React animation libraries.\n */\n forceMount?: true;\n /**\n * The matcher used to determine whether the element should be shown, given the `audio` state.\n * Defaults to `true`, which means \"shown when broadcast audio is enabled\".\n */\n matcher?: boolean | ((state: boolean) => boolean);\n}\n\nconst AudioEnabledIndicator = React.forwardRef<\n AudioEnabledIndicatorElement,\n AudioEnabledIndicatorProps\n>((props: BroadcastScopedProps<AudioEnabledIndicatorProps>, forwardedRef) => {\n const {\n __scopeBroadcast,\n forceMount,\n matcher = true,\n ...audioIndicatorProps\n } = props;\n\n const broadcastContext = useBroadcastContext(\n AUDIO_ENABLED_INDICATOR_NAME,\n __scopeBroadcast,\n );\n\n const audio = useStore(broadcastContext.store, ({ audio }) => audio);\n\n const isPresent = useMemo(\n () => (typeof matcher === \"boolean\" ? matcher === audio : matcher(audio)),\n [audio, matcher],\n );\n\n return (\n <Presence present={forceMount || isPresent}>\n <Radix.Primitive.div\n {...audioIndicatorProps}\n ref={forwardedRef}\n data-livepeer-controls-audio-enabled-indicator=\"\"\n data-enabled={String(audio)}\n data-visible={String(isPresent)}\n />\n </Presence>\n );\n});\n\nAudioEnabledIndicator.displayName = AUDIO_ENABLED_INDICATOR_NAME;\n\nexport { AudioEnabledIndicator, AudioEnabledTrigger };\nexport type { AudioEnabledIndicatorProps, AudioEnabledTriggerProps };\n","import { Slot } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport * as ReactDOM from \"react-dom\";\n\nconst NODES = [\n \"a\",\n \"audio\",\n \"button\",\n \"div\",\n \"form\",\n \"h2\",\n \"h3\",\n \"img\",\n \"input\",\n \"label\",\n \"li\",\n \"nav\",\n \"ol\",\n \"p\",\n \"span\",\n \"svg\",\n \"ul\",\n \"video\",\n] as const;\n\n// Temporary while we await merge of this fix:\n// https://github.com/DefinitelyTyped/DefinitelyTyped/pull/55396\n// biome-ignore lint/suspicious/noExplicitAny: any\ntype PropsWithoutRef<P> = P extends any\n ? \"ref\" extends keyof P\n ? Pick<P, Exclude<keyof P, \"ref\">>\n : P\n : P;\ntype ComponentPropsWithoutRef<T extends React.ElementType> = PropsWithoutRef<\n React.ComponentProps<T>\n>;\n\ntype Primitives = {\n [E in (typeof NODES)[number]]: PrimitiveForwardRefComponent<E>;\n};\ntype PrimitivePropsWithRef<E extends React.ElementType> =\n React.ComponentPropsWithRef<E> & {\n asChild?: boolean;\n };\n\ninterface PrimitiveForwardRefComponent<E extends React.ElementType>\n extends React.ForwardRefExoticComponent<PrimitivePropsWithRef<E>> {}\n\n/* -------------------------------------------------------------------------------------------------\n * Primitive\n * -----------------------------------------------------------------------------------------------*/\n\nconst Primitive = NODES.reduce((primitive, node) => {\n const Node = React.forwardRef(\n // biome-ignore lint/suspicious/noExplicitAny: any\n (props: PrimitivePropsWithRef<typeof node>, forwardedRef: any) => {\n const { asChild, ...primitiveProps } = props;\n // biome-ignore lint/suspicious/noExplicitAny: any\n const Comp: any = asChild ? Slot : node;\n\n React.useEffect(() => {\n // biome-ignore lint/suspicious/noExplicitAny: any\n (window as any)[Symbol.for(\"radix-ui\")] = true;\n }, []);\n\n return <Comp {...primitiveProps} ref={forwardedRef} />;\n },\n );\n\n Node.displayName = `Primitive.${node}`;\n\n // biome-ignore lint/performance/noAccumulatingSpread: no spread\n return { ...primitive, [node]: Node };\n}, {} as Primitives);\n\n/* -------------------------------------------------------------------------------------------------\n * Utils\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * Flush custom event dispatch\n * https://github.com/radix-ui/primitives/pull/1378\n *\n * React batches *all* event handlers since version 18, this introduces certain considerations when using custom event types.\n *\n * Internally, React prioritizes events in the following order:\n * - discrete\n * - continuous\n * - default\n *\n * https://github.com/facebook/react/blob/a8a4742f1c54493df00da648a3f9d26e3db9c8b5/packages/react-dom/src/events/ReactDOMEventListener.js#L294-L350\n *\n * `discrete` is an important distinction as updates within these events are applied immediately.\n * React however, is not able to infer the priority of custom event types due to how they are detected internally.\n * Because of this, it's possible for updates from custom events to be unexpectedly batched when\n * dispatched by another `discrete` event.\n *\n * In order to ensure that updates from custom events are applied predictably, we need to manually flush the batch.\n * This utility should be used when dispatching a custom event from within another `discrete` event, this utility\n * is not necessary when dispatching known event types, or if dispatching a custom type inside a non-discrete event.\n * For example:\n *\n * dispatching a known click 👎\n * target.dispatchEvent(new Event(‘click’))\n *\n * dispatching a custom type within a non-discrete event 👎\n * onScroll={(event) => event.target.dispatchEvent(new CustomEvent(‘customType’))}\n *\n * dispatching a custom type within a `discrete` event 👍\n * onPointerDown={(event) => dispatchDiscreteCustomEvent(event.target, new CustomEvent(‘customType’))}\n *\n * Note: though React classifies `focus`, `focusin` and `focusout` events as `discrete`, it's not recommended to use\n * this utility with them. This is because it's possible for those handlers to be called implicitly during render\n * e.g. when focus is within a component as it is unmounted, or when managing focus on mount.\n */\n\nfunction dispatchDiscreteCustomEvent<E extends CustomEvent>(\n target: E[\"target\"],\n event: E,\n) {\n if (target) ReactDOM.flushSync(() => target.dispatchEvent(event));\n}\n\n/* -----------------------------------------------------------------------------------------------*/\n\nconst Root = Primitive;\n\nexport {\n Primitive,\n //\n Root,\n //\n dispatchDiscreteCustomEvent,\n};\nexport type { ComponentPropsWithoutRef, PrimitivePropsWithRef };\n","export const noPropagate =\n <\n E extends {\n stopPropagation(): void;\n },\n >(\n // biome-ignore lint/suspicious/noExplicitAny: any\n cb: (...args: any) => any,\n ) =>\n (event: E) => {\n event.stopPropagation();\n\n return cb();\n };\n","import type { BroadcastStore } from \"@livepeer/core-web/broadcast\";\nimport type { Scope } from \"@radix-ui/react-context\";\nimport { createContextScope } from \"@radix-ui/react-context\";\n\nconst MEDIA_NAME = \"Broadcast\";\n\n// biome-ignore lint/complexity/noBannedTypes: allow {}\ntype BroadcastScopedProps<P = {}> = P & { __scopeBroadcast?: Scope };\nconst [createBroadcastContext, createBroadcastScope] =\n createContextScope(MEDIA_NAME);\n\ntype BroadcastContextValue = {\n store: BroadcastStore;\n};\n\nconst [BroadcastProvider, useBroadcastContext] =\n createBroadcastContext<BroadcastContextValue>(MEDIA_NAME);\n\nexport { BroadcastProvider, createBroadcastScope, useBroadcastContext };\nexport type { BroadcastContextValue, BroadcastScopedProps };\n","\"use client\";\n\nimport {\n addLegacyMediaMetricsToStore,\n addMetricsToStore,\n createControllerStore,\n type InitialProps,\n type PlaybackEvent,\n} from \"@livepeer/core/media\";\nimport { createStorage, noopStorage } from \"@livepeer/core/storage\";\nimport { version } from \"@livepeer/core/version\";\nimport {\n createBroadcastStore,\n getBroadcastDeviceInfo,\n type InitialBroadcastProps,\n} from \"@livepeer/core-web/broadcast\";\nimport { getDeviceInfo } from \"@livepeer/core-web/browser\";\n// biome-ignore lint/correctness/noUnusedImports: ignored using `--suppress`\nimport React, { type PropsWithChildren, useEffect, useRef } from \"react\";\nimport { MediaProvider, type MediaScopedProps } from \"../shared/context\";\nimport { BroadcastProvider, type BroadcastScopedProps } from \"./context\";\n\ninterface BroadcastProps\n extends PropsWithChildren<\n Omit<Partial<InitialBroadcastProps>, \"aspectRatio\" | \"ingestUrl\"> &\n Pick<\n Partial<InitialProps>,\n \"onError\" | \"storage\" | \"timeout\" | \"videoQuality\"\n >\n > {\n /**\n * The WHIP WebRTC ingest URL for the Broadcast. The ingestUrl can be created using `getIngest`\n * from a string (assumed to be stream keys or URLs), Cloudflare stream data, Cloudflare URL data,\n * or Livepeer stream data.\n */\n ingestUrl: string | null;\n\n /**\n * The aspect ratio of the media. Defaults to 16 / 9.\n * This significantly improves cumulative layout shift.\n * Set to `null` to render a plain div primitive.\n *\n * @see {@link https://web.dev/cls/}\n */\n aspectRatio?: number | null;\n\n /**\n * A callback that is called when the broadcast's metrics events are emitted.\n * This can be used to integrate with other analytics providers.\n */\n // biome-ignore lint/suspicious/noExplicitAny: allow any for incoming callback\n onPlaybackEvents?: (events: PlaybackEvent[]) => Promise<any> | any;\n\n /**\n * The interval at which metrics are sent, in ms. Defaults to 5000.\n */\n metricsInterval?: number;\n\n /**\n * @deprecated in favor of `iceServers`\n *\n * Whether to disable ICE gathering.\n *\n * Set to true to disable ICE gathering. This is useful for testing purposes.\n */\n noIceGathering?: boolean;\n\n silentAudioTrack?: boolean;\n /**\n * The ICE servers to use.\n *\n * If not provided, the default ICE servers will be used.\n */\n iceServers?: RTCIceServer | RTCIceServer[];\n}\n\nconst Broadcast = (\n props: MediaScopedProps<BroadcastScopedProps<BroadcastProps>>,\n) => {\n const {\n aspectRatio = 16 / 9,\n children,\n ingestUrl,\n onError,\n storage,\n timeout,\n videoQuality,\n onPlaybackEvents,\n metricsInterval,\n ...rest\n } = props;\n\n const mediaStore = useRef(\n createControllerStore({\n device: getDeviceInfo(version.react),\n storage:\n storage ??\n createStorage(\n storage !== null && typeof window !== \"undefined\"\n ? {\n storage: window.localStorage,\n }\n : {\n storage: noopStorage,\n },\n ),\n src: null,\n initialProps: {\n hotkeys: \"broadcast\",\n aspectRatio,\n volume: 0,\n onError,\n timeout,\n videoQuality,\n },\n }),\n );\n\n const broadcastStore = useRef(\n createBroadcastStore({\n device: getBroadcastDeviceInfo(version.react),\n storage:\n storage ??\n createStorage(\n storage !== null && typeof window !== \"undefined\"\n ? {\n storage: window.localStorage,\n }\n : {\n storage: noopStorage,\n },\n ),\n ingestUrl,\n initialProps: {\n aspectRatio,\n ...rest,\n },\n }),\n );\n\n useEffect(() => {\n return () => {\n mediaStore?.current?.destroy?.();\n broadcastStore?.current?.destroy?.();\n };\n }, []);\n\n useEffect(() => {\n if (ingestUrl) {\n broadcastStore.current.store\n .getState()\n .__controlsFunctions.setIngestUrl(ingestUrl);\n }\n }, [ingestUrl]);\n\n useEffect(() => {\n const metrics = addLegacyMediaMetricsToStore(mediaStore.current.store);\n\n return () => {\n metrics.destroy();\n };\n }, []);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: no deps\n useEffect(() => {\n const metrics = addMetricsToStore(mediaStore.current.store, {\n onPlaybackEvents,\n interval: metricsInterval,\n });\n\n return () => {\n metrics.destroy();\n };\n }, []);\n\n return (\n <MediaProvider store={mediaStore.current.store} scope={props.__scopeMedia}>\n <BroadcastProvider\n store={broadcastStore.current.store}\n scope={props.__scopeBroadcast}\n >\n {children}\n </BroadcastProvider>\n </MediaProvider>\n );\n};\n\nBroadcast.displayName = \"Broadcast\";\n\nconst Root = Broadcast;\n\nexport { Root };\nexport type { BroadcastProps };\n","import type { MediaControllerStore } from \"@livepeer/core/media\";\nimport type { Scope } from \"@radix-ui/react-context\";\nimport { createContextScope } from \"@radix-ui/react-context\";\nimport { useStore as useStoreZustand } from \"zustand\";\n\nconst MEDIA_NAME = \"Media\";\n\n// biome-ignore lint/complexity/noBannedTypes: allow {}\ntype MediaScopedProps<P = {}> = P & { __scopeMedia?: Scope };\nconst [createMediaContext, createMediaScope] = createContextScope(MEDIA_NAME);\n\ntype MediaContextValue = {\n store: MediaControllerStore;\n};\n\nconst [MediaProvider, useMediaContext] =\n createMediaContext<MediaContextValue>(MEDIA_NAME);\n\nconst useStore = useStoreZustand;\n\nexport { MediaProvider, createMediaScope, useMediaContext, useStore };\nexport type { MediaContextValue, MediaScopedProps };\n","\"use client\";\n\nimport { Presence } from \"@radix-ui/react-presence\";\n\nimport React, { useEffect, useMemo } from \"react\";\n\nimport { useStore } from \"zustand\";\n\nimport { useShallow } from \"zustand/react/shallow\";\nimport { type MediaScopedProps, useMediaContext } from \"../shared/context\";\nimport * as Radix from \"../shared/primitive\";\nimport { type BroadcastScopedProps, useBroadcastContext } from \"./context\";\n\nconst CONTROLS_NAME = \"Controls\";\n\ntype ControlsElement = React.ElementRef<typeof Radix.Primitive.div>;\n\ninterface ControlsProps\n extends Radix.ComponentPropsWithoutRef<typeof Radix.Primitive.div> {\n /**\n * Used to force mounting when more control is needed. Useful when\n * controlling animation with React animation libraries.\n */\n forceMount?: true;\n /**\n * Auto-hide the controls after a mouse or touch interaction (in milliseconds).\n *\n * Defaults to 3000. Set to 0 for no hiding.\n */\n autoHide?: number;\n}\n\nconst Controls = React.forwardRef<ControlsElement, ControlsProps>(\n (\n props: MediaScopedProps<BroadcastScopedProps<ControlsProps>>,\n forwardedRef,\n ) => {\n const {\n forceMount,\n __scopeMedia,\n __scopeBroadcast,\n // biome-ignore lint/correctness/noUnusedVariables: ignored using `--suppress`\n onClick,\n style,\n autoHide,\n ...controlsProps\n } = props;\n\n const context = useMediaContext(CONTROLS_NAME, __scopeMedia);\n\n const { hidden, loading, error } = useStore(\n context.store,\n useShallow(({ hidden, loading, error }) => ({\n hidden,\n loading,\n error: error?.type ?? null,\n })),\n );\n\n const broadcastContext = useBroadcastContext(\n CONTROLS_NAME,\n __scopeBroadcast,\n );\n\n const { isWebRTCSupported } = useStore(\n broadcastContext.store,\n useShallow(({ enabled, __device }) => ({\n enabled,\n isWebRTCSupported: __device.isMediaDevicesSupported,\n })),\n );\n\n const shown = useMemo(\n () => !hidden && !loading && !error && isWebRTCSupported,\n [hidden, loading, error, isWebRTCSupported],\n );\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: only set once to prevent flashing\n useEffect(() => {\n if (autoHide !== undefined) {\n context.store.getState().__controlsFunctions.setAutohide(autoHide);\n }\n }, []);\n\n return (\n <Presence present={forceMount || shown}>\n <Radix.Primitive.div\n {...controlsProps}\n ref={forwardedRef}\n data-livepeer-controls=\"\"\n data-visible={String(shown)}\n style={{\n ...style,\n // ensures controls expands in ratio\n position: \"absolute\",\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n }}\n />\n </Presence>\n );\n },\n);\n\nControls.displayName = CONTROLS_NAME;\n\nexport { Controls };\nexport type { ControlsProps };\n","\"use client\";\n\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\nimport { Presence } from \"@radix-ui/react-presence\";\nimport React, { useMemo } from \"react\";\nimport { useStore } from \"zustand\";\nimport { useShallow } from \"zustand/react/shallow\";\nimport { type MediaScopedProps, useMediaContext } from \"../shared/context\";\nimport * as Radix from \"../shared/primitive\";\nimport { noPropagate } from \"../shared/utils\";\nimport { type BroadcastScopedProps, useBroadcastContext } from \"./context\";\n\n/**\n * EnabledTrigger\n */\n\nconst ENABLED_TRIGGER_NAME = \"EnabledTrigger\";\n\ntype EnabledTriggerElement = React.ElementRef<typeof Radix.Primitive.button>;\n\ninterface EnabledTriggerProps\n extends Radix.ComponentPropsWithoutRef<typeof Radix.Primitive.button> {}\n\nconst EnabledTrigger = React.forwardRef<\n EnabledTriggerElement,\n EnabledTriggerProps\n>((props: BroadcastScopedProps<EnabledTriggerProps>, forwardedRef) => {\n const { __scopeBroadcast, ...playProps } = props;\n\n const broadcastContext = useBroadcastContext(\n ENABLED_TRIGGER_NAME,\n __scopeBroadcast,\n );\n\n const { enabled, title, toggleEnabled } = useStore(\n broadcastContext.store,\n useShallow(({ enabled, aria, __controlsFunctions }) => ({\n enabled,\n title: aria.start,\n toggleEnabled: __controlsFunctions.toggleEnabled,\n })),\n );\n\n return (\n <Radix.Primitive.button\n type=\"button\"\n aria-pressed={enabled}\n aria-label={title}\n title={title}\n {...playProps}\n onClick={composeEventHandlers(props.onClick, noPropagate(toggleEnabled))}\n ref={forwardedRef}\n data-livepeer-controls-enabled-trigger=\"\"\n data-enabled={String(enabled)}\n />\n );\n});\n\nEnabledTrigger.displayName = ENABLED_TRIGGER_NAME;\n\n/**\n * EnabledIndicator\n */\n\nconst ENABLED_INDICATOR_NAME = \"EnabledIndicator\";\n\ntype EnabledIndicatorElement = React.ElementRef<typeof Radix.Primitive.div>;\n\ninterface EnabledIndicatorProps\n extends Radix.ComponentPropsWithoutRef<typeof Radix.Primitive.div> {\n /**\n * Used to force mounting when more control is needed. Useful when\n * controlling animation with React animation libraries.\n */\n forceMount?: true;\n /**\n * The matcher used to determine whether the element should be shown, given the `enabled` state.\n * Defaults to `true`, which means \"shown when broadcasting is enabled\".\n */\n matcher?: boolean | ((state: boolean) => boolean);\n}\n\nconst EnabledIndicator = React.forwardRef<\n EnabledIndicatorElement,\n EnabledIndicatorProps\n>(\n (\n props: MediaScopedProps<BroadcastScopedProps<EnabledIndicatorProps>>,\n forwardedRef,\n ) => {\n const {\n __scopeBroadcast,\n __scopeMedia,\n forceMount,\n matcher = true,\n ...playPauseIndicatorProps\n } = props;\n\n const context = useMediaContext(ENABLED_INDICATOR_NAME, __scopeMedia);\n\n const loading = useStore(context.store, ({ loading }) => loading);\n\n const broadcastContext = useBroadcastContext(\n ENABLED_INDICATOR_NAME,\n __scopeBroadcast,\n );\n\n const enabled = useStore(broadcastContext.store, ({ enabled }) => enabled);\n\n const isPresent = useMemo(\n () =>\n !loading &&\n (typeof matcher === \"boolean\" ? matcher === enabled : matcher(enabled)),\n [enabled, matcher, loading],\n );\n\n return (\n <Presence present={forceMount || isPresent}>\n <Radix.Primitive.div\n {...playPauseIndicatorProps}\n ref={forwardedRef}\n data-livepeer-controls-enabled-indicator=\"\"\n data-enabled={String(enabled)}\n data-visible={String(isPresent)}\n />\n </Presence>\n );\n },\n);\n\nEnabledIndicator.displayName = ENABLED_INDICATOR_NAME;\n\nexport { EnabledIndicator, EnabledTrigger };\nexport type { EnabledIndicatorProps, EnabledTriggerProps };\n","\"use client\";\n\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\nimport { Presence } from \"@radix-ui/react-presence\";\nimport React, { useMemo } from \"react\";\nimport { useStore } from \"zustand\";\nimport { useShallow } from \"zustand/react/shallow\";\nimport * as Radix from \"../shared/primitive\";\nimport { noPropagate } from \"../shared/utils\";\nimport { type BroadcastScopedProps, useBroadcastContext } from \"./context\";\n\n/**\n * ScreenshareTrigger\n */\n\nconst SCREENSHARE_TRIGGER_NAME = \"ScreenshareTrigger\";\n\ntype ScreenshareTriggerElement = React.ElementRef<\n typeof Radix.Primitive.button\n>;\n\ninterface ScreenshareTriggerProps\n extends Radix.ComponentPropsWithoutRef<typeof Radix.Primitive.button> {\n /**\n * Used to force mounting when more control is needed. Useful when\n * controlling animation with React animation libraries.\n */\n forceMount?: true;\n}\n\nconst ScreenshareTrigger = React.forwardRef<\n ScreenshareTriggerElement,\n ScreenshareTriggerProps\n>((props: BroadcastScopedProps<ScreenshareTriggerProps>, forwardedRef) => {\n const { __scopeBroadcast, forceMount, ...screenshareProps } = props;\n\n const broadcastContext = useBroadcastContext(\n SCREENSHARE_TRIGGER_NAME,\n __scopeBroadcast,\n );\n\n const { isSupported, isActive, title, toggleDisplayMedia } = useStore(\n broadcastContext.store,\n useShallow(({ mediaDeviceIds, aria, __device, __controlsFunctions }) => ({\n isActive: mediaDeviceIds.videoinput === \"screen\",\n title: aria.screenshareTrigger,\n toggleDisplayMedia: __controlsFunctions.toggleDisplayMedia,\n isSupported: __device.isDisplayMediaSupported,\n })),\n );\n\n return (\n <Presence present={forceMount || isSupported}>\n <Radix.Primitive.button\n type=\"button\"\n aria-pressed={isActive}\n aria-label={title}\n title={title}\n {...screenshareProps}\n onClick={composeEventHandlers(\n props.onClick,\n noPropagate(toggleDisplayMedia),\n )}\n ref={forwardedRef}\n data-livepeer-controls-screenshare-trigger=\"\"\n data-active={String(isActive)}\n data-visible={String(isSupported)}\n />\n </Presence>\n );\n});\n\nScreenshareTrigger.displayName = SCREENSHARE_TRIGGER_NAME;\n\n/**\n * ScreenshareIndicator\n */\n\nconst SCREENSHARE_INDICATOR_NAME = \"ScreenshareIndicator\";\n\ntype ScreenshareIndicatorElement = React.ElementRef<typeof Radix.Primitive.div>;\n\ninterface ScreenshareIndicatorProps\n extends Radix.ComponentPropsWithoutRef<typeof Radix.Primitive.div> {\n /**\n * Used to force mounting when more control is needed. Useful when\n * controlling animation with React animation libraries.\n */\n forceMount?: true;\n /**\n * The matcher used to determine whether the element should be shown, given the screenshare state.\n * Defaults to `true`, which means \"shown when screenshare is active\".\n */\n matcher?: boolean | ((state: boolean) => boolean);\n}\n\nconst ScreenshareIndicator = React.forwardRef<\n ScreenshareIndicatorElement,\n ScreenshareIndicatorProps\n>((props: BroadcastScopedProps<ScreenshareIndicatorProps>, forwardedRef) => {\n const {\n __scopeBroadcast,\n forceMount,\n matcher = true,\n ...audioIndicatorProps\n } = props;\n\n const broadcastContext = useBroadcastContext(\n SCREENSHARE_INDICATOR_NAME,\n __scopeBroadcast,\n );\n\n const { isActive, isSupported } = useStore(\n broadcastContext.store,\n ({ mediaDeviceIds, __device }) => ({\n isActive: mediaDeviceIds.videoinput === \"screen\",\n isSupported: __device.isDisplayMediaSupported,\n }),\n );\n\n const isPresent = useMemo(\n () =>\n isSupported\n ? typeof matcher === \"boolean\"\n ? matcher === isActive\n : matcher(isActive)\n : false,\n [isSupported, isActive, matcher],\n );\n\n return (\n <Presence present={forceMount || isPresent}>\n <Radix.Primitive.div\n {...audioIndicatorProps}\n ref={forwardedRef}\n data-livepeer-controls-screenshare-indicator=\"\"\n data-active={String(isActive)}\n data-visible={String(isPresent)}\n />\n </Presence>\n );\n});\n\nScreenshareIndicator.displayName = SCREENSHARE_INDICATOR_NAME;\n\nexport { ScreenshareIndicator, ScreenshareTrigger };\nexport type { ScreenshareIndicatorProps, ScreenshareTriggerProps };\n","\"use client\";\n\nimport type {\n AudioDeviceId,\n MediaDeviceInfoExtended,\n} from \"@livepeer/core-web/broadcast\";\n// biome-ignore lint/correctness/noUnusedImports: ignored using `--suppress`\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\nimport { Presence } from \"@radix-ui/react-presence\";\n// biome-ignore lint/style/useImportType: necessary import\nimport React, { useCallback } from \"react\";\nimport { useStore } from \"zustand\";\nimport { useShallow } from \"zustand/react/shallow\";\nimport type { MediaScopedProps } from \"../shared/context\";\nimport type * as Radix from \"../shared/primitive\";\nimport * as SelectPrimitive from \"../shared/Select\";\nimport { type BroadcastScopedProps, useBroadcastContext } from \"./context\";\n\nconst SOURCE_SELECT_NAME = \"SourceSelect\";\n\ninterface SourceSelectProps\n extends Omit<\n Radix.ComponentPropsWithoutRef<typeof SelectPrimitive.SelectRoot>,\n \"children\"\n > {\n /**\n * Used to force mounting when more control is needed. Useful when\n * controlling animation with React animation libraries.\n */\n forceMount?: true;\n\n /**\n * The type of media device to filter the list by.\n */\n type: \"audioinput\" | \"videoinput\";\n\n /**\n * Children which consume the media device info passed down via a function.\n *\n * @param devices the media device info for all available media devices.\n * @returns Select children using the media devices.\n */\n children: (devices: MediaDeviceInfoExtended[] | null) => React.ReactNode;\n}\n\nconst SourceSelect = (\n props: MediaScopedProps<BroadcastScopedProps<SourceSelectProps>>,\n) => {\n const {\n __scopeMedia,\n __scopeBroadcast,\n forceMount,\n type,\n children,\n ...controlsProps\n } = props;\n\n const broadcastContext = useBroadcastContext(\n SOURCE_SELECT_NAME,\n __scopeBroadcast,\n );\n\n const {\n video,\n audio,\n mediaDevices,\n isSupported,\n mediaDeviceIds,\n requestMediaDeviceId,\n } = useStore(\n broadcastContext.store,\n useShallow(\n ({\n video,\n audio,\n mediaDevices,\n __device,\n mediaDeviceIds,\n __controlsFunctions,\n }) => ({\n video,\n audio,\n mediaDevices: mediaDevices?.filter((d) => d.kind === type) ?? null,\n isSupported: __device.isMediaDevicesSupported,\n requestMediaDeviceId: __controlsFunctions.requestMediaDeviceId,\n mediaDeviceIds,\n }),\n ),\n );\n\n const setMediaDeviceIdComposed = useCallback(\n (deviceId: AudioDeviceId) => {\n requestMediaDeviceId(deviceId, type);\n },\n [requestMediaDeviceId, type],\n );\n\n const handleValueChange = useCallback(\n (value: AudioDeviceId) => {\n if (props.onValueChange) {\n props.onValueChange(value);\n }\n setMediaDeviceIdComposed(value);\n },\n [props.onValueChange, setMediaDeviceIdComposed],\n );\n\n return (\n <Presence present={forceMount || isSupported}>\n <SelectPrimitive.SelectRoot\n disabled={type === \"audioinput\" ? !audio : !video}\n {...controlsProps}\n value={mediaDeviceIds[type] ?? undefined}\n onValueChange={handleValueChange}\n data-livepeer-source-select=\"\"\n data-type={type}\n data-visible={String(isSupported)}\n >\n {children(mediaDevices)}\n </SelectPrimitive.SelectRoot>\n </Presence>\n );\n};\n\nSourceSelect.displayName = SOURCE_SELECT_NAME;\n\nexport { SourceSelect };\nexport type { SourceSelectProps };\n","\"use client\";\n\nimport * as SelectPrimitive from \"@radix-ui/react-select\";\n\nconst SelectRoot = SelectPrimitive.Root;\nconst SelectTrigger = SelectPrimitive.SelectTrigger;\nconst SelectValue = SelectPrimitive.SelectValue;\nconst SelectIcon = SelectPrimitive.SelectIcon;\nconst SelectPortal = SelectPrimitive.SelectPortal;\nconst SelectContent = SelectPrimitive.SelectContent;\nconst SelectViewport = SelectPrimitive.SelectViewport;\nconst SelectGroup = SelectPrimitive.SelectGroup;\nconst SelectLabel = SelectPrimitive.SelectLabel;\nconst SelectItem = SelectPrimitive.SelectItem;\nconst SelectItemText = SelectPrimitive.SelectItemText;\nconst SelectItemIndicator = SelectPrimitive.SelectItemIndicator;\nconst SelectScrollUpButton = SelectPrimitive.SelectScrollUpButton;\nconst SelectScrollDownButton = SelectPrimitive.SelectScrollDownButton;\nconst SelectSeparator = SelectPrimitive.SelectSeparator;\nconst SelectArrow = SelectPrimitive.SelectArrow;\n\ntype SelectProps = SelectPrimitive.SelectProps;\ntype SelectTriggerProps = SelectPrimitive.SelectTriggerProps;\ntype SelectValueProps = SelectPrimitive.SelectValueProps;\ntype SelectIconProps = SelectPrimitive.SelectIconProps;\ntype SelectPortalProps = SelectPrimitive.SelectPortalProps;\ntype SelectContentProps = SelectPrimitive.SelectContentProps;\ntype SelectViewportProps = SelectPrimitive.SelectViewportProps;\ntype SelectGroupProps = SelectPrimitive.SelectGroupProps;\ntype SelectLabelProps = SelectPrimitive.SelectLabelProps;\ntype SelectItemProps = SelectPrimitive.SelectItemProps;\ntype SelectItemTextProps = SelectPrimitive.SelectItemTextProps;\ntype SelectItemIndicatorProps = SelectPrimitive.SelectItemIndicatorProps;\ntype SelectScrollUpButtonProps = SelectPrimitive.SelectScrollUpButtonProps;\ntype SelectScrollDownButtonProps = SelectPrimitive.SelectScrollDownButtonProps;\ntype SelectSeparatorProps = SelectPrimitive.SelectSeparatorProps;\ntype SelectArrowProps = SelectPrimitive.SelectArrowProps;\n\nexport {\n SelectArrow,\n SelectContent,\n SelectGroup,\n SelectIcon,\n SelectItem,\n SelectItemIndicator,\n SelectItemText,\n SelectLabel,\n SelectPortal,\n SelectRoot,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n SelectViewport,\n};\n\nexport type {\n SelectArrowProps,\n SelectContentProps,\n SelectGroupProps,\n SelectIconProps,\n SelectItemIndicatorProps,\n SelectItemProps,\n SelectItemTextProps,\n SelectLabelProps,\n SelectPortalProps,\n SelectProps,\n SelectScrollDownButtonProps,\n SelectScrollUpButtonProps,\n SelectSeparatorProps,\n SelectTriggerProps,\n SelectValueProps,\n SelectViewportProps,\n};\n","\"use client\";\n\nimport type { BroadcastStatus } from \"@livepeer/core-web/broadcast\";\nimport { Presence } from \"@radix-ui/react-presence\";\nimport React, { useMemo } from \"react\";\nimport { useStore } from \"zustand\";\nimport { type BroadcastScopedProps, useBroadcastContext } from \"../broadcast\";\nimport * as Radix from \"../shared/primitive\";\n\nconst STATUS_INDICATOR_NAME = \"StatusIndicator\";\n\ntype StatusIndicatorElement = React.ElementRef<typeof Radix.Primitive.button>;\n\ninterface StatusIndicatorProps\n extends Radix.ComponentPropsWithoutRef<typeof Radix.Primitive.button> {\n /**\n * Used to force mounting when more control is needed. Useful when\n * controlling animation with React animation libraries.\n */\n forceMount?: true;\n /**\n * The matcher used to determine whether the element should be shown, given the broadcast `status` state.\n *\n * Pending indicates that the stream is currently negotiating WebRTC connection with the server.\n *\n * Required.\n */\n matcher: BroadcastStatus | ((status: BroadcastStatus) => boolean);\n}\n\nconst StatusIndicator = React.forwardRef<\n StatusIndicatorElement,\n StatusIndicatorProps\n>((props: BroadcastScopedProps<StatusIndicatorProps>, forwardedRef) => {\n const {\n __scopeBroadcast,\n forceMount,\n matcher = true,\n ...statusIndicatorProps\n } = props;\n\n const broadcastContext = useBroadcastContext(\n STATUS_INDICATOR_NAME,\n __scopeBroadcast,\n );\n\n const status = useStore(broadcastContext.store, ({ status }) => status);\n\n const isPresent = useMemo(\n () =>\n typeof matcher === \"function\" ? matcher(status) : matcher === status,\n [matcher, status],\n );\n\n return (\n <Presence present={forceMount || isPresent}>\n <Radix.Primitive.span\n aria-label={status}\n {...statusIndicatorProps}\n ref={forwardedRef}\n data-livepeer-controls-status-indicator=\"\"\n data-status={String(status)}\n data-visible={String(isPresent)}\n />\n </Presence>\n );\n});\n\nStatusIndicator.displayName = STATUS_INDICATOR_NAME;\n\nexport { StatusIndicator };\nexport type { StatusIndicatorProps };\n","\"use client\";\n\nimport { addBroadcastEventListeners } from \"@livepeer/core-web/broadcast\";\nimport { addEventListeners } from \"@livepeer/core-web/browser\";\nimport { useComposedRefs } from \"@radix-ui/react-compose-refs\";\nimport React, { useEffect } from \"react\";\nimport { useStore } from \"zustand\";\nimport { type MediaScopedProps, useMediaContext } from \"../shared/context\";\nimport * as Radix from \"../shared/primitive\";\nimport { type BroadcastScopedProps, useBroadcastContext } from \"./context\";\n\nconst VIDEO_NAME = \"Video\";\n\ntype OmittedProps = \"src\" | \"poster\";\n\ntype VideoElement = React.ElementRef<typeof Radix.Primitive.video>;\n\ninterface VideoProps\n extends Omit<\n Radix.ComponentPropsWithoutRef<typeof Radix.Primitive.video>,\n OmittedProps\n > {\n /**\n * Whether the element should request permissions for the video/audio\n * input from the user. This defaults to `true`, which means on component mount,\n * the user will receive a permissions request asking for access to their microphone\n * and camera.\n *\n * This can be used as a controlled input to determine when this request should be made,\n * and toggled on after mount.\n */\n // enableUserMedia?: boolean;\n}\n\nconst Video = React.forwardRef<VideoElement, VideoProps>(\n (props: MediaScopedProps<BroadcastScopedProps<VideoProps>>, forwardedRef) => {\n const {\n __scopeMedia,\n __scopeBroadcast,\n style,\n muted = true,\n ...broadcastProps\n } = props;\n\n const context = useMediaContext(VIDEO_NAME, __scopeMedia);\n const broadcastContext = useBroadcastContext(VIDEO_NAME, __scopeBroadcast);\n\n const ref = React.useRef<VideoElement | null>(null);\n const composedRefs = useComposedRefs(forwardedRef, ref);\n\n const isEnabled = useStore(\n broadcastContext.store,\n ({ enabled }) => enabled,\n );\n\n useEffect(() => {\n if (ref.current) {\n const { destroy } = addEventListeners(ref.current, context.store);\n\n return destroy;\n }\n }, [context?.store]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: context\n useEffect(() => {\n if (ref.current) {\n const { destroy } = addBroadcastEventListeners(\n ref.current,\n broadcastContext.store,\n context.store,\n );\n\n return destroy;\n }\n }, []);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: context\n React.useEffect(() => {\n context.store.getState().__controlsFunctions.setMounted();\n }, []);\n\n return (\n <Radix.Primitive.video\n playsInline\n muted={muted}\n {...broadcastProps}\n ref={composedRefs}\n data-livepeer-video=\"\"\n data-enabled={Boolean(isEnabled)}\n style={{\n ...style,\n // ensures video expands in ratio\n position: \"absolute\",\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n }}\n />\n );\n },\n);\n\nVideo.displayName = VIDEO_NAME;\n\nexport { Video };\nexport type { VideoProps };\n","\"use client\";\n\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\nimport { Presence } from \"@radix-ui/react-presence\";\nimport React, { useMemo } from \"react\";\nimport { useStore } from \"zustand\";\nimport { useShallow } from \"zustand/react/shallow\";\nimport * as Radix from \"../shared/primitive\";\nimport { noPropagate } from \"../shared/utils\";\nimport { type BroadcastScopedProps, useBroadcastContext } from \"./context\";\n\n/**\n * VideoEnabledTrigger\n */\n\nconst VIDEO_ENABLED_TRIGGER_NAME = \"VideoEnabledTrigger\";\n\ntype VideoEnabledTriggerElement = React.ElementRef<\n typeof Radix.Primitive.button\n>;\n\ninterface VideoEnabledTriggerProps\n extends Radix.ComponentPropsWithoutRef<typeof Radix.Primitive.button> {}\n\nconst VideoEnabledTrigger = React.forwardRef<\n VideoEnabledTriggerElement,\n VideoEnabledTriggerProps\n>((props: BroadcastScopedProps<VideoEnabledTriggerProps>, forwardedRef) => {\n const { __scopeBroadcast, ...videoEnabledProps } = props;\n\n const broadcastContext = useBroadcastContext(\n VIDEO_ENABLED_TRIGGER_NAME,\n __scopeBroadcast,\n );\n\n const { video, title, toggleVideo } = useStore(\n broadcastContext.store,\n useShallow(({ video, aria, __controlsFunctions }) => ({\n video,\n title: aria.videoTrigger,\n toggleVideo: __controlsFunctions.toggleVideo,\n })),\n );\n\n return (\n <Radix.Primitive.button\n type=\"button\"\n aria-pressed={video}\n aria-label={title}\n title={title}\n {...videoEnabledProps}\n onClick={composeEventHandlers(props.onClick, noPropagate(toggleVideo))}\n ref={forwardedRef}\n data-livepeer-controls-video-enabled-trigger=\"\"\n data-enabled={String(video)}\n />\n );\n});\n\nVideoEnabledTrigger.displayName = VIDEO_ENABLED_TRIGGER_NAME;\n\n/**\n * VideoEnabledIndicator\n */\n\nconst VIDEO_ENABLED_INDICATOR_NAME = \"VideoEnabledIndicator\";\n\ntype VideoEnabledIndicatorElement = React.ElementRef<\n typeof Radix.Primitive.div\n>;\n\ninterface VideoEnabledIndicatorProps\n extends Radix.ComponentPropsWithoutRef<typeof Radix.Primitive.div> {\n /**\n * Used to force mounting when more control is needed. Useful when\n * controlling animation with React animation libraries.\n */\n forceMount?: true;\n /**\n * The matcher used to determine whether the element should be shown, given the `video` state.\n * Defaults to `true`, which means \"shown when broadcast video is enabled\".\n */\n matcher?: boolean | ((state: boolean) => boolean);\n}\n\nconst VideoEnabledIndicator = React.forwardRef<\n VideoEnabledIndicatorElement,\n VideoEnabledIndicatorProps\n>((props: BroadcastScopedProps<VideoEnabledIndicatorProps>, forwardedRef) => {\n const {\n __scopeBroadcast,\n forceMount,\n matcher = true,\n ...videoIndicatorProps\n } = props;\n\n const broadcastContext = useBroadcastContext(\n VIDEO_ENABLED_INDICATOR_NAME,\n __scopeBroadcast,\n );\n\n const video = useStore(broadcastContext.store, ({ video }) => video);\n\n const isPresent = useMemo(\n () => (typeof matcher === \"boolean\" ? matcher === video : matcher(video)),\n [video, matcher],\n );\n\n return (\n <Presence present={forceMount || isPresent}>\n <Radix.Primitive.div\n {...videoIndicatorProps}\n ref={forwardedRef}\n data-livepeer-controls-video-enabled-indicator=\"\"\n data-enabled={String(video)}\n />\n </Presence>\n );\n});\n\nVideoEnabledIndicator.displayName = VIDEO_ENABLED_INDICATOR_NAME;\n\nexport { VideoEnabledIndicator, VideoEnabledTrigger };\nexport type { VideoEnabledIndicatorProps, VideoEnabledTriggerProps };\n","\"use client\";\n\nimport * as RadixAspectRatio from \"@radix-ui/react-aspect-ratio\";\nimport React from \"react\";\nimport { useStore } from \"zustand\";\nimport { useShallow } from \"zustand/react/shallow\";\nimport { type MediaScopedProps, useMediaContext } from \"./context\";\nimport * as Radix from \"./primitive\";\n\nconst CONTAINER_NAME = \"Container\";\n\ntype ContainerElement = React.ElementRef<typeof Radix.Primitive.div>;\ntype ContainerProps = Radix.ComponentPropsWithoutRef<\n typeof Radix.Primitive.div\n>;\n\nconst Container = React.memo(\n React.forwardRef<ContainerElement, MediaScopedProps<ContainerProps>>(\n (props, forwardedRef) => {\n const { __scopeMedia, ...aspectRatioProps } = props;\n\n const context = useMediaContext(CONTAINER_NAME, __scopeMedia);\n\n const {\n aspectRatio,\n fullscreen,\n playing,\n canPlay,\n rate,\n error,\n live,\n hasPlayed,\n hidden,\n pictureInPicture,\n loading,\n videoQuality,\n } = useStore(\n context.store,\n useShallow(\n ({\n __initialProps,\n fullscreen,\n playing,\n canPlay,\n playbackRate,\n error,\n live,\n hasPlayed,\n hidden,\n pictureInPicture,\n loading,\n videoQuality,\n }) => ({\n aspectRatio: __initialProps.aspectRatio,\n fullscreen,\n playing,\n canPlay,\n error: Boolean(error),\n rate:\n playbackRate === \"constant\"\n ? \"constant\"\n : playbackRate > 1\n ? \"fast\"\n : playbackRate < 1\n ? \"slow\"\n : \"normal\",\n live,\n hasPlayed,\n hidden,\n pictureInPicture,\n loading,\n videoQuality,\n }),\n ),\n );\n\n return aspectRatio ? (\n <RadixAspectRatio.Root\n ratio={aspectRatio}\n {...aspectRatioProps}\n ref={forwardedRef}\n data-livepeer-aspect-ratio=\"\"\n data-fullscreen={String(fullscreen)}\n data-playing={String(playing)}\n data-can-play={String(canPlay)}\n data-playback-rate={rate}\n data-error={String(error)}\n data-loading={String(loading)}\n data-live={String(live)}\n data-has-played={String(hasPlayed)}\n data-controls-hidden={String(hidden)}\n data-picture-in-picture={String(pictureInPicture)}\n data-video-quality={String(videoQuality)}\n />\n ) : (\n <Radix.Primitive.div\n {...aspectRatioProps}\n ref={forwardedRef}\n data-livepeer-wrapper=\"\"\n data-fullscreen={String(fullscreen)}\n data-playing={String(playing)}\n data-can-play={String(canPlay)}\n data-playback-rate={rate}\n data-error={String(error)}\n data-loading={String(loading)}\n data-live={String(live)}\n data-has-played={String(hasPlayed)}\n data-controls-hidden={String(hidden)}\n data-picture-in-picture={String(pictureInPicture)}\n data-video-quality={String(videoQuality)}\n />\n );\n },\n ),\n);\n\nContainer.displayName = CONTAINER_NAME;\n\nexport { Container, type ContainerProps };\n","\"use client\";\n\nimport type { PlaybackError } from \"@livepeer/core/media\";\nimport { Presence } from \"@radix-ui/react-presence\";\nimport React, { useMemo } from \"react\";\nimport { useStore } from \"zustand\";\nimport { type MediaScopedProps, useMediaContext } from \"./context