bobinejs 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../lib/utils.ts","../../../components/ui/media-icons.tsx","../../../components/ui/media.tsx","../../../lib/media-store.ts","../../../components/ui/video-player.tsx","../../../components/ui/audio-player.tsx"],"sourcesContent":["import { clsx, type ClassValue } from 'clsx'\r\nimport { extendTailwindMerge } from 'tailwind-merge'\r\n\r\n// the named type scale in globals.css. Unregistered, `text-meta` reads as a\r\n// colour and is dropped when a `text-text-*` colour follows it.\r\nconst twMerge = extendTailwindMerge({\r\n extend: { theme: { text: ['display', 'title', 'body', 'action', 'meta', 'code'] } },\r\n})\r\n\r\nexport function cn(...inputs: ClassValue[]) {\r\n return twMerge(clsx(inputs))\r\n}\r\n","import * as React from \"react\"\n\nexport type MediaIconProps = React.ComponentProps<\"svg\">\n\nconst base = {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: 16,\n height: 16,\n viewBox: \"0 0 24 24\",\n \"aria-hidden\": true,\n} as const\n\n/**\n * Optically centred: the filled area's centroid, not the bounding box, sits\n * on the viewBox centre (x = 12), measured from the rendered shape. A\n * box-centred triangle looks shifted right, because its weight is in the flat\n * left side.\n */\nexport function PlayIcon(props: MediaIconProps) {\n return (\n <svg {...base} fill=\"currentColor\" {...props}>\n <path d=\"M6.87 4.9v14.2a1.5 1.5 0 0 0 2.31 1.26l11.07-7.1a1.5 1.5 0 0 0 0-2.52L9.18 3.64A1.5 1.5 0 0 0 6.87 4.9Z\" />\n </svg>\n )\n}\n\nexport function PauseIcon(props: MediaIconProps) {\n return (\n <svg {...base} fill=\"currentColor\" {...props}>\n <rect x=\"5\" y=\"3.5\" width=\"5\" height=\"17\" rx=\"1.6\" />\n <rect x=\"14\" y=\"3.5\" width=\"5\" height=\"17\" rx=\"1.6\" />\n </svg>\n )\n}\n\nexport function VolumeIcon({ muted, ...props }: MediaIconProps & { muted?: boolean }) {\n const fade = \"origin-[11px_12px] transition-[opacity,transform] duration-200 ease-media motion-reduce:transition-none\"\n return (\n <svg {...base} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" {...props}>\n <path\n d=\"M11 5.1v13.8a1 1 0 0 1-1.63.77L5.5 16.5h-2A1.5 1.5 0 0 1 2 15V9a1.5 1.5 0 0 1 1.5-1.5h2l3.87-3.17A1 1 0 0 1 11 5.1Z\"\n fill=\"currentColor\"\n stroke=\"none\"\n />\n <g className={fade} style={{ opacity: muted ? 0 : 1, transform: muted ? \"scale(0.6)\" : \"none\" }}>\n <path d=\"M15 9.5a3.5 3.5 0 0 1 0 5\" />\n <path d=\"M17.8 6.7a7.5 7.5 0 0 1 0 10.6\" />\n </g>\n <g className={fade} style={{ opacity: muted ? 1 : 0, transform: muted ? \"none\" : \"scale(0.6)\" }}>\n <path d=\"m16 9.5 5 5\" />\n <path d=\"m21 9.5-5 5\" />\n </g>\n </svg>\n )\n}\n\nexport function EnterFullscreenIcon(props: MediaIconProps) {\n return (\n <svg {...base} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" {...props}>\n <path d=\"M4 9V6a2 2 0 0 1 2-2h3M15 4h3a2 2 0 0 1 2 2v3M20 15v3a2 2 0 0 1-2 2h-3M9 20H6a2 2 0 0 1-2-2v-3\" />\n </svg>\n )\n}\n\nexport function ExitFullscreenIcon(props: MediaIconProps) {\n return (\n <svg {...base} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" {...props}>\n <path d=\"M9 4v3a2 2 0 0 1-2 2H4M20 9h-3a2 2 0 0 1-2-2V4M15 20v-3a2 2 0 0 1 2-2h3M4 15h3a2 2 0 0 1 2 2v3\" />\n </svg>\n )\n}\n\nexport function PictureInPictureIcon(props: MediaIconProps) {\n return (\n <svg {...base} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" {...props}>\n <rect x=\"2.5\" y=\"4.5\" width=\"19\" height=\"15\" rx=\"2.5\" />\n <rect x=\"12\" y=\"11.5\" width=\"6.5\" height=\"5\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" />\n </svg>\n )\n}\n\nconst captionLetters = \"M10.5 10.2a2.2 2.2 0 1 0 0 3.6M17 10.2a2.2 2.2 0 1 0 0 3.6\"\n\n/** Outlined when off; filled with knocked-out letters when on. */\nexport function CaptionsIcon({ active, ...props }: MediaIconProps & { active?: boolean }) {\n const maskId = `cc${React.useId().replace(/[^\\w-]/g, \"\")}`\n return (\n <svg {...base} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" {...props}>\n <mask id={maskId}>\n <rect width=\"24\" height=\"24\" fill=\"white\" />\n <path d={captionLetters} stroke=\"black\" />\n </mask>\n <rect\n x=\"2.5\"\n y=\"4.5\"\n width=\"19\"\n height=\"15\"\n rx=\"3\"\n fill=\"currentColor\"\n mask={`url(#${maskId})`}\n className=\"transition-opacity duration-200 ease-media motion-reduce:transition-none\"\n style={{ opacity: active ? 1 : 0 }}\n />\n <rect x=\"2.5\" y=\"4.5\" width=\"19\" height=\"15\" rx=\"3\" />\n <path\n d={captionLetters}\n className=\"transition-opacity duration-200 ease-media motion-reduce:transition-none\"\n style={{ opacity: active ? 0 : 1 }}\n />\n </svg>\n )\n}\n\nexport function SettingsIcon(props: MediaIconProps) {\n return (\n <svg {...base} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" {...props}>\n <path d=\"M4 7h9M17 7h3M4 17h3M11 17h9\" />\n <circle cx=\"15\" cy=\"7\" r=\"2\" />\n <circle cx=\"9\" cy=\"17\" r=\"2\" />\n </svg>\n )\n}\n\nexport function RetryIcon(props: MediaIconProps) {\n return (\n <svg {...base} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" {...props}>\n <path d=\"M3.5 12a8.5 8.5 0 1 0 2.6-6.1\" />\n <path d=\"M3.5 4v4.5H8\" />\n </svg>\n )\n}\n\nexport function CheckIcon(props: MediaIconProps) {\n return (\n <svg {...base} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.25\" strokeLinecap=\"round\" strokeLinejoin=\"round\" {...props}>\n <path d=\"m5 12.5 4.5 4.5L19 7.5\" />\n </svg>\n )\n}\n\nexport function SeekIcon({ direction, ...props }: MediaIconProps & { direction: \"back\" | \"forward\" }) {\n return (\n <svg {...base} fill=\"currentColor\" {...props} style={{ transform: direction === \"back\" ? \"scaleX(-1)\" : undefined, ...props.style }}>\n <path d=\"M3 6.7v10.6a1 1 0 0 0 1.55.83L12 13.2v4.1a1 1 0 0 0 1.55.83l7.9-5.3a1 1 0 0 0 0-1.66l-7.9-5.3A1 1 0 0 0 12 6.7v4.1L4.55 5.87A1 1 0 0 0 3 6.7Z\" />\n </svg>\n )\n}\n","\"use client\"\r\n\r\nimport * as React from \"react\"\r\n\r\nimport {\r\n createMediaStore,\r\n describeMediaError,\r\n formatTime,\r\n type MediaInfo,\r\n type MediaState,\r\n type MediaStore,\r\n} from \"@/lib/media-store\"\r\nimport { cn } from \"@/lib/utils\"\r\nimport {\r\n CaptionsIcon,\r\n CheckIcon,\r\n EnterFullscreenIcon,\r\n ExitFullscreenIcon,\r\n PauseIcon,\r\n PictureInPictureIcon,\r\n PlayIcon,\r\n RetryIcon,\r\n SeekIcon,\r\n SettingsIcon,\r\n VolumeIcon,\r\n} from \"@/components/ui/media-icons\"\r\n\r\nconst MediaContext = React.createContext<MediaStore | null>(null)\r\n\r\n/** Root options the parts read: whether to show tooltips and shortcut hints. */\r\nconst MediaOptionsContext = React.createContext({ tooltips: false, hotkeys: false })\r\n\r\nfunction useMediaStore() {\r\n const store = React.useContext(MediaContext)\r\n if (!store) throw new Error(\"Media components must be rendered inside <MediaRoot>.\")\r\n return store\r\n}\r\n\r\nfunction useMedia<T>(selector: (state: MediaState) => T): T {\r\n const store = useMediaStore()\r\n return React.useSyncExternalStore(\r\n store.subscribe,\r\n () => selector(store.getState()),\r\n () => selector(store.getServerState())\r\n )\r\n}\r\n\r\nfunction useComposedRefs<T>(...refs: (React.Ref<T> | undefined)[]) {\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n return React.useCallback(composeRefs(...refs), refs)\r\n}\r\n\r\nfunction composeRefs<T>(...refs: (React.Ref<T> | undefined)[]) {\r\n return (node: T | null) => {\r\n const cleanups = refs.map((ref) => {\r\n if (typeof ref === \"function\") return ref(node)\r\n if (ref) (ref as React.RefObject<T | null>).current = node\r\n })\r\n return () => {\r\n cleanups.forEach((cleanup, i) => {\r\n const ref = refs[i]\r\n if (typeof cleanup === \"function\") cleanup()\r\n else if (typeof ref === \"function\") ref(null)\r\n else if (ref) (ref as React.RefObject<T | null>).current = null\r\n })\r\n }\r\n }\r\n}\r\n\r\nconst clamp = (value: number) => Math.min(Math.max(value, 0), 1)\r\n\r\n/* ---------------------------------- Root ---------------------------------- */\r\n\r\ntype MediaRootProps = React.ComponentProps<\"div\"> & {\r\n /** Milliseconds of pointer inactivity before controls hide during playback. */\r\n idleTimeout?: number\r\n /** Remember volume, mute and speed in localStorage under this key. */\r\n persistKey?: string\r\n /** Title, artist and artwork for OS media controls (Media Session API). */\r\n metadata?: MediaInfo\r\n /** Enable keyboard shortcuts while focus is inside the player. */\r\n hotkeys?: boolean\r\n /** Small labels over the buttons on hover and keyboard focus, with their shortcut. */\r\n tooltips?: boolean\r\n}\r\n\r\n// Keep the controls awake while the pointer rests on them or a menu is open.\r\nconst SETTINGS_OPEN = \"[data-slot='media-settings'][data-open]\"\r\n\r\n/**\r\n * Hovering the control bar keeps it awake, but only on a device that can\r\n * hover. On touch, `:hover` sticks to the last tapped control, so the bar would\r\n * never sleep after a tap.\r\n */\r\nconst holdAwakeSelector = () =>\r\n window.matchMedia(\"(hover: hover)\").matches ? `[data-slot='media-controls']:hover, ${SETTINGS_OPEN}` : SETTINGS_OPEN\r\n\r\nfunction MediaRoot({\r\n className,\r\n idleTimeout = 2000,\r\n persistKey,\r\n metadata,\r\n hotkeys = true,\r\n tooltips = false,\r\n onPointerMove,\r\n onPointerDown,\r\n onPointerLeave,\r\n onKeyDown,\r\n ref,\r\n ...props\r\n}: MediaRootProps) {\r\n const [store] = React.useState(() => createMediaStore({ persistKey }))\r\n const [active, setActive] = React.useState(false)\r\n const timer = React.useRef<ReturnType<typeof setTimeout>>(undefined)\r\n const rootEl = React.useRef<HTMLDivElement>(null)\r\n\r\n const state = React.useSyncExternalStore(store.subscribe, store.getState, store.getServerState)\r\n const rootRef = useComposedRefs(ref, rootEl, store.setContainer)\r\n const options = React.useMemo(() => ({ tooltips, hotkeys }), [tooltips, hotkeys])\r\n\r\n React.useEffect(() => () => clearTimeout(timer.current), [])\r\n\r\n const { title, artist, album, artwork } = metadata ?? {}\r\n const artworkKey = JSON.stringify(artwork ?? null)\r\n React.useEffect(() => {\r\n store.setMetadata(title || artist || album ? { title, artist, album, artwork: JSON.parse(artworkKey) ?? undefined } : null)\r\n }, [store, title, artist, album, artworkKey])\r\n\r\n const wake = () => {\r\n setActive(true)\r\n clearTimeout(timer.current)\r\n timer.current = setTimeout(function sleep() {\r\n if (rootEl.current?.querySelector(holdAwakeSelector())) {\r\n timer.current = setTimeout(sleep, idleTimeout)\r\n return\r\n }\r\n setActive(false)\r\n }, idleTimeout)\r\n }\r\n\r\n const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\r\n onKeyDown?.(event)\r\n if (!hotkeys || event.defaultPrevented || event.metaKey || event.ctrlKey || event.altKey) return\r\n const target = event.target as HTMLElement\r\n if (target.closest(\"input, textarea, select, [contenteditable='true'], [data-slot='media-settings']\")) return\r\n const onButton = target.tagName === \"BUTTON\"\r\n\r\n const key = event.key.length === 1 ? event.key.toLowerCase() : event.key\r\n const keymap: Record<string, () => void> = {\r\n \" \": () => store.toggle(),\r\n k: () => store.toggle(),\r\n j: () => store.seekBy(-10),\r\n l: () => store.seekBy(10),\r\n ArrowLeft: () => store.seekBy(-5),\r\n ArrowRight: () => store.seekBy(5),\r\n ArrowUp: () => store.setVolume(store.getState().volume + 0.1),\r\n ArrowDown: () => store.setVolume(store.getState().volume - 0.1),\r\n m: () => store.toggleMute(),\r\n f: () => store.toggleFullscreen(),\r\n c: () => store.toggleCaptions(),\r\n i: () => store.togglePictureInPicture(),\r\n Home: () => store.seek(0),\r\n End: () => store.seekToRatio(1),\r\n }\r\n for (let digit = 0; digit <= 9; digit++) {\r\n keymap[String(digit)] = () => store.seekToRatio(digit / 10)\r\n }\r\n const action = keymap[key]\r\n if (!action || (key === \" \" && onButton)) return\r\n event.preventDefault()\r\n action()\r\n wake()\r\n }\r\n\r\n const idle = !active && !state.paused\r\n\r\n return (\r\n <MediaContext.Provider value={store}>\r\n <MediaOptionsContext.Provider value={options}>\r\n <div\r\n ref={rootRef}\r\n data-slot=\"media-root\"\r\n data-paused={state.paused || undefined}\r\n data-playing={!state.paused || undefined}\r\n data-started={state.started || undefined}\r\n data-waiting={state.waiting || undefined}\r\n data-fullscreen={state.fullscreen || undefined}\r\n data-error={state.error ? \"\" : undefined}\r\n data-captions={state.textTrack !== -1 && state.cues.length > 0 ? \"\" : undefined}\r\n data-idle={idle || undefined}\r\n data-active={active || undefined}\r\n className={cn(\r\n /* no gray tap flash, no long-press callout over the video, and no\r\n double-tap-zoom wait before a tap registers */\r\n \"group/media relative isolate touch-manipulation [-webkit-tap-highlight-color:transparent] [-webkit-touch-callout:none]\",\r\n idle && \"cursor-none\",\r\n className\r\n )}\r\n onPointerMove={(event) => {\r\n onPointerMove?.(event)\r\n if (event.pointerType === \"mouse\") wake()\r\n }}\r\n onPointerDown={(event) => {\r\n onPointerDown?.(event)\r\n wake()\r\n }}\r\n onPointerLeave={(event) => {\r\n onPointerLeave?.(event)\r\n\r\n /**\r\n * A finger lifting fires pointerleave too, so hiding here on touch\r\n * flashed the controls off after every tap and made the next tap\r\n * only reveal them. On touch the idle timer hides them instead.\r\n */\r\n if (event.pointerType !== \"mouse\") return\r\n clearTimeout(timer.current)\r\n setActive(false)\r\n }}\r\n onKeyDown={handleKeyDown}\r\n {...props}\r\n />\r\n </MediaOptionsContext.Provider>\r\n </MediaContext.Provider>\r\n )\r\n}\r\n\r\n/* --------------------------------- Media ---------------------------------- */\r\n\r\n/**\r\n * Attaches a source the element can't play natively (HLS, DASH, MSE).\r\n * Return a cleanup function to tear the adapter down when `src` changes.\r\n */\r\ntype MediaSourceLoader = (media: HTMLMediaElement, src: string) => void | (() => void)\r\n\r\ntype MediaElementProps<T> = T & {\r\n /** Custom source adapter, e.g. `hlsLoader` from `@/lib/media-hls`. */\r\n loader?: MediaSourceLoader\r\n}\r\n\r\nfunction useMediaElement<E extends HTMLMediaElement>(\r\n ref: React.Ref<E> | undefined,\r\n src: string | undefined,\r\n loader: MediaSourceLoader | undefined\r\n) {\r\n const store = useMediaStore()\r\n const mediaRef = useComposedRefs(ref, store.attach)\r\n\r\n React.useEffect(() => {\r\n const media = store.getMedia()\r\n if (!loader || !src || !media) return\r\n return loader(media, src) ?? undefined\r\n }, [store, loader, src])\r\n\r\n return { mediaRef }\r\n}\r\n\r\ntype MediaVideoProps = MediaElementProps<React.ComponentProps<\"video\">>\r\n\r\nfunction MediaVideo({ className, ref, src, loader, ...props }: MediaVideoProps) {\r\n const media = useMediaElement(ref, typeof src === \"string\" ? src : undefined, loader)\r\n return (\r\n <video\r\n ref={media.mediaRef}\r\n data-slot=\"media-video\"\r\n playsInline\r\n preload=\"metadata\"\r\n {...props}\r\n src={loader ? undefined : src}\r\n controls={false}\r\n className={cn(\"block\", className)}\r\n />\r\n )\r\n}\r\n\r\ntype MediaAudioProps = MediaElementProps<React.ComponentProps<\"audio\">>\r\n\r\nfunction MediaAudio({ ref, src, loader, ...props }: MediaAudioProps) {\r\n const media = useMediaElement(ref, typeof src === \"string\" ? src : undefined, loader)\r\n return (\r\n <audio\r\n ref={media.mediaRef}\r\n data-slot=\"media-audio\"\r\n preload=\"metadata\"\r\n {...props}\r\n src={loader ? undefined : src}\r\n controls={false}\r\n />\r\n )\r\n}\r\n\r\n/* -------------------------------- Controls -------------------------------- */\r\n\r\n/* on touch, an invisible 36px hit area around the 28px button; looks the same */\r\nconst buttonClass =\r\n \"pointer-coarse:after:absolute pointer-coarse:after:-inset-1 pointer-coarse:after:content-[''] group/media-button relative inline-grid size-7 shrink-0 cursor-pointer place-items-center rounded-md outline-none transition-[background-color,scale,opacity] duration-150 ease-media hover:bg-current/15 focus-visible:ring-2 focus-visible:ring-current/60 motion-safe:enabled:active:scale-[0.96] disabled:pointer-events-none disabled:opacity-50 motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0\"\r\n\r\ntype MediaTooltipAlign = \"start\" | \"center\" | \"end\"\r\n\r\n/**\r\n * The label over a control. Decorative: the control already carries the same\r\n * text as its accessible name. Hover shows it after a short delay and hides it\r\n * at once; keyboard focus shows it straight away. Touch never shows it, since\r\n * the hover variants only apply to pointers that can hover.\r\n */\r\nfunction MediaTooltip({ label, shortcut, align = \"center\" }: { label: React.ReactNode; shortcut?: string; align?: MediaTooltipAlign }) {\r\n const { tooltips, hotkeys } = React.useContext(MediaOptionsContext)\r\n if (!tooltips || !label) return null\r\n return (\r\n <span\r\n aria-hidden=\"true\"\r\n data-slot=\"media-tooltip\"\r\n className={cn(\r\n \"pointer-events-none absolute bottom-full mb-2 flex items-center gap-1.5 rounded-md bg-foreground px-1.5 py-0.5 text-[11px] leading-4 font-medium whitespace-nowrap text-background opacity-0\",\r\n \"transition-[opacity,translate] duration-150 ease-media motion-safe:translate-y-0.5 motion-reduce:transition-none\",\r\n \"group-hover/media-button:translate-y-0 group-hover/media-button:opacity-100 group-hover/media-button:delay-300\",\r\n \"group-focus-visible/media-button:translate-y-0 group-focus-visible/media-button:opacity-100\",\r\n \"group-aria-expanded/media-button:hidden\",\r\n align === \"start\" && \"left-0\",\r\n align === \"center\" && \"left-1/2 -translate-x-1/2\",\r\n align === \"end\" && \"right-0\"\r\n )}\r\n >\r\n {label}\r\n {hotkeys && shortcut && <kbd className=\"font-mono opacity-60\">{shortcut}</kbd>}\r\n </span>\r\n )\r\n}\r\n\r\ntype MediaButtonProps = React.ComponentProps<\"button\"> & {\r\n /** Tooltip text, shown when the root has `tooltips`. `false` hides it. */\r\n tooltip?: React.ReactNode\r\n /** Shortcut shown beside the tooltip when the root has `hotkeys`. */\r\n shortcut?: string\r\n /** Pin the tooltip to an edge for buttons at either end of a bar. */\r\n tooltipAlign?: MediaTooltipAlign\r\n}\r\n\r\nfunction MediaButton({ className, type = \"button\", tooltip, shortcut, tooltipAlign, children, ...props }: MediaButtonProps) {\r\n return (\r\n <button type={type} data-slot=\"media-button\" className={cn(buttonClass, className)} {...props}>\r\n {children}\r\n <MediaTooltip label={tooltip} shortcut={shortcut} align={tooltipAlign} />\r\n </button>\r\n )\r\n}\r\n\r\nfunction IconSwap({ show, a, b }: { show: \"a\" | \"b\"; a: React.ReactNode; b: React.ReactNode }) {\r\n const layer =\r\n \"col-start-1 row-start-1 grid place-items-center transition-[opacity,scale,filter] duration-200 ease-media motion-reduce:transition-none\"\r\n return (\r\n <span className=\"grid\">\r\n <span className={cn(layer, show === \"a\" ? \"opacity-100\" : \"scale-50 opacity-0 blur-[2px]\")}>{a}</span>\r\n <span className={cn(layer, show === \"b\" ? \"opacity-100\" : \"scale-50 opacity-0 blur-[2px]\")}>{b}</span>\r\n </span>\r\n )\r\n}\r\n\r\nfunction MediaPlayButton({ onClick, ...props }: MediaButtonProps) {\r\n const store = useMediaStore()\r\n const paused = useMedia((s) => s.paused)\r\n return (\r\n <MediaButton\r\n aria-label={paused ? \"Play\" : \"Pause\"}\r\n tooltip={paused ? \"Play\" : \"Pause\"}\r\n shortcut=\"K\"\r\n onClick={(event) => {\r\n onClick?.(event)\r\n store.toggle()\r\n }}\r\n {...props}\r\n >\r\n <IconSwap show={paused ? \"a\" : \"b\"} a={<PlayIcon />} b={<PauseIcon />} />\r\n </MediaButton>\r\n )\r\n}\r\n\r\nfunction MediaMuteButton({ onClick, ...props }: MediaButtonProps) {\r\n const store = useMediaStore()\r\n const muted = useMedia((s) => s.muted || s.volume === 0)\r\n return (\r\n <MediaButton\r\n aria-label={muted ? \"Unmute\" : \"Mute\"}\r\n tooltip={muted ? \"Unmute\" : \"Mute\"}\r\n shortcut=\"M\"\r\n onClick={(event) => {\r\n onClick?.(event)\r\n store.toggleMute()\r\n }}\r\n {...props}\r\n >\r\n <VolumeIcon muted={muted} />\r\n </MediaButton>\r\n )\r\n}\r\n\r\nfunction MediaFullscreenButton({ onClick, ...props }: MediaButtonProps) {\r\n const store = useMediaStore()\r\n const fullscreen = useMedia((s) => s.fullscreen)\r\n return (\r\n <MediaButton\r\n aria-label={fullscreen ? \"Exit fullscreen\" : \"Enter fullscreen\"}\r\n tooltip={fullscreen ? \"Exit fullscreen\" : \"Fullscreen\"}\r\n shortcut=\"F\"\r\n onClick={(event) => {\r\n onClick?.(event)\r\n store.toggleFullscreen()\r\n }}\r\n {...props}\r\n >\r\n <IconSwap show={fullscreen ? \"b\" : \"a\"} a={<EnterFullscreenIcon />} b={<ExitFullscreenIcon />} />\r\n </MediaButton>\r\n )\r\n}\r\n\r\nconst subscribeNoop = () => () => {}\r\n\r\nfunction MediaPictureInPictureButton({ onClick, ...props }: MediaButtonProps) {\r\n const store = useMediaStore()\r\n const active = useMedia((s) => s.pictureInPicture)\r\n const supported = React.useSyncExternalStore(\r\n subscribeNoop,\r\n () => \"pictureInPictureEnabled\" in document && document.pictureInPictureEnabled,\r\n () => false\r\n )\r\n if (!supported) return null\r\n return (\r\n <MediaButton\r\n aria-label={active ? \"Exit picture in picture\" : \"Enter picture in picture\"}\r\n aria-pressed={active}\r\n tooltip={active ? \"Exit picture in picture\" : \"Picture in picture\"}\r\n shortcut=\"I\"\r\n onClick={(event) => {\r\n onClick?.(event)\r\n store.togglePictureInPicture()\r\n }}\r\n {...props}\r\n >\r\n <PictureInPictureIcon />\r\n </MediaButton>\r\n )\r\n}\r\n\r\ntype MediaPlaybackRateButtonProps = MediaButtonProps & {\r\n /** Speeds a click cycles through, in order. */\r\n rates?: number[]\r\n}\r\n\r\nfunction MediaPlaybackRateButton({\r\n rates = [1, 1.25, 1.5, 2, 0.75],\r\n className,\r\n style,\r\n onClick,\r\n ...props\r\n}: MediaPlaybackRateButtonProps) {\r\n const store = useMediaStore()\r\n const rate = useMedia((s) => s.playbackRate)\r\n\r\n /**\r\n * Sized for the longest label, so cycling 1x to 0.75x never moves the\r\n * controls beside it. The font is mono, so characters measure exactly. A\r\n * variable rather than an inline width, so a w-* class still overrides it.\r\n */\r\n const chars = Math.max(...[...rates, rate].map((r) => `${r}×`.length))\r\n return (\r\n <MediaButton\r\n aria-label={`Playback speed ${rate}x`}\r\n tooltip=\"Playback speed\"\r\n className={cn(\"w-(--media-rate-width) min-w-7 px-1.5 font-mono text-xs tabular-nums\", className)}\r\n style={{ \"--media-rate-width\": `calc(${chars}ch + 0.75rem)`, ...style } as React.CSSProperties}\r\n onClick={(event) => {\r\n onClick?.(event)\r\n const index = rates.indexOf(rate)\r\n store.setPlaybackRate(rates[(index + 1) % rates.length])\r\n }}\r\n {...props}\r\n >\r\n {`${rate}×`}\r\n </MediaButton>\r\n )\r\n}\r\n\r\n/* -------------------------------- Controls -------------------------------- */\r\n\r\n/**\r\n * Bar that reveals while the pointer is active, playback is paused,\r\n * or keyboard focus is inside the player.\r\n */\r\ntype MediaControlsProps = React.ComponentProps<\"div\">\r\n\r\nfunction MediaControls({ className, ...props }: MediaControlsProps) {\r\n return (\r\n <div\r\n data-slot=\"media-controls\"\r\n className={cn(\r\n \"flex items-center transition-[opacity,translate] duration-200 ease-media motion-reduce:transition-none\",\r\n \"pointer-events-none translate-y-2 opacity-0\",\r\n \"group-data-active/media:pointer-events-auto group-data-active/media:translate-y-0 group-data-active/media:opacity-100\",\r\n \"group-has-focus-visible/media:pointer-events-auto group-has-focus-visible/media:translate-y-0 group-has-focus-visible/media:opacity-100\",\r\n \"group-data-paused/media:group-data-started/media:pointer-events-auto group-data-paused/media:group-data-started/media:translate-y-0 group-data-paused/media:group-data-started/media:opacity-100\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\n/* --------------------------------- Surface -------------------------------- */\r\n\r\ntype MediaSurfaceProps = React.ComponentProps<\"button\"> & {\r\n /** Seconds a double-tap on either half seeks by. 0 disables it. */\r\n doubleTapSeek?: number\r\n /** Double-click with a mouse toggles fullscreen. */\r\n doubleClickFullscreen?: boolean\r\n /** Classes for the \"±10s\" bubble shown after a double-tap seek. */\r\n indicatorClassName?: string\r\n}\r\n\r\nconst DOUBLE_TAP_MS = 280\r\n\r\n/**\r\n * Full-bleed click target under the controls. Click toggles playback; on touch,\r\n * the first tap reveals controls and a double-tap on either half seeks.\r\n */\r\nfunction MediaSurface({\r\n className,\r\n doubleTapSeek = 10,\r\n doubleClickFullscreen = true,\r\n indicatorClassName,\r\n onClick,\r\n onDoubleClick,\r\n onPointerDown,\r\n children,\r\n ...props\r\n}: MediaSurfaceProps) {\r\n const store = useMediaStore()\r\n const paused = useMedia((s) => s.paused)\r\n const pointer = React.useRef({ type: \"mouse\", revealOnly: false, lastTap: 0 })\r\n const tapTimer = React.useRef<ReturnType<typeof setTimeout>>(undefined)\r\n const flashTimer = React.useRef<ReturnType<typeof setTimeout>>(undefined)\r\n const [flash, setFlash] = React.useState<{ side: \"back\" | \"forward\"; count: number } | null>(null)\r\n\r\n React.useEffect(\r\n () => () => {\r\n clearTimeout(tapTimer.current)\r\n clearTimeout(flashTimer.current)\r\n },\r\n []\r\n )\r\n\r\n const showFlash = (side: \"back\" | \"forward\") => {\r\n setFlash((prev) => ({ side, count: prev?.side === side ? prev.count + 1 : 1 }))\r\n clearTimeout(flashTimer.current)\r\n flashTimer.current = setTimeout(() => setFlash(null), 650)\r\n }\r\n\r\n const handleTouchTap = (event: React.MouseEvent<HTMLButtonElement>) => {\r\n const now = performance.now()\r\n const { revealOnly, lastTap } = pointer.current\r\n if (doubleTapSeek > 0 && now - lastTap < DOUBLE_TAP_MS) {\r\n clearTimeout(tapTimer.current)\r\n pointer.current.lastTap = now\r\n const rect = event.currentTarget.getBoundingClientRect()\r\n const side = event.clientX - rect.left < rect.width / 2 ? \"back\" : \"forward\"\r\n store.seekBy(side === \"back\" ? -doubleTapSeek : doubleTapSeek)\r\n showFlash(side)\r\n return\r\n }\r\n pointer.current.lastTap = now\r\n if (revealOnly) return\r\n // Wait to see whether this tap becomes a double-tap before toggling.\r\n clearTimeout(tapTimer.current)\r\n tapTimer.current = setTimeout(() => store.toggle(), doubleTapSeek > 0 ? DOUBLE_TAP_MS : 0)\r\n }\r\n\r\n return (\r\n <button\r\n type=\"button\"\r\n data-slot=\"media-surface\"\r\n aria-label={paused ? \"Play\" : \"Pause\"}\r\n className={cn(\"absolute inset-0 cursor-[inherit] outline-none\", className)}\r\n onPointerDown={(event) => {\r\n onPointerDown?.(event)\r\n const root = event.currentTarget.closest(\"[data-slot='media-root']\")\r\n pointer.current.type = event.pointerType\r\n // On touch, the first tap while playing reveals controls instead of pausing.\r\n pointer.current.revealOnly =\r\n event.pointerType !== \"mouse\" && !store.getState().paused && !root?.hasAttribute(\"data-active\")\r\n }}\r\n onClick={(event) => {\r\n onClick?.(event)\r\n // Keyboard activation reports detail 0 and always toggles.\r\n if (event.detail !== 0 && pointer.current.type !== \"mouse\") {\r\n handleTouchTap(event)\r\n return\r\n }\r\n store.toggle()\r\n }}\r\n onDoubleClick={(event) => {\r\n onDoubleClick?.(event)\r\n if (doubleClickFullscreen && pointer.current.type === \"mouse\") store.toggleFullscreen()\r\n }}\r\n {...props}\r\n >\r\n {doubleTapSeek > 0 && (\r\n <span aria-hidden=\"true\" className=\"pointer-events-none absolute inset-0 flex items-center justify-between px-[8%]\">\r\n {([\"back\", \"forward\"] as const).map((side) => (\r\n <span\r\n key={side}\r\n data-show={flash?.side === side || undefined}\r\n className={cn(\r\n \"flex items-center gap-1.5 rounded-full px-3 py-1.5 font-mono text-xs tabular-nums opacity-0 scale-90 transition-[opacity,scale] duration-200 ease-media data-show:scale-100 data-show:opacity-100 motion-reduce:transition-none\",\r\n side === \"forward\" && \"flex-row-reverse\",\r\n indicatorClassName\r\n )}\r\n >\r\n <SeekIcon direction={side} className=\"size-3.5\" />\r\n {`${(flash?.side === side ? flash.count : 1) * doubleTapSeek}s`}\r\n </span>\r\n ))}\r\n </span>\r\n )}\r\n {children}\r\n </button>\r\n )\r\n}\r\n\r\n/* ---------------------------------- Seek ---------------------------------- */\r\n\r\ntype MediaSeekProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\r\n /** Seconds moved per arrow key press. */\r\n step?: number\r\n /** Show a time tooltip while hovering. */\r\n preview?: boolean\r\n previewClassName?: string\r\n}\r\n\r\nfunction MediaSeek({\r\n className,\r\n step = 5,\r\n preview = true,\r\n previewClassName,\r\n ...props\r\n}: MediaSeekProps) {\r\n const store = useMediaStore()\r\n const duration = useMedia((s) => s.duration)\r\n const bufferedEnd = useMedia((s) => s.bufferedEnd)\r\n const currentTime = useMedia((s) => Math.floor(s.currentTime))\r\n const fillRef = React.useRef<HTMLDivElement>(null)\r\n const scrub = React.useRef<{ resume: boolean } | null>(null)\r\n const [hover, setHover] = React.useState<number | null>(null)\r\n\r\n React.useEffect(() => {\r\n let frame = 0\r\n const draw = () => {\r\n cancelAnimationFrame(frame)\r\n const media = store.getMedia()\r\n const { duration, paused } = store.getState()\r\n if (!scrub.current && fillRef.current) {\r\n const progress = duration ? clamp((media?.currentTime ?? 0) / duration) : 0\r\n fillRef.current.style.transform = `scaleX(${progress})`\r\n }\r\n if (!paused) frame = requestAnimationFrame(draw)\r\n }\r\n draw()\r\n const unsubscribe = store.subscribe(draw)\r\n return () => {\r\n unsubscribe()\r\n cancelAnimationFrame(frame)\r\n }\r\n }, [store])\r\n\r\n const ratioFromEvent = (event: React.PointerEvent<HTMLDivElement>) => {\r\n const rect = event.currentTarget.getBoundingClientRect()\r\n return clamp((event.clientX - rect.left) / rect.width)\r\n }\r\n\r\n const seekToRatio = (ratio: number) => {\r\n if (fillRef.current) fillRef.current.style.transform = `scaleX(${ratio})`\r\n store.seek(ratio * store.getState().duration)\r\n }\r\n\r\n const endScrub = (event: React.PointerEvent<HTMLDivElement>) => {\r\n if (!scrub.current) return\r\n if (event.currentTarget.hasPointerCapture(event.pointerId)) {\r\n event.currentTarget.releasePointerCapture(event.pointerId)\r\n }\r\n const { resume } = scrub.current\r\n scrub.current = null\r\n if (resume) store.play()\r\n }\r\n\r\n return (\r\n <div\r\n role=\"slider\"\r\n tabIndex={0}\r\n aria-label=\"Seek\"\r\n aria-valuemin={0}\r\n aria-valuemax={Math.floor(duration)}\r\n aria-valuenow={currentTime}\r\n aria-valuetext={`${formatTime(currentTime)} of ${formatTime(duration)}`}\r\n data-slot=\"media-seek\"\r\n className={cn(\r\n /* on touch, an invisible 44px-tall hit area around the 20px bar */\r\n \"group/seek relative flex h-5 min-w-0 grow cursor-pointer touch-none items-center rounded-full outline-none select-none pointer-coarse:before:absolute pointer-coarse:before:inset-x-0 pointer-coarse:before:-inset-y-3 pointer-coarse:before:content-['']\",\r\n className\r\n )}\r\n onPointerDown={(event) => {\r\n if (event.button !== 0) return\r\n event.currentTarget.setPointerCapture(event.pointerId)\r\n scrub.current = { resume: !store.getState().paused }\r\n store.pause()\r\n seekToRatio(ratioFromEvent(event))\r\n }}\r\n onPointerMove={(event) => {\r\n const ratio = ratioFromEvent(event)\r\n if (event.pointerType === \"mouse\") setHover(ratio)\r\n if (scrub.current) seekToRatio(ratio)\r\n }}\r\n onPointerUp={endScrub}\r\n\r\n /**\r\n * The system can take a touch away mid-drag (an edge swipe, an incoming\r\n * call). Without this the bar stayed in scrub mode: paused, with the\r\n * fill frozen, because the draw loop skips while scrubbing.\r\n */\r\n onPointerCancel={endScrub}\r\n onPointerLeave={() => setHover(null)}\r\n onKeyDown={(event) => {\r\n const { currentTime, duration } = store.getState()\r\n const moves: Record<string, number> = {\r\n ArrowLeft: currentTime - step,\r\n ArrowDown: currentTime - step,\r\n ArrowRight: currentTime + step,\r\n ArrowUp: currentTime + step,\r\n PageDown: currentTime - duration / 10,\r\n PageUp: currentTime + duration / 10,\r\n Home: 0,\r\n End: duration,\r\n }\r\n if (!(event.key in moves)) return\r\n event.preventDefault()\r\n event.stopPropagation()\r\n store.seek(moves[event.key])\r\n }}\r\n {...props}\r\n >\r\n <div className=\"relative h-1 w-full overflow-hidden rounded-full bg-current/25 transition-[height] duration-150 ease-media group-hover/seek:h-1.5 group-focus-visible/seek:h-1.5 group-active/seek:h-1.5 motion-reduce:transition-none\">\r\n <div\r\n className=\"absolute inset-0 origin-left bg-current/25 transition-transform duration-300 ease-media\"\r\n style={{ transform: `scaleX(${duration ? clamp(bufferedEnd / duration) : 0})` }}\r\n />\r\n <div ref={fillRef} className=\"absolute inset-0 origin-left bg-current\" style={{ transform: \"scaleX(0)\" }} />\r\n </div>\r\n <span className=\"pointer-events-none absolute inset-0 rounded-full ring-current/60 group-focus-visible/seek:ring-2\" />\r\n {preview && hover !== null && duration > 0 && (\r\n <span\r\n aria-hidden\r\n className={cn(\r\n \"pointer-events-none absolute bottom-full mb-1.5 -translate-x-1/2 rounded-md bg-foreground px-1.5 py-0.5 font-mono text-[11px] leading-4 text-background tabular-nums\",\r\n previewClassName\r\n )}\r\n style={{ left: `${hover * 100}%` }}\r\n >\r\n {formatTime(hover * duration)}\r\n </span>\r\n )}\r\n </div>\r\n )\r\n}\r\n\r\n/* --------------------------------- Volume --------------------------------- */\r\n\r\ntype MediaVolumeProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\r\n /** \"vertical\" fills from the bottom, for a slider in a popover. */\r\n orientation?: \"horizontal\" | \"vertical\"\r\n}\r\n\r\nfunction MediaVolume({ orientation = \"horizontal\", className, ...props }: MediaVolumeProps) {\r\n const store = useMediaStore()\r\n const volume = useMedia((s) => (s.muted ? 0 : s.volume))\r\n const dragging = React.useRef(false)\r\n const vertical = orientation === \"vertical\"\r\n\r\n const setFromEvent = (event: React.PointerEvent<HTMLDivElement>) => {\r\n const rect = event.currentTarget.getBoundingClientRect()\r\n store.setVolume(\r\n clamp(vertical ? (rect.bottom - event.clientY) / rect.height : (event.clientX - rect.left) / rect.width)\r\n )\r\n }\r\n\r\n return (\r\n <div\r\n role=\"slider\"\r\n tabIndex={0}\r\n aria-label=\"Volume\"\r\n aria-valuemin={0}\r\n aria-valuemax={100}\r\n aria-valuenow={Math.round(volume * 100)}\r\n aria-valuetext={`${Math.round(volume * 100)}%`}\r\n aria-orientation={orientation}\r\n data-slot=\"media-volume\"\r\n className={cn(\r\n \"group/volume relative flex shrink-0 cursor-pointer touch-none outline-none select-none\",\r\n vertical ? \"h-16 w-5 justify-center\" : \"h-5 w-16 items-center\",\r\n className\r\n )}\r\n onPointerDown={(event) => {\r\n if (event.button !== 0) return\r\n event.currentTarget.setPointerCapture(event.pointerId)\r\n dragging.current = true\r\n setFromEvent(event)\r\n }}\r\n onPointerMove={(event) => dragging.current && setFromEvent(event)}\r\n onPointerUp={(event) => {\r\n dragging.current = false\r\n event.currentTarget.releasePointerCapture(event.pointerId)\r\n }}\r\n\r\n /* a drag the system takes away must end here too, or it stays live */\r\n onPointerCancel={() => {\r\n dragging.current = false\r\n }}\r\n onKeyDown={(event) => {\r\n const moves: Record<string, number> = {\r\n ArrowLeft: volume - 0.1,\r\n ArrowDown: volume - 0.1,\r\n ArrowRight: volume + 0.1,\r\n ArrowUp: volume + 0.1,\r\n PageDown: volume - 0.25,\r\n PageUp: volume + 0.25,\r\n Home: 0,\r\n End: 1,\r\n }\r\n if (!(event.key in moves)) return\r\n event.preventDefault()\r\n event.stopPropagation()\r\n store.setVolume(moves[event.key])\r\n }}\r\n {...props}\r\n >\r\n <div\r\n className={cn(\r\n \"relative overflow-hidden rounded-full bg-current/25 duration-150 ease-media motion-reduce:transition-none\",\r\n vertical\r\n ? \"h-full w-1 transition-[width] group-hover/volume:w-1.5 group-focus-visible/volume:w-1.5\"\r\n : \"h-1 w-full transition-[height] group-hover/volume:h-1.5 group-focus-visible/volume:h-1.5\"\r\n )}\r\n >\r\n <div\r\n className={cn(\"absolute inset-0 bg-current\", vertical ? \"origin-bottom\" : \"origin-left\")}\r\n style={{ transform: vertical ? `scaleY(${volume})` : `scaleX(${volume})` }}\r\n />\r\n </div>\r\n <span className=\"pointer-events-none absolute inset-0 rounded-full ring-current/60 group-focus-visible/volume:ring-2\" />\r\n </div>\r\n )\r\n}\r\n\r\n/* ---------------------------------- Time ---------------------------------- */\r\n\r\ntype MediaTimeProps = React.ComponentProps<\"span\"> & {\r\n type?: \"current\" | \"duration\" | \"remaining\"\r\n /** Click to show remaining time and back. \"remaining\" swaps with elapsed. */\r\n toggle?: boolean\r\n}\r\n\r\nconst timeNames = { current: \"elapsed\", duration: \"duration\", remaining: \"remaining\" } as const\r\n\r\nfunction MediaTime({ type = \"current\", toggle = false, className, ...props }: MediaTimeProps) {\r\n const [picked, setPicked] = React.useState(type)\r\n const shown = toggle ? picked : type\r\n const value = useMedia((s) => {\r\n if (shown === \"duration\") return Math.floor(s.duration)\r\n if (shown === \"remaining\") return Math.ceil(Math.max(s.duration - s.currentTime, 0))\r\n return Math.floor(s.currentTime)\r\n })\r\n const text = shown === \"remaining\" ? `-${formatTime(value)}` : formatTime(value)\r\n const other = type === \"remaining\" ? \"current\" : \"remaining\"\r\n const next = shown === type ? other : type\r\n\r\n return (\r\n <span data-slot=\"media-time\" className={cn(\"font-mono text-xs tabular-nums\", className)} {...props}>\r\n {toggle ? (\r\n <button\r\n type=\"button\"\r\n aria-label={`${shown === \"remaining\" ? \"Time remaining\" : shown === \"duration\" ? \"Duration\" : \"Time elapsed\"} ${formatTime(value)}`}\r\n onClick={() => setPicked(next)}\r\n className=\"group/media-button relative cursor-pointer rounded-sm outline-none transition-colors duration-150 ease-media hover:text-current focus-visible:ring-2 focus-visible:ring-current/60\"\r\n >\r\n {text}\r\n <MediaTooltip label={`Show ${timeNames[next]}`} />\r\n </button>\r\n ) : (\r\n text\r\n )}\r\n </span>\r\n )\r\n}\r\n\r\n/* --------------------------------- Loading -------------------------------- */\r\n\r\ntype MediaLoadingProps = React.ComponentProps<\"span\">\r\n\r\nfunction MediaLoading({ className, ...props }: MediaLoadingProps) {\r\n const waiting = useMedia((s) => s.waiting && !s.paused)\r\n return (\r\n <span\r\n role=\"status\"\r\n aria-label={waiting ? \"Loading\" : undefined}\r\n data-slot=\"media-loading\"\r\n className={cn(\r\n \"pointer-events-none size-6 rounded-full border-2 border-current/25 border-t-current transition-opacity duration-200 ease-media\",\r\n waiting ? \"animate-spin opacity-100 delay-300\" : \"opacity-0\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\n/* -------------------------------- Captions -------------------------------- */\r\n\r\n/**\r\n * Renders active caption cues. Native rendering is disabled so cues can be\r\n * styled and can slide above the controls when they appear.\r\n */\r\ntype MediaCaptionsProps = React.ComponentProps<\"div\">\r\n\r\nfunction MediaCaptions({ className, ...props }: MediaCaptionsProps) {\r\n const cues = useMedia((s) => (s.textTrack === -1 ? emptyCues : s.cues))\r\n if (!cues.length) return null\r\n return (\r\n <div\r\n data-slot=\"media-captions\"\r\n className={cn(\"pointer-events-none flex flex-col items-center gap-1 text-center\", className)}\r\n {...props}\r\n >\r\n {cues.map((cue, i) => (\r\n <span key={`${i}-${cue}`} className=\"whitespace-pre-line text-balance rounded-md px-2 py-0.5 box-decoration-clone\">\r\n {cue}\r\n </span>\r\n ))}\r\n </div>\r\n )\r\n}\r\n\r\nconst emptyCues: string[] = []\r\n\r\nfunction MediaCaptionsButton({ onClick, ...props }: MediaButtonProps) {\r\n const store = useMediaStore()\r\n const available = useMedia((s) => s.textTracks.length > 0)\r\n const on = useMedia((s) => s.textTrack !== -1)\r\n if (!available) return null\r\n return (\r\n <MediaButton\r\n aria-label=\"Captions\"\r\n aria-pressed={on}\r\n tooltip={on ? \"Captions off\" : \"Captions\"}\r\n shortcut=\"C\"\r\n onClick={(event) => {\r\n onClick?.(event)\r\n store.toggleCaptions()\r\n }}\r\n {...props}\r\n >\r\n <CaptionsIcon active={on} />\r\n </MediaButton>\r\n )\r\n}\r\n\r\n/* -------------------------------- Settings -------------------------------- */\r\n\r\ntype MediaSettingsProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\r\n rates?: number[]\r\n /** Classes for the floating panel. */\r\n panelClassName?: string\r\n}\r\n\r\n/**\r\n * Popover with playback speed and caption track choices. Arrow keys move\r\n * between options; Escape or a click outside closes it.\r\n */\r\nfunction MediaSettings({ rates = [0.5, 0.75, 1, 1.25, 1.5, 2], className, panelClassName, ...props }: MediaSettingsProps) {\r\n const store = useMediaStore()\r\n const rate = useMedia((s) => s.playbackRate)\r\n const tracks = useMedia((s) => s.textTracks)\r\n const textTrack = useMedia((s) => s.textTrack)\r\n const [open, setOpen] = React.useState(false)\r\n const rootEl = React.useRef<HTMLDivElement>(null)\r\n const triggerEl = React.useRef<HTMLButtonElement>(null)\r\n const panelEl = React.useRef<HTMLDivElement>(null)\r\n const panelId = React.useId()\r\n\r\n /**\r\n * The player clips its children, so on a short player (a phone in portrait)\r\n * a full-height panel lost its top rows. Cap the panel to the space above\r\n * the trigger and let it scroll, and re-measure when the player resizes.\r\n */\r\n React.useLayoutEffect(() => {\r\n const panel = panelEl.current\r\n const trigger = triggerEl.current\r\n const player = rootEl.current?.closest<HTMLElement>(\"[data-slot='media-root']\")\r\n if (!open || !panel || !trigger || !player) return\r\n const fit = () => {\r\n const space = trigger.getBoundingClientRect().top - player.getBoundingClientRect().top - 16\r\n panel.style.maxHeight = `${Math.max(96, Math.min(288, space))}px`\r\n }\r\n fit()\r\n const observer = new ResizeObserver(fit)\r\n observer.observe(player)\r\n return () => observer.disconnect()\r\n }, [open])\r\n\r\n React.useEffect(() => {\r\n if (!open) return\r\n const onPointerDown = (event: PointerEvent) => {\r\n if (!rootEl.current?.contains(event.target as Node)) setOpen(false)\r\n }\r\n document.addEventListener(\"pointerdown\", onPointerDown)\r\n rootEl.current?.querySelector<HTMLElement>(\"[aria-checked='true']\")?.focus()\r\n return () => document.removeEventListener(\"pointerdown\", onPointerDown)\r\n }, [open])\r\n\r\n const close = () => {\r\n setOpen(false)\r\n triggerEl.current?.focus()\r\n }\r\n\r\n const onPanelKeyDown = (event: React.KeyboardEvent) => {\r\n if (event.key === \"Escape\") {\r\n event.stopPropagation()\r\n close()\r\n return\r\n }\r\n const step = { ArrowDown: 1, ArrowRight: 1, ArrowUp: -1, ArrowLeft: -1 }[event.key]\r\n if (!step) return\r\n event.preventDefault()\r\n event.stopPropagation()\r\n const items = Array.from(rootEl.current?.querySelectorAll<HTMLElement>(\"[role='menuitemradio']\") ?? [])\r\n const index = items.indexOf(document.activeElement as HTMLElement)\r\n const next = items[(index + step + items.length) % items.length]\r\n next?.focus()\r\n }\r\n\r\n const option = (label: string, checked: boolean, select: () => void) => (\r\n <button\r\n key={label}\r\n type=\"button\"\r\n role=\"menuitemradio\"\r\n aria-checked={checked}\r\n tabIndex={checked ? 0 : -1}\r\n onClick={() => {\r\n select()\r\n close()\r\n }}\r\n className=\"flex h-8 w-full cursor-pointer items-center justify-between gap-6 rounded-md px-2 text-left text-sm outline-none transition-colors duration-150 ease-media hover:bg-current/10 focus-visible:bg-current/15\"\r\n >\r\n <span>{label}</span>\r\n <CheckIcon className={cn(\"size-3.5 transition-opacity duration-150\", checked ? \"opacity-100\" : \"opacity-0\")} />\r\n </button>\r\n )\r\n\r\n return (\r\n <div\r\n ref={rootEl}\r\n data-slot=\"media-settings\"\r\n data-open={open || undefined}\r\n className={cn(\"relative\", className)}\r\n {...props}\r\n >\r\n <MediaButton\r\n ref={triggerEl}\r\n aria-label=\"Settings\"\r\n tooltip=\"Settings\"\r\n aria-haspopup=\"menu\"\r\n aria-expanded={open}\r\n aria-controls={open ? panelId : undefined}\r\n onClick={() => setOpen((o) => !o)}\r\n >\r\n <SettingsIcon className={cn(\"transition-transform duration-300 ease-media motion-reduce:transition-none\", open && \"rotate-90\")} />\r\n </MediaButton>\r\n {open && (\r\n <div\r\n ref={panelEl}\r\n id={panelId}\r\n role=\"menu\"\r\n aria-label=\"Playback settings\"\r\n onKeyDown={onPanelKeyDown}\r\n className={cn(\r\n \"absolute right-0 bottom-full z-10 mb-2 flex max-h-72 w-44 origin-bottom-right flex-col gap-2 overflow-y-auto overscroll-contain rounded-xl p-1.5 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\",\r\n \"transition-[opacity,translate,scale] duration-150 ease-media starting:translate-y-1 starting:scale-95 starting:opacity-0 motion-reduce:transition-none\",\r\n panelClassName\r\n )}\r\n >\r\n <div role=\"group\" aria-label=\"Speed\" className=\"flex flex-col\">\r\n <span className=\"px-2 pt-1 pb-1.5 text-xs opacity-60\">Speed</span>\r\n <div className=\"grid grid-cols-3 gap-1\">\r\n {rates.map((r) => (\r\n <button\r\n key={r}\r\n type=\"button\"\r\n role=\"menuitemradio\"\r\n aria-checked={rate === r}\r\n aria-label={r === 1 ? \"Normal speed\" : `${r}× speed`}\r\n tabIndex={rate === r ? 0 : -1}\r\n onClick={() => {\r\n store.setPlaybackRate(r)\r\n close()\r\n }}\r\n className={cn(\r\n \"h-8 cursor-pointer rounded-md text-sm tabular-nums outline-none transition-colors duration-150 ease-media hover:bg-current/10 focus-visible:ring-1 focus-visible:ring-current/40\",\r\n rate === r && \"bg-current/15\"\r\n )}\r\n >\r\n {`${r}×`}\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n {tracks.length > 0 && (\r\n <div role=\"group\" aria-label=\"Captions\" className=\"flex flex-col\">\r\n <span className=\"px-2 pt-1 pb-1.5 text-xs opacity-60\">Captions</span>\r\n {option(\"Off\", textTrack === -1, () => store.setTextTrack(-1))}\r\n {tracks.map((track, i) => option(track.label, textTrack === i, () => store.setTextTrack(i)))}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n )\r\n}\r\n\r\n/* ---------------------------------- Error --------------------------------- */\r\n\r\ntype MediaErrorMessageProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\r\n /** Override the default message for a given MediaError. */\r\n message?: (error: MediaError) => React.ReactNode\r\n}\r\n\r\nfunction MediaErrorMessage({ className, message, ...props }: MediaErrorMessageProps) {\r\n const store = useMediaStore()\r\n const error = useMedia((s) => s.error)\r\n if (!error) return null\r\n return (\r\n <div\r\n role=\"alert\"\r\n data-slot=\"media-error\"\r\n className={cn(\"flex flex-col items-center justify-center gap-3 text-center\", className)}\r\n {...props}\r\n >\r\n <p className=\"text-pretty text-sm\">{message ? message(error) : describeMediaError(error)}</p>\r\n <button\r\n type=\"button\"\r\n onClick={() => store.retry()}\r\n className=\"inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-full px-3 text-sm outline-none ring-1 ring-current/25 transition-[background-color,scale] duration-150 ease-media hover:bg-current/10 focus-visible:ring-2 focus-visible:ring-current/60 motion-safe:active:scale-[0.96]\"\r\n >\r\n <RetryIcon className=\"size-3.5\" />\r\n Try again\r\n </button>\r\n </div>\r\n )\r\n}\r\n\r\nexport {\r\n MediaRoot,\r\n MediaVideo,\r\n MediaAudio,\r\n MediaSurface,\r\n MediaCaptions,\r\n MediaCaptionsButton,\r\n MediaSettings,\r\n MediaErrorMessage,\r\n MediaControls,\r\n MediaButton,\r\n MediaPlayButton,\r\n MediaMuteButton,\r\n MediaFullscreenButton,\r\n MediaPictureInPictureButton,\r\n MediaPlaybackRateButton,\r\n MediaSeek,\r\n MediaVolume,\r\n MediaTime,\r\n MediaLoading,\r\n useMedia,\r\n useMediaStore,\r\n}\r\nexport type {\r\n MediaSourceLoader,\r\n MediaRootProps,\r\n MediaVideoProps,\r\n MediaAudioProps,\r\n MediaSurfaceProps,\r\n MediaControlsProps,\r\n MediaButtonProps,\r\n MediaTooltipAlign,\r\n MediaPlaybackRateButtonProps,\r\n MediaSeekProps,\r\n MediaVolumeProps,\r\n MediaTimeProps,\r\n MediaLoadingProps,\r\n MediaCaptionsProps,\r\n MediaSettingsProps,\r\n MediaErrorMessageProps,\r\n}\r\n","export type MediaTextTrack = {\r\n label: string\r\n language: string\r\n kind: \"captions\" | \"subtitles\"\r\n}\r\n\r\nexport type MediaInfo = {\r\n title?: string\r\n artist?: string\r\n album?: string\r\n artwork?: { src: string; sizes?: string; type?: string }[]\r\n}\r\n\r\nexport type MediaState = {\r\n paused: boolean\r\n ended: boolean\r\n started: boolean\r\n waiting: boolean\r\n currentTime: number\r\n duration: number\r\n bufferedEnd: number\r\n volume: number\r\n muted: boolean\r\n playbackRate: number\r\n fullscreen: boolean\r\n pictureInPicture: boolean\r\n error: MediaError | null\r\n /** Caption and subtitle tracks, in document order. */\r\n textTracks: MediaTextTrack[]\r\n /** Index into `textTracks`, or -1 when captions are off. */\r\n textTrack: number\r\n /** Text of the cues active right now, one entry per cue. */\r\n cues: string[]\r\n}\r\n\r\nexport type MediaStoreOptions = {\r\n /** localStorage key used to remember volume, mute and speed. Off by default. */\r\n persistKey?: string\r\n}\r\n\r\ntype Listener = () => void\r\n\r\nconst MEDIA_EVENTS = [\r\n \"play\",\r\n \"pause\",\r\n \"playing\",\r\n \"ended\",\r\n \"waiting\",\r\n \"canplay\",\r\n \"seeking\",\r\n \"seeked\",\r\n \"timeupdate\",\r\n \"durationchange\",\r\n \"loadedmetadata\",\r\n \"progress\",\r\n \"volumechange\",\r\n \"ratechange\",\r\n \"emptied\",\r\n \"error\",\r\n \"enterpictureinpicture\",\r\n \"leavepictureinpicture\",\r\n] as const\r\n\r\nconst TRACK_EVENTS = [\"addtrack\", \"removetrack\", \"change\"] as const\r\n\r\nconst initialState: MediaState = {\r\n paused: true,\r\n ended: false,\r\n started: false,\r\n waiting: false,\r\n currentTime: 0,\r\n duration: 0,\r\n bufferedEnd: 0,\r\n volume: 1,\r\n muted: false,\r\n playbackRate: 1,\r\n fullscreen: false,\r\n pictureInPicture: false,\r\n error: null,\r\n textTracks: [],\r\n textTrack: -1,\r\n cues: [],\r\n}\r\n\r\ntype WebkitVideo = HTMLVideoElement & {\r\n webkitEnterFullscreen?: () => void\r\n webkitDisplayingFullscreen?: boolean\r\n}\r\n\r\nfunction readBufferedEnd(media: HTMLMediaElement) {\r\n const { buffered, currentTime } = media\r\n for (let i = 0; i < buffered.length; i++) {\r\n if (buffered.start(i) <= currentTime && buffered.end(i) >= currentTime) {\r\n return buffered.end(i)\r\n }\r\n }\r\n return buffered.length ? buffered.end(buffered.length - 1) : 0\r\n}\r\n\r\nfunction captionTracks(media: HTMLMediaElement | null) {\r\n if (!media?.textTracks) return []\r\n return Array.from(media.textTracks).filter(\r\n (track) => track.kind === \"captions\" || track.kind === \"subtitles\"\r\n )\r\n}\r\n\r\nfunction cueText(cue: TextTrackCue) {\r\n return ((cue as VTTCue).text ?? \"\").replace(/<[^>]+>/g, \"\").trim()\r\n}\r\n\r\nconst sameList = (a: readonly unknown[], b: readonly unknown[]) =>\r\n a.length === b.length && a.every((value, i) => Object.is(value, b[i]))\r\n\r\nconst sameTracks = (a: MediaTextTrack[], b: MediaTextTrack[]) =>\r\n a.length === b.length &&\r\n a.every((t, i) => t.label === b[i].label && t.language === b[i].language && t.kind === b[i].kind)\r\n\r\ntype Preferences = Pick<MediaState, \"volume\" | \"muted\" | \"playbackRate\">\r\n\r\nfunction readPreferences(key: string): Partial<Preferences> {\r\n try {\r\n return JSON.parse(localStorage.getItem(key) ?? \"{}\")\r\n } catch {\r\n return {}\r\n }\r\n}\r\n\r\n/**\r\n * Framework-agnostic store over any HTMLMediaElement. React bindings,\r\n * web components, or anything else can subscribe to it.\r\n */\r\nexport function createMediaStore({ persistKey }: MediaStoreOptions = {}) {\r\n let state = initialState\r\n let media: HTMLMediaElement | null = null\r\n let container: HTMLElement | null = null\r\n let cueTrack: TextTrack | null = null\r\n let metadata: MediaInfo | null = null\r\n const listeners = new Set<Listener>()\r\n\r\n const set = (patch: Partial<MediaState>) => {\r\n let changed = false\r\n for (const key in patch) {\r\n const k = key as keyof MediaState\r\n const next = patch[k]\r\n const prev = state[k]\r\n if (k === \"cues\" ? !sameList(prev as string[], next as string[]) : k === \"textTracks\" ? !sameTracks(prev as MediaTextTrack[], next as MediaTextTrack[]) : !Object.is(prev, next)) {\r\n changed = true\r\n break\r\n }\r\n }\r\n if (!changed) return\r\n state = { ...state, ...patch }\r\n listeners.forEach((l) => l())\r\n }\r\n\r\n const sync = () => {\r\n if (!media) return\r\n set({\r\n paused: media.paused,\r\n ended: media.ended,\r\n started: state.started || !media.paused || media.currentTime > 0,\r\n currentTime: media.currentTime,\r\n duration: Number.isFinite(media.duration) ? media.duration : 0,\r\n bufferedEnd: readBufferedEnd(media),\r\n volume: media.volume,\r\n muted: media.muted,\r\n playbackRate: media.playbackRate,\r\n pictureInPicture:\r\n typeof document !== \"undefined\" && document.pictureInPictureElement === media,\r\n error: media.error,\r\n })\r\n }\r\n\r\n /* ------------------------------ Captions ------------------------------ */\r\n\r\n const readCues = () => {\r\n const active = cueTrack?.activeCues\r\n set({ cues: active ? Array.from(active).map(cueText).filter(Boolean) : [] })\r\n }\r\n\r\n const bindCueTrack = (track: TextTrack | null) => {\r\n if (cueTrack === track) return\r\n cueTrack?.removeEventListener(\"cuechange\", readCues)\r\n cueTrack = track\r\n cueTrack?.addEventListener(\"cuechange\", readCues)\r\n }\r\n\r\n // Native rendering is replaced by our own cue layer: the active track runs\r\n // in \"hidden\" mode (cues load, nothing paints) and every other track is off.\r\n const syncTracks = (requested = state.textTrack) => {\r\n const tracks = captionTracks(media)\r\n let active = requested\r\n tracks.forEach((track, i) => {\r\n if (track.mode === \"showing\") active = i\r\n })\r\n if (active >= tracks.length) active = -1\r\n tracks.forEach((track, i) => {\r\n const mode = i === active ? \"hidden\" : \"disabled\"\r\n if (track.mode !== mode) track.mode = mode\r\n })\r\n bindCueTrack(tracks[active] ?? null)\r\n set({\r\n textTracks: tracks.map((track, i) => ({\r\n label: track.label || track.language || `Track ${i + 1}`,\r\n language: track.language,\r\n kind: track.kind as MediaTextTrack[\"kind\"],\r\n })),\r\n textTrack: active,\r\n })\r\n readCues()\r\n }\r\n\r\n const onTracksChange = () => syncTracks()\r\n\r\n /* ---------------------------- Media Session --------------------------- */\r\n\r\n const applyMediaSession = () => {\r\n if (typeof navigator === \"undefined\" || !(\"mediaSession\" in navigator) || !metadata) return\r\n const session = navigator.mediaSession\r\n session.metadata = new MediaMetadata(metadata)\r\n const handlers: [MediaSessionAction, MediaSessionActionHandler][] = [\r\n [\"play\", () => actions.play()],\r\n [\"pause\", () => actions.pause()],\r\n [\"seekbackward\", (d) => actions.seekBy(-(d.seekOffset ?? 10))],\r\n [\"seekforward\", (d) => actions.seekBy(d.seekOffset ?? 10)],\r\n [\"seekto\", (d) => d.seekTime !== undefined && actions.seek(d.seekTime)],\r\n ]\r\n for (const [action, handler] of handlers) {\r\n try {\r\n session.setActionHandler(action, handler)\r\n } catch {\r\n // Action not supported by this browser.\r\n }\r\n }\r\n }\r\n\r\n const updatePositionState = () => {\r\n if (!metadata || !media || typeof navigator === \"undefined\" || !(\"mediaSession\" in navigator)) return\r\n if (!Number.isFinite(media.duration) || media.duration <= 0) return\r\n try {\r\n navigator.mediaSession.setPositionState({\r\n duration: media.duration,\r\n playbackRate: media.playbackRate,\r\n position: Math.min(media.currentTime, media.duration),\r\n })\r\n } catch {\r\n // Ignore transient invalid states.\r\n }\r\n }\r\n\r\n /* ------------------------------- Events ------------------------------- */\r\n\r\n const savePreferences = () => {\r\n if (!persistKey || !media) return\r\n try {\r\n const prefs: Preferences = {\r\n volume: media.volume,\r\n muted: media.muted,\r\n playbackRate: media.playbackRate,\r\n }\r\n localStorage.setItem(persistKey, JSON.stringify(prefs))\r\n } catch {\r\n // Storage may be full or disabled.\r\n }\r\n }\r\n\r\n const onEvent = (event: Event) => {\r\n switch (event.type) {\r\n case \"emptied\":\r\n set({ started: false, waiting: false })\r\n break\r\n case \"waiting\":\r\n set({ waiting: true })\r\n break\r\n case \"playing\":\r\n case \"canplay\":\r\n case \"pause\":\r\n case \"error\":\r\n set({ waiting: false })\r\n break\r\n case \"play\":\r\n applyMediaSession()\r\n break\r\n case \"volumechange\":\r\n case \"ratechange\":\r\n savePreferences()\r\n break\r\n case \"loadedmetadata\":\r\n syncTracks()\r\n break\r\n }\r\n if (event.type === \"durationchange\" || event.type === \"ratechange\" || event.type === \"seeked\") {\r\n updatePositionState()\r\n }\r\n sync()\r\n }\r\n\r\n const onFullscreenChange = () => {\r\n const el = document.fullscreenElement\r\n const video = media as WebkitVideo | null\r\n set({\r\n fullscreen:\r\n (!!el && (el === container || el === media)) || !!video?.webkitDisplayingFullscreen,\r\n })\r\n }\r\n\r\n const attach = (el: HTMLMediaElement | null) => {\r\n if (media === el) return\r\n if (media) {\r\n MEDIA_EVENTS.forEach((e) => media!.removeEventListener(e, onEvent))\r\n TRACK_EVENTS.forEach((e) => media!.textTracks?.removeEventListener?.(e, onTracksChange))\r\n media.removeEventListener(\"webkitbeginfullscreen\", onFullscreenChange)\r\n media.removeEventListener(\"webkitendfullscreen\", onFullscreenChange)\r\n bindCueTrack(null)\r\n }\r\n media = el\r\n if (!el) {\r\n state = initialState\r\n listeners.forEach((l) => l())\r\n return\r\n }\r\n if (persistKey) {\r\n const prefs = readPreferences(persistKey)\r\n if (typeof prefs.volume === \"number\") el.volume = Math.min(Math.max(prefs.volume, 0), 1)\r\n if (typeof prefs.muted === \"boolean\") el.muted = prefs.muted\r\n if (typeof prefs.playbackRate === \"number\") el.playbackRate = prefs.playbackRate\r\n }\r\n MEDIA_EVENTS.forEach((e) => el.addEventListener(e, onEvent))\r\n TRACK_EVENTS.forEach((e) => el.textTracks?.addEventListener?.(e, onTracksChange))\r\n el.addEventListener(\"webkitbeginfullscreen\", onFullscreenChange)\r\n el.addEventListener(\"webkitendfullscreen\", onFullscreenChange)\r\n sync()\r\n syncTracks()\r\n }\r\n\r\n const setContainer = (el: HTMLElement | null) => {\r\n if (typeof document === \"undefined\") return\r\n document.removeEventListener(\"fullscreenchange\", onFullscreenChange)\r\n container = el\r\n if (el) document.addEventListener(\"fullscreenchange\", onFullscreenChange)\r\n }\r\n\r\n const play = async () => {\r\n if (!media) return\r\n try {\r\n await media.play()\r\n } catch {\r\n // Autoplay policies may reject; the paused state stays accurate.\r\n }\r\n }\r\n\r\n const pause = () => media?.pause()\r\n\r\n const actions = {\r\n play,\r\n pause,\r\n toggle: () => (media?.paused || media?.ended ? play() : pause()),\r\n seek: (time: number) => {\r\n if (!media) return\r\n const max = Number.isFinite(media.duration) ? media.duration : time\r\n media.currentTime = Math.min(Math.max(time, 0), max)\r\n sync()\r\n },\r\n seekBy: (delta: number) => actions.seek((media?.currentTime ?? 0) + delta),\r\n /** Jump to a fraction (0–1) of the duration. */\r\n seekToRatio: (ratio: number) => actions.seek(ratio * (media?.duration || 0)),\r\n setVolume: (volume: number) => {\r\n if (!media) return\r\n media.volume = Math.min(Math.max(volume, 0), 1)\r\n media.muted = media.volume === 0\r\n },\r\n toggleMute: () => {\r\n if (!media) return\r\n if (media.muted && media.volume === 0) media.volume = 1\r\n media.muted = !media.muted\r\n },\r\n setPlaybackRate: (rate: number) => {\r\n if (media) media.playbackRate = rate\r\n },\r\n /** Select a caption track by index, or -1 to turn captions off. */\r\n setTextTrack: (index: number) => syncTracksExplicit(index),\r\n toggleCaptions: () => {\r\n if (!state.textTracks.length) return\r\n syncTracksExplicit(state.textTrack === -1 ? 0 : -1)\r\n },\r\n /** Reload the current source after an error. */\r\n retry: () => {\r\n if (!media) return\r\n media.load()\r\n void play()\r\n },\r\n setMetadata: (next: MediaInfo | null) => {\r\n metadata = next\r\n if (media && !media.paused) applyMediaSession()\r\n },\r\n toggleFullscreen: async () => {\r\n if (typeof document === \"undefined\") return\r\n if (document.fullscreenElement) {\r\n await document.exitFullscreen().catch(() => {})\r\n return\r\n }\r\n if (container?.requestFullscreen) {\r\n await container.requestFullscreen().catch(() => {})\r\n return\r\n }\r\n // iOS Safari only supports fullscreen on the video element itself.\r\n ;(media as WebkitVideo | null)?.webkitEnterFullscreen?.()\r\n },\r\n togglePictureInPicture: async () => {\r\n if (!(media instanceof HTMLVideoElement)) return\r\n try {\r\n if (document.pictureInPictureElement) await document.exitPictureInPicture()\r\n else await media.requestPictureInPicture()\r\n } catch {\r\n // Unsupported or blocked by the browser.\r\n }\r\n },\r\n }\r\n\r\n // An explicit choice must win over a track the browser marked \"showing\".\r\n const syncTracksExplicit = (index: number) => {\r\n captionTracks(media).forEach((track, i) => {\r\n if (i !== index && track.mode === \"showing\") track.mode = \"disabled\"\r\n })\r\n syncTracks(index)\r\n }\r\n\r\n return {\r\n getState: () => state,\r\n getServerState: () => initialState,\r\n getMedia: () => media,\r\n subscribe: (listener: Listener) => {\r\n listeners.add(listener)\r\n return () => listeners.delete(listener)\r\n },\r\n attach,\r\n setContainer,\r\n ...actions,\r\n }\r\n}\r\n\r\nexport type MediaStore = ReturnType<typeof createMediaStore>\r\n\r\nexport function formatTime(seconds: number) {\r\n if (!Number.isFinite(seconds) || seconds < 0) seconds = 0\r\n const s = Math.floor(seconds % 60)\r\n const m = Math.floor((seconds / 60) % 60)\r\n const h = Math.floor(seconds / 3600)\r\n const ss = s.toString().padStart(2, \"0\")\r\n return h > 0 ? `${h}:${m.toString().padStart(2, \"0\")}:${ss}` : `${m}:${ss}`\r\n}\r\n\r\n/** Human-readable message for a MediaError code. */\r\nexport function describeMediaError(error: MediaError | null) {\r\n switch (error?.code) {\r\n case 1:\r\n return \"Playback was aborted.\"\r\n case 2:\r\n return \"A network error stopped the download.\"\r\n case 3:\r\n return \"This media could not be decoded.\"\r\n case 4:\r\n return \"This format or source isn't supported.\"\r\n default:\r\n return \"Something went wrong during playback.\"\r\n }\r\n}\r\n","\"use client\"\r\n\r\nimport * as React from \"react\"\r\n\r\nimport { cn } from \"@/lib/utils\"\r\nimport type { MediaInfo } from \"@/lib/media-store\"\r\nimport { PlayIcon } from \"@/components/ui/media-icons\"\r\nimport {\r\n MediaCaptions,\r\n MediaCaptionsButton,\r\n MediaControls,\r\n MediaErrorMessage,\r\n MediaFullscreenButton,\r\n MediaLoading,\r\n MediaMuteButton,\r\n MediaPictureInPictureButton,\r\n MediaPlayButton,\r\n MediaRoot,\r\n MediaSeek,\r\n MediaSettings,\r\n MediaSurface,\r\n MediaTime,\r\n MediaVideo,\r\n MediaVolume,\r\n type MediaSourceLoader,\r\n} from \"@/components/ui/media\"\r\n\r\ntype VideoTrack = {\r\n src: string\r\n label: string\r\n srcLang: string\r\n kind?: \"captions\" | \"subtitles\"\r\n default?: boolean\r\n}\r\n\r\ntype VideoPlayerProps = Omit<React.ComponentProps<\"video\">, \"controls\"> & {\r\n /** Classes for the outer frame. `className` targets the <video> element. */\r\n frameClassName?: string\r\n /** WebVTT caption or subtitle files. */\r\n tracks?: VideoTrack[]\r\n /** Adapter for sources the browser can't play natively, e.g. HLS. */\r\n loader?: MediaSourceLoader\r\n /** Shown in OS media controls and lock screens. */\r\n metadata?: MediaInfo\r\n /** Remember volume, mute and speed under this localStorage key. */\r\n persistKey?: string\r\n /** Small labels with shortcuts over the controls on hover and focus. On by default. */\r\n tooltips?: boolean\r\n}\r\n\r\nconst reveal = \"transition-[opacity,scale] duration-200 ease-media motion-reduce:transition-none\"\r\nconst visibleWithControls =\r\n \"group-data-active/media:opacity-100 group-has-focus-visible/media:opacity-100 group-data-paused/media:group-data-started/media:opacity-100\"\r\nconst glass = \"bg-media-scrim/70 text-media-foreground ring-1 ring-media-foreground/10 backdrop-blur-xl\"\r\n\r\n/**\r\n * The volume popover opens on hover or keyboard focus, never on a click, so the\r\n * mute button keeps its one job. `has-focus-visible` rather than focus-within,\r\n * so a mouse click on mute does not pin it open.\r\n */\r\nconst volumeOpen =\r\n \"group-hover/volume-menu:pointer-events-auto group-hover/volume-menu:translate-y-0 group-hover/volume-menu:opacity-100 group-has-focus-visible/volume-menu:pointer-events-auto group-has-focus-visible/volume-menu:translate-y-0 group-has-focus-visible/volume-menu:opacity-100\"\r\n\r\nfunction VideoPlayer({\r\n className,\r\n frameClassName,\r\n tracks,\r\n loader,\r\n metadata,\r\n persistKey,\r\n tooltips = true,\r\n children,\r\n crossOrigin,\r\n ...props\r\n}: VideoPlayerProps) {\r\n return (\r\n <MediaRoot\r\n persistKey={persistKey}\r\n metadata={metadata}\r\n tooltips={tooltips}\r\n className={cn(\r\n \"@container/player aspect-video w-full overflow-hidden rounded-xl bg-media-scrim text-media-foreground data-fullscreen:rounded-none\",\r\n frameClassName\r\n )}\r\n >\r\n <MediaVideo\r\n className={cn(\"size-full object-contain\", className)}\r\n loader={loader}\r\n crossOrigin={crossOrigin ?? (tracks?.length ? \"anonymous\" : undefined)}\r\n {...props}\r\n >\r\n {tracks?.map((track) => (\r\n <track\r\n key={track.src}\r\n src={track.src}\r\n label={track.label}\r\n srcLang={track.srcLang}\r\n kind={track.kind ?? \"subtitles\"}\r\n default={track.default}\r\n />\r\n ))}\r\n {children}\r\n </MediaVideo>\r\n\r\n <MediaSurface indicatorClassName={glass} />\r\n\r\n <span\r\n aria-hidden\r\n className={cn(\r\n \"pointer-events-none absolute top-1/2 left-1/2 grid size-12 -translate-1/2 place-items-center rounded-full bg-media-scrim/40 backdrop-blur-md\",\r\n reveal,\r\n \"group-data-started/media:scale-75 group-data-started/media:opacity-0\"\r\n )}\r\n >\r\n <PlayIcon className=\"size-5\" />\r\n </span>\r\n\r\n <span className=\"pointer-events-none absolute inset-0 grid place-items-center\">\r\n <MediaLoading />\r\n </span>\r\n\r\n <MediaCaptions\r\n className={cn(\r\n \"absolute inset-x-4 bottom-4 text-sm leading-snug transition-transform duration-200 ease-media motion-reduce:transition-none sm:text-base\",\r\n \"group-data-active/media:-translate-y-10 group-has-focus-visible/media:-translate-y-10 group-data-paused/media:group-data-started/media:-translate-y-10\",\r\n \"[&>span]:bg-media-scrim/75 [&>span]:text-media-foreground\"\r\n )}\r\n />\r\n\r\n <div\r\n aria-hidden\r\n className={cn(\r\n \"pointer-events-none absolute inset-x-0 bottom-0 h-24 bg-linear-to-t from-media-scrim/60 to-transparent opacity-0\",\r\n reveal,\r\n visibleWithControls\r\n )}\r\n />\r\n\r\n {/* tooltips match the seek preview: light on the dark bar */}\r\n <MediaControls className=\"absolute inset-x-2 bottom-2 h-9 gap-1 px-1 **:data-[slot=media-tooltip]:bg-media-foreground **:data-[slot=media-tooltip]:text-media-scrim\">\r\n <MediaPlayButton tooltipAlign=\"start\" />\r\n <MediaSeek className=\"mx-2\" previewClassName=\"bg-media-foreground text-media-scrim\" />\r\n <MediaTime type=\"remaining\" toggle className=\"min-w-10 text-right text-media-foreground/80\" />\r\n\r\n {/* a vertical slider above mute, so opening it never squeezes the seek\r\n bar. Fine pointers only: phones set volume with their own buttons,\r\n and iOS ignores volume changes from the page */}\r\n <div className=\"group/volume-menu relative\">\r\n {/* no tooltip: the volume popover opens in the same spot */}\r\n <MediaMuteButton tooltip={false} />\r\n <div\r\n className={cn(\r\n \"pointer-events-none absolute bottom-full left-1/2 hidden -translate-x-1/2 translate-y-1 pb-2 opacity-0 pointer-fine:block\",\r\n \"transition-[opacity,translate] duration-150 ease-media motion-reduce:transition-none\",\r\n volumeOpen\r\n )}\r\n >\r\n <div className={cn(\"flex h-24 w-7 justify-center rounded-full py-3\", glass)}>\r\n <MediaVolume orientation=\"vertical\" className=\"h-full\" />\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <MediaCaptionsButton />\r\n <MediaSettings panelClassName={glass} />\r\n {/* only where the bar has room for it; the I key works at any size */}\r\n <MediaPictureInPictureButton tooltipAlign=\"end\" className=\"hidden @sm/player:inline-grid\" />\r\n <MediaFullscreenButton tooltipAlign=\"end\" />\r\n </MediaControls>\r\n\r\n <MediaErrorMessage className=\"absolute inset-0 bg-media-scrim/85 px-6 text-media-foreground backdrop-blur-sm\" />\r\n </MediaRoot>\r\n )\r\n}\r\n\r\nexport { VideoPlayer, type VideoPlayerProps, type VideoTrack }\r\n","\"use client\"\r\n\r\nimport * as React from \"react\"\r\n\r\nimport { cn } from \"@/lib/utils\"\r\nimport type { MediaInfo } from \"@/lib/media-store\"\r\nimport {\r\n MediaAudio,\r\n MediaErrorMessage,\r\n MediaLoading,\r\n MediaMuteButton,\r\n MediaPlayButton,\r\n MediaPlaybackRateButton,\r\n MediaRoot,\r\n MediaSeek,\r\n MediaTime,\r\n MediaVolume,\r\n type MediaSourceLoader,\r\n} from \"@/components/ui/media\"\r\n\r\ntype AudioPlayerProps = Omit<React.ComponentProps<\"audio\">, \"controls\" | \"title\"> & {\r\n title?: React.ReactNode\r\n description?: React.ReactNode\r\n /** Cover image shown beside the controls and in OS media controls. */\r\n artwork?: string\r\n /** Show a playback speed toggle. Useful for podcasts and voice notes. */\r\n showSpeed?: boolean\r\n /** Small labels with shortcuts over the controls on hover and focus. On by default. */\r\n tooltips?: boolean\r\n /** Classes for the outer frame. `className` targets the <audio> element. */\r\n frameClassName?: string\r\n /** Adapter for sources the browser can't play natively, e.g. HLS. */\r\n loader?: MediaSourceLoader\r\n /** Overrides metadata derived from `title`, `description` and `artwork`. */\r\n metadata?: MediaInfo\r\n /** Remember volume, mute and speed under this localStorage key. */\r\n persistKey?: string\r\n}\r\n\r\nconst muted = \"text-muted-foreground hover:text-foreground\"\r\n\r\n/**\r\n * The volume popover opens on hover or keyboard focus, never on a click, so the\r\n * mute button keeps its one job. `has-focus-visible` rather than focus-within,\r\n * so a mouse click on mute does not pin it open.\r\n */\r\nconst volumeOpen =\r\n \"group-hover/volume-menu:pointer-events-auto group-hover/volume-menu:translate-y-0 group-hover/volume-menu:opacity-100 group-has-focus-visible/volume-menu:pointer-events-auto group-has-focus-visible/volume-menu:translate-y-0 group-has-focus-visible/volume-menu:opacity-100\"\r\n\r\nfunction AudioPlayer({\r\n title,\r\n description,\r\n artwork,\r\n showSpeed,\r\n tooltips = true,\r\n className,\r\n frameClassName,\r\n loader,\r\n metadata,\r\n persistKey,\r\n ...props\r\n}: AudioPlayerProps) {\r\n const sessionInfo =\r\n metadata ??\r\n (typeof title === \"string\"\r\n ? {\r\n title,\r\n artist: typeof description === \"string\" ? description : undefined,\r\n artwork: artwork ? [{ src: artwork }] : undefined,\r\n }\r\n : undefined)\r\n\r\n const hasInfo = Boolean(title || description)\r\n\r\n return (\r\n <MediaRoot\r\n persistKey={persistKey}\r\n metadata={sessionInfo}\r\n tooltips={tooltips}\r\n className={cn(\r\n \"@container/audio grid w-full grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-x-3 gap-y-1 rounded-xl border bg-card p-2.5 pr-3 text-card-foreground\",\r\n frameClassName\r\n )}\r\n >\r\n <MediaAudio className={className} loader={loader} {...props} />\r\n\r\n {/* with a title, the info stacks over the seek row and the controls\r\n span both. Below 24rem the controls take the first row, then the\r\n title and the seek row each get the full width */}\r\n <div className={cn(\"flex items-center\", hasInfo && \"row-span-2 @max-sm/audio:row-span-1\")}>\r\n <span className=\"relative shrink-0\">\r\n {artwork ? (\r\n /**\r\n * The tile presses as one piece. Scaling the button alone pulled\r\n * its scrim in from the edges and bared a band of the image. No\r\n * overflow-hidden, so the play tooltip is not clipped.\r\n */\r\n <span className=\"relative block size-11 rounded-lg bg-muted transition-[scale] duration-150 ease-media motion-safe:has-[button:enabled:active]:scale-[0.96] motion-reduce:transition-none\">\r\n {/* eslint-disable-next-line @next/next/no-img-element */}\r\n <img\r\n src={artwork}\r\n alt=\"\"\r\n className=\"size-full rounded-lg object-cover outline outline-black/10 -outline-offset-1 dark:outline-white/10\"\r\n />\r\n <MediaPlayButton\r\n tooltipAlign=\"start\"\r\n className=\"absolute inset-0 size-full rounded-lg motion-safe:enabled:active:scale-100 bg-media-scrim/15 text-media-foreground hover:bg-media-scrim/35 focus-visible:bg-media-scrim/35 group-data-playing/media:bg-media-scrim/35 [&_svg]:size-4 [&_svg]:drop-shadow-sm\"\r\n />\r\n </span>\r\n ) : (\r\n <MediaPlayButton\r\n tooltipAlign=\"start\"\r\n className=\"size-10 rounded-full bg-primary text-primary-foreground hover:bg-primary/90 [&_svg]:size-4\"\r\n />\r\n )}\r\n {/* while the stream buffers: a ring around the round button, or a\r\n spinner on the cover, since a spinning square would look broken */}\r\n <MediaLoading\r\n className={\r\n artwork\r\n ? \"absolute inset-0 m-auto size-5 text-background\"\r\n : \"absolute -inset-1 size-auto border-current/15 border-t-current text-foreground\"\r\n }\r\n />\r\n </span>\r\n </div>\r\n\r\n {hasInfo && (\r\n <div className=\"col-start-2 flex min-w-0 items-baseline gap-2 self-end text-sm leading-5 @max-sm/audio:col-span-3 @max-sm/audio:col-start-1 @max-sm/audio:row-start-2 @max-sm/audio:pt-1\">\r\n {title && <span className=\"truncate font-medium\">{title}</span>}\r\n {description && <span className=\"truncate text-muted-foreground\">{description}</span>}\r\n </div>\r\n )}\r\n\r\n <div\r\n className={cn(\r\n \"col-start-2 flex items-center gap-2.5 text-muted-foreground\",\r\n hasInfo && \"row-start-2 self-start @max-sm/audio:col-span-3 @max-sm/audio:col-start-1 @max-sm/audio:row-start-3\"\r\n )}\r\n >\r\n <MediaTime className=\"min-w-9\" />\r\n <MediaSeek className=\"text-foreground\" />\r\n <MediaTime type=\"duration\" toggle className=\"min-w-9 text-right\" />\r\n </div>\r\n\r\n <div className={cn(\"col-start-3 flex items-center gap-0.5\", hasInfo && \"row-span-2 row-start-1 @max-sm/audio:row-span-1\")}>\r\n {showSpeed && <MediaPlaybackRateButton rates={[1, 1.5, 2, 0.75]} className={muted} />}\r\n\r\n {/* a vertical slider above mute. Fine pointers only: phones set\r\n volume with their own buttons, and iOS ignores it from the page */}\r\n <div className=\"group/volume-menu relative\">\r\n {/* no tooltip: the volume popover opens in the same spot */}\r\n <MediaMuteButton tooltip={false} className={muted} />\r\n <div\r\n className={cn(\r\n \"pointer-events-none absolute bottom-full left-1/2 z-10 hidden -translate-x-1/2 translate-y-1 pb-2 opacity-0 pointer-fine:block\",\r\n \"transition-[opacity,translate] duration-150 ease-media motion-reduce:transition-none\",\r\n volumeOpen\r\n )}\r\n >\r\n <div className=\"flex h-24 w-7 justify-center rounded-full bg-popover py-3 text-popover-foreground shadow-md ring-1 ring-foreground/10\">\r\n <MediaVolume orientation=\"vertical\" className=\"h-full\" />\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <MediaErrorMessage className=\"absolute inset-0 flex-row gap-4 rounded-[inherit] bg-card/95 px-4 text-card-foreground\" />\r\n </MediaRoot>\r\n )\r\n}\r\n\r\nexport { AudioPlayer, type AudioPlayerProps }\r\n"],"mappings":"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","names":["muted","React","jsx","jsxs","muted","duration","currentTime","jsx","jsxs","jsx","jsxs","volumeOpen"]}
1
+ {"version":3,"sources":["../src/utils.ts","../../../components/ui/media-icons.tsx","../../../components/ui/media.tsx","../../../lib/media-store.ts","../../../components/ui/video-player.tsx","../../../components/ui/audio-player.tsx"],"sourcesContent":["import { clsx, type ClassValue } from \"clsx\"\r\nimport { twMerge } from \"tailwind-merge\"\r\n\r\n/**\r\n * The package's `cn`. The site's `lib/utils.ts` registers the site's type\r\n * scale with tailwind-merge, which a consumer's app does not have.\r\n */\r\nexport function cn(...inputs: ClassValue[]) {\r\n return twMerge(clsx(inputs))\r\n}\r\n","import * as React from \"react\"\n\nexport type MediaIconProps = React.ComponentProps<\"svg\">\n\nconst base = {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: 16,\n height: 16,\n viewBox: \"0 0 24 24\",\n \"aria-hidden\": true,\n} as const\n\n/**\n * Optically centred: the filled area's centroid, not the bounding box, sits\n * on the viewBox centre (x = 12), measured from the rendered shape. A\n * box-centred triangle looks shifted right, because its weight is in the flat\n * left side.\n */\nexport function PlayIcon(props: MediaIconProps) {\n return (\n <svg {...base} fill=\"currentColor\" {...props}>\n <path d=\"M6.87 4.9v14.2a1.5 1.5 0 0 0 2.31 1.26l11.07-7.1a1.5 1.5 0 0 0 0-2.52L9.18 3.64A1.5 1.5 0 0 0 6.87 4.9Z\" />\n </svg>\n )\n}\n\nexport function PauseIcon(props: MediaIconProps) {\n return (\n <svg {...base} fill=\"currentColor\" {...props}>\n <rect x=\"5\" y=\"3.5\" width=\"5\" height=\"17\" rx=\"1.6\" />\n <rect x=\"14\" y=\"3.5\" width=\"5\" height=\"17\" rx=\"1.6\" />\n </svg>\n )\n}\n\nexport function VolumeIcon({ muted, ...props }: MediaIconProps & { muted?: boolean }) {\n const fade = \"origin-[11px_12px] transition-[opacity,transform] duration-200 ease-media motion-reduce:transition-none\"\n return (\n <svg {...base} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" {...props}>\n <path\n d=\"M11 5.1v13.8a1 1 0 0 1-1.63.77L5.5 16.5h-2A1.5 1.5 0 0 1 2 15V9a1.5 1.5 0 0 1 1.5-1.5h2l3.87-3.17A1 1 0 0 1 11 5.1Z\"\n fill=\"currentColor\"\n stroke=\"none\"\n />\n <g className={fade} style={{ opacity: muted ? 0 : 1, transform: muted ? \"scale(0.6)\" : \"none\" }}>\n <path d=\"M15 9.5a3.5 3.5 0 0 1 0 5\" />\n <path d=\"M17.8 6.7a7.5 7.5 0 0 1 0 10.6\" />\n </g>\n <g className={fade} style={{ opacity: muted ? 1 : 0, transform: muted ? \"none\" : \"scale(0.6)\" }}>\n <path d=\"m16 9.5 5 5\" />\n <path d=\"m21 9.5-5 5\" />\n </g>\n </svg>\n )\n}\n\nexport function EnterFullscreenIcon(props: MediaIconProps) {\n return (\n <svg {...base} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" {...props}>\n <path d=\"M4 9V6a2 2 0 0 1 2-2h3M15 4h3a2 2 0 0 1 2 2v3M20 15v3a2 2 0 0 1-2 2h-3M9 20H6a2 2 0 0 1-2-2v-3\" />\n </svg>\n )\n}\n\nexport function ExitFullscreenIcon(props: MediaIconProps) {\n return (\n <svg {...base} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" {...props}>\n <path d=\"M9 4v3a2 2 0 0 1-2 2H4M20 9h-3a2 2 0 0 1-2-2V4M15 20v-3a2 2 0 0 1 2-2h3M4 15h3a2 2 0 0 1 2 2v3\" />\n </svg>\n )\n}\n\nexport function PictureInPictureIcon(props: MediaIconProps) {\n return (\n <svg {...base} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" {...props}>\n <rect x=\"2.5\" y=\"4.5\" width=\"19\" height=\"15\" rx=\"2.5\" />\n <rect x=\"12\" y=\"11.5\" width=\"6.5\" height=\"5\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" />\n </svg>\n )\n}\n\nconst captionLetters = \"M10.5 10.2a2.2 2.2 0 1 0 0 3.6M17 10.2a2.2 2.2 0 1 0 0 3.6\"\n\n/** Outlined when off; filled with knocked-out letters when on. */\nexport function CaptionsIcon({ active, ...props }: MediaIconProps & { active?: boolean }) {\n const maskId = `cc${React.useId().replace(/[^\\w-]/g, \"\")}`\n return (\n <svg {...base} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" {...props}>\n <mask id={maskId}>\n <rect width=\"24\" height=\"24\" fill=\"white\" />\n <path d={captionLetters} stroke=\"black\" />\n </mask>\n <rect\n x=\"2.5\"\n y=\"4.5\"\n width=\"19\"\n height=\"15\"\n rx=\"3\"\n fill=\"currentColor\"\n mask={`url(#${maskId})`}\n className=\"transition-opacity duration-200 ease-media motion-reduce:transition-none\"\n style={{ opacity: active ? 1 : 0 }}\n />\n <rect x=\"2.5\" y=\"4.5\" width=\"19\" height=\"15\" rx=\"3\" />\n <path\n d={captionLetters}\n className=\"transition-opacity duration-200 ease-media motion-reduce:transition-none\"\n style={{ opacity: active ? 0 : 1 }}\n />\n </svg>\n )\n}\n\nexport function SettingsIcon(props: MediaIconProps) {\n return (\n <svg {...base} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" {...props}>\n <path d=\"M4 7h9M17 7h3M4 17h3M11 17h9\" />\n <circle cx=\"15\" cy=\"7\" r=\"2\" />\n <circle cx=\"9\" cy=\"17\" r=\"2\" />\n </svg>\n )\n}\n\nexport function RetryIcon(props: MediaIconProps) {\n return (\n <svg {...base} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" {...props}>\n <path d=\"M3.5 12a8.5 8.5 0 1 0 2.6-6.1\" />\n <path d=\"M3.5 4v4.5H8\" />\n </svg>\n )\n}\n\nexport function CheckIcon(props: MediaIconProps) {\n return (\n <svg {...base} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.25\" strokeLinecap=\"round\" strokeLinejoin=\"round\" {...props}>\n <path d=\"m5 12.5 4.5 4.5L19 7.5\" />\n </svg>\n )\n}\n\nexport function SeekIcon({ direction, ...props }: MediaIconProps & { direction: \"back\" | \"forward\" }) {\n return (\n <svg {...base} fill=\"currentColor\" {...props} style={{ transform: direction === \"back\" ? \"scaleX(-1)\" : undefined, ...props.style }}>\n <path d=\"M3 6.7v10.6a1 1 0 0 0 1.55.83L12 13.2v4.1a1 1 0 0 0 1.55.83l7.9-5.3a1 1 0 0 0 0-1.66l-7.9-5.3A1 1 0 0 0 12 6.7v4.1L4.55 5.87A1 1 0 0 0 3 6.7Z\" />\n </svg>\n )\n}\n","\"use client\"\r\n\r\nimport * as React from \"react\"\r\n\r\nimport {\r\n createMediaStore,\r\n describeMediaError,\r\n formatTime,\r\n type MediaInfo,\r\n type MediaState,\r\n type MediaStore,\r\n} from \"@/lib/media-store\"\r\nimport { cn } from \"@/lib/utils\"\r\nimport {\r\n CaptionsIcon,\r\n CheckIcon,\r\n EnterFullscreenIcon,\r\n ExitFullscreenIcon,\r\n PauseIcon,\r\n PictureInPictureIcon,\r\n PlayIcon,\r\n RetryIcon,\r\n SeekIcon,\r\n SettingsIcon,\r\n VolumeIcon,\r\n} from \"@/components/ui/media-icons\"\r\n\r\nconst MediaContext = React.createContext<MediaStore | null>(null)\r\n\r\n/** Root options the parts read: whether to show tooltips and shortcut hints. */\r\nconst MediaOptionsContext = React.createContext({ tooltips: false, hotkeys: false })\r\n\r\nfunction useMediaStore() {\r\n const store = React.useContext(MediaContext)\r\n if (!store) throw new Error(\"Media components must be rendered inside <MediaRoot>.\")\r\n return store\r\n}\r\n\r\nfunction useMedia<T>(selector: (state: MediaState) => T): T {\r\n const store = useMediaStore()\r\n return React.useSyncExternalStore(\r\n store.subscribe,\r\n () => selector(store.getState()),\r\n () => selector(store.getServerState())\r\n )\r\n}\r\n\r\n/* a fixed arity, so the dependency list is a literal the hooks rules can check */\r\nfunction useComposedRefs<T>(a: React.Ref<T> | undefined, b: React.Ref<T> | undefined, c?: React.Ref<T>) {\r\n return React.useCallback((node: T | null) => composeRefs(a, b, c)(node), [a, b, c])\r\n}\r\n\r\nfunction composeRefs<T>(...refs: (React.Ref<T> | undefined)[]) {\r\n return (node: T | null) => {\r\n const cleanups = refs.map((ref) => {\r\n if (typeof ref === \"function\") return ref(node)\r\n if (ref) (ref as React.RefObject<T | null>).current = node\r\n })\r\n return () => {\r\n cleanups.forEach((cleanup, i) => {\r\n const ref = refs[i]\r\n if (typeof cleanup === \"function\") cleanup()\r\n else if (typeof ref === \"function\") ref(null)\r\n else if (ref) (ref as React.RefObject<T | null>).current = null\r\n })\r\n }\r\n }\r\n}\r\n\r\nconst clamp = (value: number) => Math.min(Math.max(value, 0), 1)\r\n\r\n/* ---------------------------------- Root ---------------------------------- */\r\n\r\ntype MediaRootProps = React.ComponentProps<\"div\"> & {\r\n /** Milliseconds of pointer inactivity before controls hide during playback. */\r\n idleTimeout?: number\r\n /** Remember volume, mute and speed in localStorage under this key. */\r\n persistKey?: string\r\n /** Title, artist and artwork for OS media controls (Media Session API). */\r\n metadata?: MediaInfo\r\n /** Enable keyboard shortcuts while focus is inside the player. */\r\n hotkeys?: boolean\r\n /** Small labels over the buttons on hover and keyboard focus, with their shortcut. */\r\n tooltips?: boolean\r\n}\r\n\r\n// Keep the controls awake while the pointer rests on them or a menu is open.\r\nconst SETTINGS_OPEN = \"[data-slot='media-settings'][data-open]\"\r\n\r\n/**\r\n * Hovering the control bar keeps it awake, but only on a device that can\r\n * hover. On touch, `:hover` sticks to the last tapped control, so the bar would\r\n * never sleep after a tap.\r\n */\r\nconst holdAwakeSelector = () =>\r\n window.matchMedia(\"(hover: hover)\").matches ? `[data-slot='media-controls']:hover, ${SETTINGS_OPEN}` : SETTINGS_OPEN\r\n\r\nfunction MediaRoot({\r\n className,\r\n idleTimeout = 2000,\r\n persistKey,\r\n metadata,\r\n hotkeys = true,\r\n tooltips = false,\r\n onPointerMove,\r\n onPointerDown,\r\n onPointerLeave,\r\n onKeyDown,\r\n ref,\r\n ...props\r\n}: MediaRootProps) {\r\n const [store] = React.useState(() => createMediaStore({ persistKey }))\r\n const [active, setActive] = React.useState(false)\r\n const timer = React.useRef<ReturnType<typeof setTimeout>>(undefined)\r\n const rootEl = React.useRef<HTMLDivElement>(null)\r\n\r\n const state = React.useSyncExternalStore(store.subscribe, store.getState, store.getServerState)\r\n const rootRef = useComposedRefs(ref, rootEl, store.setContainer)\r\n const options = React.useMemo(() => ({ tooltips, hotkeys }), [tooltips, hotkeys])\r\n\r\n React.useEffect(() => () => clearTimeout(timer.current), [])\r\n\r\n const { title, artist, album, artwork } = metadata ?? {}\r\n const artworkKey = JSON.stringify(artwork ?? null)\r\n React.useEffect(() => {\r\n store.setMetadata(title || artist || album ? { title, artist, album, artwork: JSON.parse(artworkKey) ?? undefined } : null)\r\n }, [store, title, artist, album, artworkKey])\r\n\r\n const wake = () => {\r\n setActive(true)\r\n clearTimeout(timer.current)\r\n timer.current = setTimeout(function sleep() {\r\n if (rootEl.current?.querySelector(holdAwakeSelector())) {\r\n timer.current = setTimeout(sleep, idleTimeout)\r\n return\r\n }\r\n setActive(false)\r\n }, idleTimeout)\r\n }\r\n\r\n const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\r\n onKeyDown?.(event)\r\n if (!hotkeys || event.defaultPrevented || event.metaKey || event.ctrlKey || event.altKey) return\r\n const target = event.target as HTMLElement\r\n if (target.closest(\"input, textarea, select, [contenteditable='true'], [data-slot='media-settings']\")) return\r\n const onButton = target.tagName === \"BUTTON\"\r\n\r\n const key = event.key.length === 1 ? event.key.toLowerCase() : event.key\r\n const keymap: Record<string, () => void> = {\r\n \" \": () => store.toggle(),\r\n k: () => store.toggle(),\r\n j: () => store.seekBy(-10),\r\n l: () => store.seekBy(10),\r\n ArrowLeft: () => store.seekBy(-5),\r\n ArrowRight: () => store.seekBy(5),\r\n ArrowUp: () => store.setVolume(store.getState().volume + 0.1),\r\n ArrowDown: () => store.setVolume(store.getState().volume - 0.1),\r\n m: () => store.toggleMute(),\r\n f: () => store.toggleFullscreen(),\r\n c: () => store.toggleCaptions(),\r\n i: () => store.togglePictureInPicture(),\r\n Home: () => store.seek(0),\r\n End: () => store.seekToRatio(1),\r\n }\r\n for (let digit = 0; digit <= 9; digit++) {\r\n keymap[String(digit)] = () => store.seekToRatio(digit / 10)\r\n }\r\n const action = keymap[key]\r\n if (!action || (key === \" \" && onButton)) return\r\n event.preventDefault()\r\n action()\r\n wake()\r\n }\r\n\r\n const idle = !active && !state.paused\r\n\r\n return (\r\n <MediaContext.Provider value={store}>\r\n <MediaOptionsContext.Provider value={options}>\r\n <div\r\n ref={rootRef}\r\n data-slot=\"media-root\"\r\n data-paused={state.paused || undefined}\r\n data-playing={!state.paused || undefined}\r\n data-started={state.started || undefined}\r\n data-waiting={state.waiting || undefined}\r\n data-fullscreen={state.fullscreen || undefined}\r\n data-error={state.error ? \"\" : undefined}\r\n data-captions={state.textTrack !== -1 && state.cues.length > 0 ? \"\" : undefined}\r\n data-idle={idle || undefined}\r\n data-active={active || undefined}\r\n className={cn(\r\n /* no gray tap flash, no long-press callout over the video, and no\r\n double-tap-zoom wait before a tap registers */\r\n \"group/media relative isolate touch-manipulation [-webkit-tap-highlight-color:transparent] [-webkit-touch-callout:none]\",\r\n idle && \"cursor-none\",\r\n className\r\n )}\r\n onPointerMove={(event) => {\r\n onPointerMove?.(event)\r\n if (event.pointerType === \"mouse\") wake()\r\n }}\r\n onPointerDown={(event) => {\r\n onPointerDown?.(event)\r\n wake()\r\n }}\r\n onPointerLeave={(event) => {\r\n onPointerLeave?.(event)\r\n\r\n /**\r\n * A finger lifting fires pointerleave too, so hiding here on touch\r\n * flashed the controls off after every tap and made the next tap\r\n * only reveal them. On touch the idle timer hides them instead.\r\n */\r\n if (event.pointerType !== \"mouse\") return\r\n clearTimeout(timer.current)\r\n setActive(false)\r\n }}\r\n onKeyDown={handleKeyDown}\r\n {...props}\r\n />\r\n </MediaOptionsContext.Provider>\r\n </MediaContext.Provider>\r\n )\r\n}\r\n\r\n/* --------------------------------- Media ---------------------------------- */\r\n\r\n/**\r\n * Attaches a source the element can't play natively (HLS, DASH, MSE).\r\n * Return a cleanup function to tear the adapter down when `src` changes.\r\n */\r\ntype MediaSourceLoader = (media: HTMLMediaElement, src: string) => void | (() => void)\r\n\r\ntype MediaElementProps<T> = T & {\r\n /** Custom source adapter, e.g. `hlsLoader` from `@/lib/media-hls`. */\r\n loader?: MediaSourceLoader\r\n}\r\n\r\nfunction useMediaElement<E extends HTMLMediaElement>(\r\n ref: React.Ref<E> | undefined,\r\n src: string | undefined,\r\n loader: MediaSourceLoader | undefined\r\n) {\r\n const store = useMediaStore()\r\n const mediaRef = useComposedRefs(ref, store.attach)\r\n\r\n React.useEffect(() => {\r\n const media = store.getMedia()\r\n if (!loader || !src || !media) return\r\n return loader(media, src) ?? undefined\r\n }, [store, loader, src])\r\n\r\n return mediaRef\r\n}\r\n\r\ntype MediaVideoProps = MediaElementProps<React.ComponentProps<\"video\">>\r\n\r\nfunction MediaVideo({ className, ref, src, loader, ...props }: MediaVideoProps) {\r\n const mediaRef = useMediaElement(ref, typeof src === \"string\" ? src : undefined, loader)\r\n return (\r\n <video\r\n ref={mediaRef}\r\n data-slot=\"media-video\"\r\n playsInline\r\n preload=\"metadata\"\r\n {...props}\r\n src={loader ? undefined : src}\r\n controls={false}\r\n className={cn(\"block\", className)}\r\n />\r\n )\r\n}\r\n\r\ntype MediaAudioProps = MediaElementProps<React.ComponentProps<\"audio\">>\r\n\r\nfunction MediaAudio({ ref, src, loader, ...props }: MediaAudioProps) {\r\n const mediaRef = useMediaElement(ref, typeof src === \"string\" ? src : undefined, loader)\r\n return (\r\n <audio\r\n ref={mediaRef}\r\n data-slot=\"media-audio\"\r\n preload=\"metadata\"\r\n {...props}\r\n src={loader ? undefined : src}\r\n controls={false}\r\n />\r\n )\r\n}\r\n\r\n/* -------------------------------- Controls -------------------------------- */\r\n\r\n/* on touch, an invisible 36px hit area around the 28px button; looks the same */\r\nconst buttonClass =\r\n \"pointer-coarse:after:absolute pointer-coarse:after:-inset-1 pointer-coarse:after:content-[''] group/media-button relative inline-grid size-7 shrink-0 cursor-pointer place-items-center rounded-md outline-none transition-[background-color,scale,opacity] duration-150 ease-media hover:bg-current/15 focus-visible:ring-2 focus-visible:ring-current/60 motion-safe:enabled:active:scale-[0.96] disabled:pointer-events-none disabled:opacity-50 motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0\"\r\n\r\ntype MediaTooltipAlign = \"start\" | \"center\" | \"end\"\r\n\r\n/**\r\n * The label over a control. Decorative: the control already carries the same\r\n * text as its accessible name. Hover shows it after a short delay and hides it\r\n * at once; keyboard focus shows it straight away. Touch never shows it, since\r\n * the hover variants only apply to pointers that can hover.\r\n */\r\nfunction MediaTooltip({ label, shortcut, align = \"center\" }: { label: React.ReactNode; shortcut?: string; align?: MediaTooltipAlign }) {\r\n const { tooltips, hotkeys } = React.useContext(MediaOptionsContext)\r\n if (!tooltips || !label) return null\r\n return (\r\n <span\r\n aria-hidden=\"true\"\r\n data-slot=\"media-tooltip\"\r\n className={cn(\r\n \"pointer-events-none absolute bottom-full mb-2 flex items-center gap-1.5 rounded-md bg-foreground px-1.5 py-0.5 text-[11px] leading-4 font-medium whitespace-nowrap text-background opacity-0\",\r\n \"transition-[opacity,translate] duration-150 ease-media motion-safe:translate-y-0.5 motion-reduce:transition-none\",\r\n \"group-hover/media-button:translate-y-0 group-hover/media-button:opacity-100 group-hover/media-button:delay-300\",\r\n \"group-focus-visible/media-button:translate-y-0 group-focus-visible/media-button:opacity-100\",\r\n \"group-aria-expanded/media-button:hidden\",\r\n align === \"start\" && \"left-0\",\r\n align === \"center\" && \"left-1/2 -translate-x-1/2\",\r\n align === \"end\" && \"right-0\"\r\n )}\r\n >\r\n {label}\r\n {hotkeys && shortcut && <kbd className=\"font-mono opacity-60\">{shortcut}</kbd>}\r\n </span>\r\n )\r\n}\r\n\r\ntype MediaButtonProps = React.ComponentProps<\"button\"> & {\r\n /** Tooltip text, shown when the root has `tooltips`. `false` hides it. */\r\n tooltip?: React.ReactNode\r\n /** Shortcut shown beside the tooltip when the root has `hotkeys`. */\r\n shortcut?: string\r\n /** Pin the tooltip to an edge for buttons at either end of a bar. */\r\n tooltipAlign?: MediaTooltipAlign\r\n}\r\n\r\nfunction MediaButton({ className, type = \"button\", tooltip, shortcut, tooltipAlign, children, ...props }: MediaButtonProps) {\r\n return (\r\n <button type={type} data-slot=\"media-button\" className={cn(buttonClass, className)} {...props}>\r\n {children}\r\n <MediaTooltip label={tooltip} shortcut={shortcut} align={tooltipAlign} />\r\n </button>\r\n )\r\n}\r\n\r\nfunction IconSwap({ show, a, b }: { show: \"a\" | \"b\"; a: React.ReactNode; b: React.ReactNode }) {\r\n const layer =\r\n \"col-start-1 row-start-1 grid place-items-center transition-[opacity,scale,filter] duration-200 ease-media motion-reduce:transition-none\"\r\n return (\r\n <span className=\"grid\">\r\n <span className={cn(layer, show === \"a\" ? \"opacity-100\" : \"scale-50 opacity-0 blur-[2px]\")}>{a}</span>\r\n <span className={cn(layer, show === \"b\" ? \"opacity-100\" : \"scale-50 opacity-0 blur-[2px]\")}>{b}</span>\r\n </span>\r\n )\r\n}\r\n\r\nfunction MediaPlayButton({ onClick, ...props }: MediaButtonProps) {\r\n const store = useMediaStore()\r\n const paused = useMedia((s) => s.paused)\r\n const hasSource = useMedia((s) => s.hasSource)\r\n return (\r\n <MediaButton\r\n aria-label={paused ? \"Play\" : \"Pause\"}\r\n tooltip={paused ? \"Play\" : \"Pause\"}\r\n shortcut=\"K\"\r\n disabled={!hasSource}\r\n onClick={(event) => {\r\n onClick?.(event)\r\n store.toggle()\r\n }}\r\n {...props}\r\n >\r\n <IconSwap show={paused ? \"a\" : \"b\"} a={<PlayIcon />} b={<PauseIcon />} />\r\n </MediaButton>\r\n )\r\n}\r\n\r\nfunction MediaMuteButton({ onClick, ...props }: MediaButtonProps) {\r\n const store = useMediaStore()\r\n const muted = useMedia((s) => s.muted || s.volume === 0)\r\n return (\r\n <MediaButton\r\n aria-label={muted ? \"Unmute\" : \"Mute\"}\r\n tooltip={muted ? \"Unmute\" : \"Mute\"}\r\n shortcut=\"M\"\r\n onClick={(event) => {\r\n onClick?.(event)\r\n store.toggleMute()\r\n }}\r\n {...props}\r\n >\r\n <VolumeIcon muted={muted} />\r\n </MediaButton>\r\n )\r\n}\r\n\r\nfunction MediaFullscreenButton({ onClick, ...props }: MediaButtonProps) {\r\n const store = useMediaStore()\r\n const fullscreen = useMedia((s) => s.fullscreen)\r\n return (\r\n <MediaButton\r\n aria-label={fullscreen ? \"Exit fullscreen\" : \"Enter fullscreen\"}\r\n tooltip={fullscreen ? \"Exit fullscreen\" : \"Fullscreen\"}\r\n shortcut=\"F\"\r\n onClick={(event) => {\r\n onClick?.(event)\r\n store.toggleFullscreen()\r\n }}\r\n {...props}\r\n >\r\n <IconSwap show={fullscreen ? \"b\" : \"a\"} a={<EnterFullscreenIcon />} b={<ExitFullscreenIcon />} />\r\n </MediaButton>\r\n )\r\n}\r\n\r\nconst subscribeNoop = () => () => {}\r\n\r\nfunction MediaPictureInPictureButton({ onClick, ...props }: MediaButtonProps) {\r\n const store = useMediaStore()\r\n const active = useMedia((s) => s.pictureInPicture)\r\n const supported = React.useSyncExternalStore(\r\n subscribeNoop,\r\n () => \"pictureInPictureEnabled\" in document && document.pictureInPictureEnabled,\r\n () => false\r\n )\r\n if (!supported) return null\r\n return (\r\n <MediaButton\r\n aria-label={active ? \"Exit picture in picture\" : \"Enter picture in picture\"}\r\n aria-pressed={active}\r\n tooltip={active ? \"Exit picture in picture\" : \"Picture in picture\"}\r\n shortcut=\"I\"\r\n onClick={(event) => {\r\n onClick?.(event)\r\n store.togglePictureInPicture()\r\n }}\r\n {...props}\r\n >\r\n <PictureInPictureIcon />\r\n </MediaButton>\r\n )\r\n}\r\n\r\ntype MediaPlaybackRateButtonProps = MediaButtonProps & {\r\n /** Speeds a click cycles through, in order. */\r\n rates?: number[]\r\n}\r\n\r\nfunction MediaPlaybackRateButton({\r\n rates = [1, 1.25, 1.5, 2, 0.75],\r\n className,\r\n style,\r\n onClick,\r\n ...props\r\n}: MediaPlaybackRateButtonProps) {\r\n const store = useMediaStore()\r\n const rate = useMedia((s) => s.playbackRate)\r\n\r\n /**\r\n * Sized for the longest label, so cycling 1x to 0.75x never moves the\r\n * controls beside it. The font is mono, so characters measure exactly. A\r\n * variable rather than an inline width, so a w-* class still overrides it.\r\n */\r\n const chars = Math.max(...[...rates, rate].map((r) => `${r}×`.length))\r\n return (\r\n <MediaButton\r\n aria-label={`Playback speed ${rate}x`}\r\n tooltip=\"Playback speed\"\r\n dir=\"ltr\"\r\n className={cn(\"w-(--media-rate-width) min-w-7 px-1.5 font-mono text-xs tabular-nums\", className)}\r\n style={{ \"--media-rate-width\": `calc(${chars}ch + 0.75rem)`, ...style } as React.CSSProperties}\r\n onClick={(event) => {\r\n onClick?.(event)\r\n const index = rates.indexOf(rate)\r\n store.setPlaybackRate(rates[(index + 1) % rates.length])\r\n }}\r\n {...props}\r\n >\r\n {`${rate}×`}\r\n </MediaButton>\r\n )\r\n}\r\n\r\n/* -------------------------------- Controls -------------------------------- */\r\n\r\n/**\r\n * Bar that reveals while the pointer is active, playback is paused,\r\n * or keyboard focus is inside the player.\r\n */\r\ntype MediaControlsProps = React.ComponentProps<\"div\">\r\n\r\nfunction MediaControls({ className, ...props }: MediaControlsProps) {\r\n return (\r\n <div\r\n data-slot=\"media-controls\"\r\n className={cn(\r\n \"flex items-center transition-[opacity,translate] duration-200 ease-media motion-reduce:transition-none\",\r\n \"pointer-events-none translate-y-2 opacity-0\",\r\n \"group-data-active/media:pointer-events-auto group-data-active/media:translate-y-0 group-data-active/media:opacity-100\",\r\n \"group-has-focus-visible/media:pointer-events-auto group-has-focus-visible/media:translate-y-0 group-has-focus-visible/media:opacity-100\",\r\n \"group-data-paused/media:group-data-started/media:pointer-events-auto group-data-paused/media:group-data-started/media:translate-y-0 group-data-paused/media:group-data-started/media:opacity-100\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\n/* --------------------------------- Surface -------------------------------- */\r\n\r\ntype MediaSurfaceProps = React.ComponentProps<\"button\"> & {\r\n /** Seconds a double-tap on either half seeks by. 0 disables it. */\r\n doubleTapSeek?: number\r\n /** Double-click with a mouse toggles fullscreen. */\r\n doubleClickFullscreen?: boolean\r\n /** Classes for the \"±10s\" bubble shown after a double-tap seek. */\r\n indicatorClassName?: string\r\n}\r\n\r\nconst DOUBLE_TAP_MS = 280\r\n\r\n/**\r\n * Full-bleed click target under the controls. Click toggles playback; on touch,\r\n * the first tap reveals controls and a double-tap on either half seeks.\r\n */\r\nfunction MediaSurface({\r\n className,\r\n doubleTapSeek = 10,\r\n doubleClickFullscreen = true,\r\n indicatorClassName,\r\n onClick,\r\n onDoubleClick,\r\n onPointerDown,\r\n children,\r\n ...props\r\n}: MediaSurfaceProps) {\r\n const store = useMediaStore()\r\n const paused = useMedia((s) => s.paused)\r\n const pointer = React.useRef({ type: \"mouse\", revealOnly: false, lastTap: 0 })\r\n const tapTimer = React.useRef<ReturnType<typeof setTimeout>>(undefined)\r\n const flashTimer = React.useRef<ReturnType<typeof setTimeout>>(undefined)\r\n const [flash, setFlash] = React.useState<{ side: \"back\" | \"forward\"; count: number } | null>(null)\r\n\r\n React.useEffect(\r\n () => () => {\r\n clearTimeout(tapTimer.current)\r\n clearTimeout(flashTimer.current)\r\n },\r\n []\r\n )\r\n\r\n const showFlash = (side: \"back\" | \"forward\") => {\r\n setFlash((prev) => ({ side, count: prev?.side === side ? prev.count + 1 : 1 }))\r\n clearTimeout(flashTimer.current)\r\n flashTimer.current = setTimeout(() => setFlash(null), 650)\r\n }\r\n\r\n const handleTouchTap = (event: React.MouseEvent<HTMLButtonElement>) => {\r\n const now = performance.now()\r\n const { revealOnly, lastTap } = pointer.current\r\n if (doubleTapSeek > 0 && now - lastTap < DOUBLE_TAP_MS) {\r\n clearTimeout(tapTimer.current)\r\n pointer.current.lastTap = now\r\n const rect = event.currentTarget.getBoundingClientRect()\r\n const side = event.clientX - rect.left < rect.width / 2 ? \"back\" : \"forward\"\r\n store.seekBy(side === \"back\" ? -doubleTapSeek : doubleTapSeek)\r\n showFlash(side)\r\n return\r\n }\r\n pointer.current.lastTap = now\r\n if (revealOnly) return\r\n // Wait to see whether this tap becomes a double-tap before toggling.\r\n clearTimeout(tapTimer.current)\r\n tapTimer.current = setTimeout(() => store.toggle(), doubleTapSeek > 0 ? DOUBLE_TAP_MS : 0)\r\n }\r\n\r\n return (\r\n <button\r\n type=\"button\"\r\n data-slot=\"media-surface\"\r\n aria-label={paused ? \"Play\" : \"Pause\"}\r\n className={cn(\"absolute inset-0 cursor-[inherit] outline-none\", className)}\r\n onPointerDown={(event) => {\r\n onPointerDown?.(event)\r\n const root = event.currentTarget.closest(\"[data-slot='media-root']\")\r\n pointer.current.type = event.pointerType\r\n // On touch, the first tap while playing reveals controls instead of pausing.\r\n pointer.current.revealOnly =\r\n event.pointerType !== \"mouse\" && !store.getState().paused && !root?.hasAttribute(\"data-active\")\r\n }}\r\n onClick={(event) => {\r\n onClick?.(event)\r\n // Keyboard activation reports detail 0 and always toggles.\r\n if (event.detail !== 0 && pointer.current.type !== \"mouse\") {\r\n handleTouchTap(event)\r\n return\r\n }\r\n store.toggle()\r\n }}\r\n onDoubleClick={(event) => {\r\n onDoubleClick?.(event)\r\n if (doubleClickFullscreen && pointer.current.type === \"mouse\") store.toggleFullscreen()\r\n }}\r\n {...props}\r\n >\r\n {doubleTapSeek > 0 && (\r\n <span aria-hidden=\"true\" className=\"pointer-events-none absolute inset-0 flex items-center justify-between px-[8%]\">\r\n {([\"back\", \"forward\"] as const).map((side) => (\r\n <span\r\n key={side}\r\n data-show={flash?.side === side || undefined}\r\n className={cn(\r\n \"flex items-center gap-1.5 rounded-full px-3 py-1.5 font-mono text-xs tabular-nums opacity-0 scale-90 transition-[opacity,scale] duration-200 ease-media data-show:scale-100 data-show:opacity-100 motion-reduce:transition-none\",\r\n side === \"forward\" && \"flex-row-reverse\",\r\n indicatorClassName\r\n )}\r\n >\r\n <SeekIcon direction={side} className=\"size-3.5\" />\r\n {`${(flash?.side === side ? flash.count : 1) * doubleTapSeek}s`}\r\n </span>\r\n ))}\r\n </span>\r\n )}\r\n {children}\r\n </button>\r\n )\r\n}\r\n\r\n/* ---------------------------------- Seek ---------------------------------- */\r\n\r\ntype MediaSeekProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\r\n /** Seconds moved per arrow key press. */\r\n step?: number\r\n /** Show a time tooltip while hovering. */\r\n preview?: boolean\r\n previewClassName?: string\r\n}\r\n\r\nfunction MediaSeek({\r\n className,\r\n step = 5,\r\n preview = true,\r\n previewClassName,\r\n ...props\r\n}: MediaSeekProps) {\r\n const store = useMediaStore()\r\n const duration = useMedia((s) => s.duration)\r\n const bufferedEnd = useMedia((s) => s.bufferedEnd)\r\n const currentTime = useMedia((s) => Math.floor(s.currentTime))\r\n const fillRef = React.useRef<HTMLDivElement>(null)\r\n const scrub = React.useRef<{ resume: boolean } | null>(null)\r\n const [hover, setHover] = React.useState<number | null>(null)\r\n\r\n React.useEffect(() => {\r\n let frame = 0\r\n const draw = () => {\r\n cancelAnimationFrame(frame)\r\n const media = store.getMedia()\r\n const { duration, paused } = store.getState()\r\n if (!scrub.current && fillRef.current) {\r\n const progress = duration ? clamp((media?.currentTime ?? 0) / duration) : 0\r\n fillRef.current.style.transform = `scaleX(${progress})`\r\n }\r\n if (!paused) frame = requestAnimationFrame(draw)\r\n }\r\n draw()\r\n const unsubscribe = store.subscribe(draw)\r\n return () => {\r\n unsubscribe()\r\n cancelAnimationFrame(frame)\r\n }\r\n }, [store])\r\n\r\n const ratioFromEvent = (event: React.PointerEvent<HTMLDivElement>) => {\r\n const rect = event.currentTarget.getBoundingClientRect()\r\n return clamp((event.clientX - rect.left) / rect.width)\r\n }\r\n\r\n const seekToRatio = (ratio: number) => {\r\n if (fillRef.current) fillRef.current.style.transform = `scaleX(${ratio})`\r\n store.seek(ratio * store.getState().duration)\r\n }\r\n\r\n const endScrub = (event: React.PointerEvent<HTMLDivElement>) => {\r\n if (!scrub.current) return\r\n if (event.currentTarget.hasPointerCapture(event.pointerId)) {\r\n event.currentTarget.releasePointerCapture(event.pointerId)\r\n }\r\n const { resume } = scrub.current\r\n scrub.current = null\r\n if (resume) store.play()\r\n }\r\n\r\n return (\r\n <div\r\n role=\"slider\"\r\n tabIndex={0}\r\n aria-label=\"Seek\"\r\n aria-valuemin={0}\r\n aria-valuemax={Math.floor(duration)}\r\n aria-valuenow={currentTime}\r\n aria-valuetext={`${formatTime(currentTime)} of ${formatTime(duration)}`}\r\n data-slot=\"media-seek\"\r\n className={cn(\r\n /* on touch, an invisible 44px-tall hit area around the 20px bar */\r\n \"group/seek relative flex h-5 min-w-0 grow cursor-pointer touch-none items-center rounded-full outline-none select-none pointer-coarse:before:absolute pointer-coarse:before:inset-x-0 pointer-coarse:before:-inset-y-3 pointer-coarse:before:content-['']\",\r\n className\r\n )}\r\n onPointerDown={(event) => {\r\n if (event.button !== 0) return\r\n event.currentTarget.setPointerCapture(event.pointerId)\r\n scrub.current = { resume: !store.getState().paused }\r\n store.pause()\r\n seekToRatio(ratioFromEvent(event))\r\n }}\r\n onPointerMove={(event) => {\r\n const ratio = ratioFromEvent(event)\r\n if (event.pointerType === \"mouse\") setHover(ratio)\r\n if (scrub.current) seekToRatio(ratio)\r\n }}\r\n onPointerUp={endScrub}\r\n\r\n /**\r\n * The system can take a touch away mid-drag (an edge swipe, an incoming\r\n * call). Without this the bar stayed in scrub mode: paused, with the\r\n * fill frozen, because the draw loop skips while scrubbing.\r\n */\r\n onPointerCancel={endScrub}\r\n onPointerLeave={() => setHover(null)}\r\n onKeyDown={(event) => {\r\n const { currentTime, duration } = store.getState()\r\n const moves: Record<string, number> = {\r\n ArrowLeft: currentTime - step,\r\n ArrowDown: currentTime - step,\r\n ArrowRight: currentTime + step,\r\n ArrowUp: currentTime + step,\r\n PageDown: currentTime - duration / 10,\r\n PageUp: currentTime + duration / 10,\r\n Home: 0,\r\n End: duration,\r\n }\r\n if (!(event.key in moves)) return\r\n event.preventDefault()\r\n event.stopPropagation()\r\n store.seek(moves[event.key])\r\n }}\r\n {...props}\r\n >\r\n <div className=\"relative h-1 w-full overflow-hidden rounded-full bg-current/25 transition-[height] duration-150 ease-media group-hover/seek:h-1.5 group-focus-visible/seek:h-1.5 group-active/seek:h-1.5 motion-reduce:transition-none\">\r\n <div\r\n className=\"absolute inset-0 origin-left bg-current/25 transition-transform duration-300 ease-media\"\r\n style={{ transform: `scaleX(${duration ? clamp(bufferedEnd / duration) : 0})` }}\r\n />\r\n <div ref={fillRef} className=\"absolute inset-0 origin-left bg-current\" style={{ transform: \"scaleX(0)\" }} />\r\n </div>\r\n <span className=\"pointer-events-none absolute inset-0 rounded-full ring-current/60 group-focus-visible/seek:ring-2\" />\r\n {preview && hover !== null && duration > 0 && (\r\n <span\r\n aria-hidden\r\n className={cn(\r\n \"pointer-events-none absolute bottom-full mb-1.5 -translate-x-1/2 rounded-md bg-foreground px-1.5 py-0.5 font-mono text-[11px] leading-4 text-background tabular-nums\",\r\n previewClassName\r\n )}\r\n style={{ left: `${hover * 100}%` }}\r\n >\r\n {formatTime(hover * duration, duration)}\r\n </span>\r\n )}\r\n </div>\r\n )\r\n}\r\n\r\n/* --------------------------------- Volume --------------------------------- */\r\n\r\ntype MediaVolumeProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\r\n /** \"vertical\" fills from the bottom, for a slider in a popover. */\r\n orientation?: \"horizontal\" | \"vertical\"\r\n}\r\n\r\nfunction MediaVolume({ orientation = \"horizontal\", className, ...props }: MediaVolumeProps) {\r\n const store = useMediaStore()\r\n const volume = useMedia((s) => (s.muted ? 0 : s.volume))\r\n const dragging = React.useRef(false)\r\n const vertical = orientation === \"vertical\"\r\n\r\n const setFromEvent = (event: React.PointerEvent<HTMLDivElement>) => {\r\n const rect = event.currentTarget.getBoundingClientRect()\r\n store.setVolume(\r\n clamp(vertical ? (rect.bottom - event.clientY) / rect.height : (event.clientX - rect.left) / rect.width)\r\n )\r\n }\r\n\r\n return (\r\n <div\r\n role=\"slider\"\r\n tabIndex={0}\r\n aria-label=\"Volume\"\r\n aria-valuemin={0}\r\n aria-valuemax={100}\r\n aria-valuenow={Math.round(volume * 100)}\r\n aria-valuetext={`${Math.round(volume * 100)}%`}\r\n aria-orientation={orientation}\r\n data-slot=\"media-volume\"\r\n className={cn(\r\n \"group/volume relative flex shrink-0 cursor-pointer touch-none outline-none select-none\",\r\n vertical ? \"h-16 w-5 justify-center\" : \"h-5 w-16 items-center\",\r\n className\r\n )}\r\n onPointerDown={(event) => {\r\n if (event.button !== 0) return\r\n event.currentTarget.setPointerCapture(event.pointerId)\r\n dragging.current = true\r\n setFromEvent(event)\r\n }}\r\n onPointerMove={(event) => dragging.current && setFromEvent(event)}\r\n onPointerUp={(event) => {\r\n dragging.current = false\r\n event.currentTarget.releasePointerCapture(event.pointerId)\r\n }}\r\n\r\n /* a drag the system takes away must end here too, or it stays live */\r\n onPointerCancel={() => {\r\n dragging.current = false\r\n }}\r\n onKeyDown={(event) => {\r\n const moves: Record<string, number> = {\r\n ArrowLeft: volume - 0.1,\r\n ArrowDown: volume - 0.1,\r\n ArrowRight: volume + 0.1,\r\n ArrowUp: volume + 0.1,\r\n PageDown: volume - 0.25,\r\n PageUp: volume + 0.25,\r\n Home: 0,\r\n End: 1,\r\n }\r\n if (!(event.key in moves)) return\r\n event.preventDefault()\r\n event.stopPropagation()\r\n store.setVolume(moves[event.key])\r\n }}\r\n {...props}\r\n >\r\n <div\r\n className={cn(\r\n \"relative overflow-hidden rounded-full bg-current/25 duration-150 ease-media motion-reduce:transition-none\",\r\n vertical\r\n ? \"h-full w-1 transition-[width] group-hover/volume:w-1.5 group-focus-visible/volume:w-1.5\"\r\n : \"h-1 w-full transition-[height] group-hover/volume:h-1.5 group-focus-visible/volume:h-1.5\"\r\n )}\r\n >\r\n <div\r\n className={cn(\"absolute inset-0 bg-current\", vertical ? \"origin-bottom\" : \"origin-left\")}\r\n style={{ transform: vertical ? `scaleY(${volume})` : `scaleX(${volume})` }}\r\n />\r\n </div>\r\n <span className=\"pointer-events-none absolute inset-0 rounded-full ring-current/60 group-focus-visible/volume:ring-2\" />\r\n </div>\r\n )\r\n}\r\n\r\n/* ---------------------------------- Time ---------------------------------- */\r\n\r\ntype MediaTimeProps = React.ComponentProps<\"span\"> & {\r\n type?: \"current\" | \"duration\" | \"remaining\"\r\n /** Click to show remaining time and back. \"remaining\" swaps with elapsed. */\r\n toggle?: boolean\r\n}\r\n\r\nconst timeNames = { current: \"elapsed\", duration: \"duration\", remaining: \"remaining\" } as const\r\n\r\nfunction MediaTime({ type = \"current\", toggle = false, className, ...props }: MediaTimeProps) {\r\n const [picked, setPicked] = React.useState(type)\r\n const shown = toggle ? picked : type\r\n const duration = useMedia((s) => s.duration)\r\n\r\n /**\r\n * Elapsed rounds down and remaining rounds up, so the two add up to the\r\n * duration. A clip under a second shows 0:01, not 0:00.\r\n */\r\n const value = useMedia((s) => {\r\n if (shown === \"duration\") return Math.floor(s.duration) || Math.ceil(s.duration)\r\n if (shown === \"remaining\") return Math.ceil(Math.max(s.duration - s.currentTime, 0))\r\n return Math.floor(s.currentTime)\r\n })\r\n const time = formatTime(value, duration)\r\n const text = shown === \"remaining\" ? `-${time}` : time\r\n const other = type === \"remaining\" ? \"current\" : \"remaining\"\r\n const next = shown === type ? other : type\r\n\r\n return (\r\n <span data-slot=\"media-time\" dir=\"ltr\" className={cn(\"font-mono text-xs tabular-nums\", className)} {...props}>\r\n {toggle ? (\r\n <button\r\n type=\"button\"\r\n aria-label={`${shown === \"remaining\" ? \"Time remaining\" : shown === \"duration\" ? \"Duration\" : \"Time elapsed\"} ${time}`}\r\n onClick={() => setPicked(next)}\r\n className=\"group/media-button relative cursor-pointer rounded-sm outline-none transition-colors duration-150 ease-media hover:text-current focus-visible:ring-2 focus-visible:ring-current/60\"\r\n >\r\n {text}\r\n <MediaTooltip label={`Show ${timeNames[next]}`} />\r\n </button>\r\n ) : (\r\n text\r\n )}\r\n </span>\r\n )\r\n}\r\n\r\n/* --------------------------------- Loading -------------------------------- */\r\n\r\ntype MediaLoadingProps = React.ComponentProps<\"span\">\r\n\r\nfunction MediaLoading({ className, ...props }: MediaLoadingProps) {\r\n const waiting = useMedia((s) => s.waiting && !s.paused)\r\n return (\r\n <span\r\n role=\"status\"\r\n aria-label={waiting ? \"Loading\" : undefined}\r\n data-slot=\"media-loading\"\r\n className={cn(\r\n \"pointer-events-none size-6 rounded-full border-2 border-current/25 border-t-current transition-opacity duration-200 ease-media\",\r\n waiting ? \"animate-spin opacity-100 delay-300\" : \"opacity-0\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\n/* -------------------------------- Captions -------------------------------- */\r\n\r\n/**\r\n * Renders active caption cues. Native rendering is disabled so cues can be\r\n * styled and can slide above the controls when they appear.\r\n */\r\ntype MediaCaptionsProps = React.ComponentProps<\"div\">\r\n\r\nfunction MediaCaptions({ className, ...props }: MediaCaptionsProps) {\r\n const cues = useMedia((s) => (s.textTrack === -1 ? emptyCues : s.cues))\r\n if (!cues.length) return null\r\n return (\r\n <div\r\n data-slot=\"media-captions\"\r\n className={cn(\"pointer-events-none flex flex-col items-center gap-1 text-center\", className)}\r\n {...props}\r\n >\r\n {cues.map((cue, i) => (\r\n <span key={`${i}-${cue}`} dir=\"auto\" className=\"whitespace-pre-line text-balance rounded-md px-2 py-0.5 box-decoration-clone\">\r\n {cue}\r\n </span>\r\n ))}\r\n </div>\r\n )\r\n}\r\n\r\nconst emptyCues: string[] = []\r\n\r\nfunction MediaCaptionsButton({ onClick, ...props }: MediaButtonProps) {\r\n const store = useMediaStore()\r\n const available = useMedia((s) => s.textTracks.length > 0)\r\n const on = useMedia((s) => s.textTrack !== -1)\r\n if (!available) return null\r\n return (\r\n <MediaButton\r\n aria-label=\"Captions\"\r\n aria-pressed={on}\r\n tooltip={on ? \"Captions off\" : \"Captions\"}\r\n shortcut=\"C\"\r\n onClick={(event) => {\r\n onClick?.(event)\r\n store.toggleCaptions()\r\n }}\r\n {...props}\r\n >\r\n <CaptionsIcon active={on} />\r\n </MediaButton>\r\n )\r\n}\r\n\r\n/* -------------------------------- Settings -------------------------------- */\r\n\r\ntype MediaSettingsProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\r\n rates?: number[]\r\n /** Classes for the floating panel. */\r\n panelClassName?: string\r\n}\r\n\r\n/**\r\n * Popover with playback speed and caption track choices. Arrow keys move\r\n * between options; Escape or a click outside closes it.\r\n */\r\nfunction MediaSettings({ rates = [0.5, 0.75, 1, 1.25, 1.5, 2], className, panelClassName, ...props }: MediaSettingsProps) {\r\n const store = useMediaStore()\r\n const rate = useMedia((s) => s.playbackRate)\r\n const tracks = useMedia((s) => s.textTracks)\r\n const textTrack = useMedia((s) => s.textTrack)\r\n const [open, setOpen] = React.useState(false)\r\n const rootEl = React.useRef<HTMLDivElement>(null)\r\n const triggerEl = React.useRef<HTMLButtonElement>(null)\r\n const panelEl = React.useRef<HTMLDivElement>(null)\r\n const listEl = React.useRef<HTMLDivElement>(null)\r\n const panelId = React.useId()\r\n\r\n /**\r\n * The player clips its children, so on a short player (a phone in portrait)\r\n * a full-height panel lost its top rows. Cap the panel to the space above\r\n * the trigger and let it scroll, and re-measure when the player resizes.\r\n */\r\n React.useLayoutEffect(() => {\r\n const panel = panelEl.current\r\n const list = listEl.current\r\n const trigger = triggerEl.current\r\n const player = rootEl.current?.closest<HTMLElement>(\"[data-slot='media-root']\")\r\n if (!open || !panel || !list || !trigger || !player) return\r\n\r\n /**\r\n * A hidden scrollbar gives no sign of more options, so the list's bottom\r\n * edge fades. The fade is the distance left to scroll, capped at 24px, so\r\n * it eases out with the scroll instead of snapping off at the end. It sits\r\n * on the inner list, so the panel's edge and background stay whole.\r\n */\r\n const edge = () => {\r\n const left = Math.min(24, list.scrollHeight - list.clientHeight - list.scrollTop)\r\n list.style.maskImage = left > 1 ? `linear-gradient(to bottom, black calc(100% - ${left}px), rgb(0 0 0 / 0.3))` : \"\"\r\n }\r\n const fit = () => {\r\n const space = trigger.getBoundingClientRect().top - player.getBoundingClientRect().top - 16\r\n panel.style.maxHeight = `${Math.max(96, Math.min(288, space))}px`\r\n edge()\r\n }\r\n fit()\r\n const observer = new ResizeObserver(fit)\r\n observer.observe(player)\r\n list.addEventListener(\"scroll\", edge, { passive: true })\r\n return () => {\r\n observer.disconnect()\r\n list.removeEventListener(\"scroll\", edge)\r\n }\r\n }, [open])\r\n\r\n React.useEffect(() => {\r\n if (!open) return\r\n const onPointerDown = (event: PointerEvent) => {\r\n if (!rootEl.current?.contains(event.target as Node)) setOpen(false)\r\n }\r\n document.addEventListener(\"pointerdown\", onPointerDown)\r\n rootEl.current?.querySelector<HTMLElement>(\"[aria-checked='true']\")?.focus()\r\n return () => document.removeEventListener(\"pointerdown\", onPointerDown)\r\n }, [open])\r\n\r\n const close = () => {\r\n setOpen(false)\r\n triggerEl.current?.focus()\r\n }\r\n\r\n const onPanelKeyDown = (event: React.KeyboardEvent) => {\r\n if (event.key === \"Escape\") {\r\n event.stopPropagation()\r\n close()\r\n return\r\n }\r\n const step = { ArrowDown: 1, ArrowRight: 1, ArrowUp: -1, ArrowLeft: -1 }[event.key]\r\n if (!step) return\r\n event.preventDefault()\r\n event.stopPropagation()\r\n const items = Array.from(rootEl.current?.querySelectorAll<HTMLElement>(\"[role='menuitemradio']\") ?? [])\r\n const index = items.indexOf(document.activeElement as HTMLElement)\r\n const next = items[(index + step + items.length) % items.length]\r\n next?.focus()\r\n }\r\n\r\n const option = (label: string, checked: boolean, select: () => void) => (\r\n <button\r\n key={label}\r\n type=\"button\"\r\n role=\"menuitemradio\"\r\n aria-checked={checked}\r\n tabIndex={checked ? 0 : -1}\r\n onClick={() => {\r\n select()\r\n close()\r\n }}\r\n className=\"flex min-h-8 w-full shrink-0 cursor-pointer items-center justify-between gap-3 rounded-md px-2 py-1.5 text-left text-sm leading-5 outline-none transition-colors duration-150 ease-media hover:bg-current/10 focus-visible:bg-current/15\"\r\n >\r\n <span dir=\"auto\" className=\"min-w-0\">\r\n {label}\r\n </span>\r\n <CheckIcon className={cn(\"size-3.5 shrink-0 transition-opacity duration-150\", checked ? \"opacity-100\" : \"opacity-0\")} />\r\n </button>\r\n )\r\n\r\n return (\r\n <div\r\n ref={rootEl}\r\n data-slot=\"media-settings\"\r\n data-open={open || undefined}\r\n className={cn(\"relative\", className)}\r\n {...props}\r\n >\r\n <MediaButton\r\n ref={triggerEl}\r\n aria-label=\"Settings\"\r\n tooltip=\"Settings\"\r\n aria-haspopup=\"menu\"\r\n aria-expanded={open}\r\n aria-controls={open ? panelId : undefined}\r\n onClick={() => setOpen((o) => !o)}\r\n >\r\n <SettingsIcon className={cn(\"transition-transform duration-300 ease-media motion-reduce:transition-none\", open && \"rotate-90\")} />\r\n </MediaButton>\r\n {open && (\r\n <div\r\n ref={panelEl}\r\n id={panelId}\r\n role=\"menu\"\r\n aria-label=\"Playback settings\"\r\n onKeyDown={onPanelKeyDown}\r\n className={cn(\r\n \"absolute right-0 bottom-full z-10 mb-2 flex max-h-72 w-48 origin-bottom-right flex-col overflow-hidden rounded-xl\",\r\n \"transition-[opacity,translate,scale] duration-150 ease-media starting:translate-y-1 starting:scale-95 starting:opacity-0 motion-reduce:transition-none\",\r\n panelClassName\r\n )}\r\n >\r\n <div\r\n ref={listEl}\r\n className=\"flex min-h-0 flex-col gap-2 overflow-y-auto overscroll-contain p-1.5 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\"\r\n >\r\n <div role=\"group\" aria-label=\"Speed\" className=\"flex flex-col\">\r\n <span className=\"px-2 pt-1 pb-1.5 text-xs opacity-60\">Speed</span>\r\n <div className=\"grid grid-cols-3 gap-1\">\r\n {rates.map((r) => (\r\n <button\r\n key={r}\r\n type=\"button\"\r\n role=\"menuitemradio\"\r\n aria-checked={rate === r}\r\n aria-label={r === 1 ? \"Normal speed\" : `${r}× speed`}\r\n tabIndex={rate === r ? 0 : -1}\r\n onClick={() => {\r\n store.setPlaybackRate(r)\r\n close()\r\n }}\r\n className={cn(\r\n \"h-8 cursor-pointer rounded-md text-sm tabular-nums outline-none transition-colors duration-150 ease-media hover:bg-current/10 focus-visible:ring-1 focus-visible:ring-current/40\",\r\n rate === r && \"bg-current/15\"\r\n )}\r\n >\r\n {`${r}×`}\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n {tracks.length > 0 && (\r\n <div role=\"group\" aria-label=\"Captions\" className=\"flex flex-col\">\r\n <span className=\"px-2 pt-1 pb-1.5 text-xs opacity-60\">Captions</span>\r\n {option(\"Off\", textTrack === -1, () => store.setTextTrack(-1))}\r\n {tracks.map((track, i) => option(track.label, textTrack === i, () => store.setTextTrack(i)))}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n )\r\n}\r\n\r\n/* ---------------------------------- Error --------------------------------- */\r\n\r\ntype MediaErrorMessageProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\r\n /** Override the default message for a given MediaError. */\r\n message?: (error: MediaError) => React.ReactNode\r\n}\r\n\r\nfunction MediaErrorMessage({ className, message, ...props }: MediaErrorMessageProps) {\r\n const store = useMediaStore()\r\n const error = useMedia((s) => s.error)\r\n if (!error) return null\r\n return (\r\n <div\r\n role=\"alert\"\r\n data-slot=\"media-error\"\r\n className={cn(\"flex flex-col items-center justify-center gap-3 text-center\", className)}\r\n {...props}\r\n >\r\n <p className=\"text-pretty text-sm\">{message ? message(error) : describeMediaError(error)}</p>\r\n <button\r\n type=\"button\"\r\n onClick={() => store.retry()}\r\n className=\"inline-flex h-8 shrink-0 cursor-pointer items-center gap-1.5 rounded-full px-3 text-sm whitespace-nowrap outline-none ring-1 ring-current/25 transition-[background-color,scale] duration-150 ease-media hover:bg-current/10 focus-visible:ring-2 focus-visible:ring-current/60 motion-safe:active:scale-[0.96]\"\r\n >\r\n <RetryIcon className=\"size-3.5\" />\r\n Try again\r\n </button>\r\n </div>\r\n )\r\n}\r\n\r\nexport {\r\n MediaRoot,\r\n MediaVideo,\r\n MediaAudio,\r\n MediaSurface,\r\n MediaCaptions,\r\n MediaCaptionsButton,\r\n MediaSettings,\r\n MediaErrorMessage,\r\n MediaControls,\r\n MediaButton,\r\n MediaPlayButton,\r\n MediaMuteButton,\r\n MediaFullscreenButton,\r\n MediaPictureInPictureButton,\r\n MediaPlaybackRateButton,\r\n MediaSeek,\r\n MediaVolume,\r\n MediaTime,\r\n MediaLoading,\r\n useMedia,\r\n useMediaStore,\r\n}\r\nexport type {\r\n MediaSourceLoader,\r\n MediaRootProps,\r\n MediaVideoProps,\r\n MediaAudioProps,\r\n MediaSurfaceProps,\r\n MediaControlsProps,\r\n MediaButtonProps,\r\n MediaTooltipAlign,\r\n MediaPlaybackRateButtonProps,\r\n MediaSeekProps,\r\n MediaVolumeProps,\r\n MediaTimeProps,\r\n MediaLoadingProps,\r\n MediaCaptionsProps,\r\n MediaSettingsProps,\r\n MediaErrorMessageProps,\r\n}\r\n","export type MediaTextTrack = {\r\n label: string\r\n language: string\r\n kind: \"captions\" | \"subtitles\"\r\n}\r\n\r\nexport type MediaInfo = {\r\n title?: string\r\n artist?: string\r\n album?: string\r\n artwork?: { src: string; sizes?: string; type?: string }[]\r\n}\r\n\r\nexport type MediaState = {\r\n paused: boolean\r\n ended: boolean\r\n started: boolean\r\n waiting: boolean\r\n currentTime: number\r\n duration: number\r\n bufferedEnd: number\r\n volume: number\r\n muted: boolean\r\n playbackRate: number\r\n fullscreen: boolean\r\n pictureInPicture: boolean\r\n error: MediaError | null\r\n /** False while the element has no source to play. */\r\n hasSource: boolean\r\n /** Caption and subtitle tracks, in document order. */\r\n textTracks: MediaTextTrack[]\r\n /** Index into `textTracks`, or -1 when captions are off. */\r\n textTrack: number\r\n /** Text of the cues active right now, one entry per cue. */\r\n cues: string[]\r\n}\r\n\r\nexport type MediaStoreOptions = {\r\n /** localStorage key used to remember volume, mute and speed. Off by default. */\r\n persistKey?: string\r\n}\r\n\r\ntype Listener = () => void\r\n\r\nconst MEDIA_EVENTS = [\r\n \"play\",\r\n \"pause\",\r\n \"playing\",\r\n \"ended\",\r\n \"waiting\",\r\n \"canplay\",\r\n \"seeking\",\r\n \"seeked\",\r\n \"timeupdate\",\r\n \"durationchange\",\r\n \"loadedmetadata\",\r\n \"progress\",\r\n \"volumechange\",\r\n \"ratechange\",\r\n \"emptied\",\r\n \"loadstart\",\r\n \"error\",\r\n \"enterpictureinpicture\",\r\n \"leavepictureinpicture\",\r\n] as const\r\n\r\nconst TRACK_EVENTS = [\"addtrack\", \"removetrack\", \"change\"] as const\r\n\r\nconst initialState: MediaState = {\r\n paused: true,\r\n ended: false,\r\n started: false,\r\n waiting: false,\r\n currentTime: 0,\r\n duration: 0,\r\n bufferedEnd: 0,\r\n volume: 1,\r\n muted: false,\r\n playbackRate: 1,\r\n fullscreen: false,\r\n pictureInPicture: false,\r\n error: null,\r\n hasSource: false,\r\n textTracks: [],\r\n textTrack: -1,\r\n cues: [],\r\n}\r\n\r\ntype WebkitVideo = HTMLVideoElement & {\r\n webkitEnterFullscreen?: () => void\r\n webkitDisplayingFullscreen?: boolean\r\n}\r\n\r\nfunction readBufferedEnd(media: HTMLMediaElement) {\r\n const { buffered, currentTime } = media\r\n for (let i = 0; i < buffered.length; i++) {\r\n if (buffered.start(i) <= currentTime && buffered.end(i) >= currentTime) {\r\n return buffered.end(i)\r\n }\r\n }\r\n return buffered.length ? buffered.end(buffered.length - 1) : 0\r\n}\r\n\r\nfunction captionTracks(media: HTMLMediaElement | null) {\r\n if (!media?.textTracks) return []\r\n return Array.from(media.textTracks).filter(\r\n (track) => track.kind === \"captions\" || track.kind === \"subtitles\"\r\n )\r\n}\r\n\r\n/**\r\n * The HTML form drops voice and class tags and decodes entities, so a cue\r\n * written as `Q&amp;A` reads `Q&A`. jsdom has no `getCueAsHTML`.\r\n */\r\nfunction cueText(cue: TextTrackCue) {\r\n const vtt = cue as VTTCue\r\n const text = typeof vtt.getCueAsHTML === \"function\" ? vtt.getCueAsHTML().textContent : vtt.text?.replace(/<[^>]+>/g, \"\")\r\n return (text ?? \"\").trim()\r\n}\r\n\r\n/* the track a `<track default>` element asks for, or -1 */\r\nfunction defaultTrack(media: HTMLMediaElement | null, tracks: TextTrack[]) {\r\n const el = Array.from(media?.querySelectorAll(\"track\") ?? []).find((track) => track.default)\r\n return el ? tracks.indexOf(el.track) : -1\r\n}\r\n\r\nconst sameList = (a: readonly unknown[], b: readonly unknown[]) =>\r\n a.length === b.length && a.every((value, i) => Object.is(value, b[i]))\r\n\r\nconst sameTracks = (a: MediaTextTrack[], b: MediaTextTrack[]) =>\r\n a.length === b.length &&\r\n a.every((t, i) => t.label === b[i].label && t.language === b[i].language && t.kind === b[i].kind)\r\n\r\ntype Preferences = Pick<MediaState, \"volume\" | \"muted\" | \"playbackRate\">\r\n\r\nfunction readPreferences(key: string): Partial<Preferences> {\r\n try {\r\n return JSON.parse(localStorage.getItem(key) ?? \"{}\")\r\n } catch {\r\n return {}\r\n }\r\n}\r\n\r\n/**\r\n * Framework-agnostic store over any HTMLMediaElement. React bindings,\r\n * web components, or anything else can subscribe to it.\r\n */\r\nexport function createMediaStore({ persistKey }: MediaStoreOptions = {}) {\r\n let state = initialState\r\n let media: HTMLMediaElement | null = null\r\n let container: HTMLElement | null = null\r\n let cueTrack: TextTrack | null = null\r\n let metadata: MediaInfo | null = null\r\n\r\n /* set once the viewer picks a track, so `default` stops applying */\r\n let trackChosen = false\r\n const listeners = new Set<Listener>()\r\n\r\n const set = (patch: Partial<MediaState>) => {\r\n let changed = false\r\n for (const key in patch) {\r\n const k = key as keyof MediaState\r\n const next = patch[k]\r\n const prev = state[k]\r\n if (k === \"cues\" ? !sameList(prev as string[], next as string[]) : k === \"textTracks\" ? !sameTracks(prev as MediaTextTrack[], next as MediaTextTrack[]) : !Object.is(prev, next)) {\r\n changed = true\r\n break\r\n }\r\n }\r\n if (!changed) return\r\n state = { ...state, ...patch }\r\n listeners.forEach((l) => l())\r\n }\r\n\r\n const sync = () => {\r\n if (!media) return\r\n set({\r\n paused: media.paused,\r\n ended: media.ended,\r\n started: state.started || !media.paused || media.currentTime > 0,\r\n currentTime: media.currentTime,\r\n duration: Number.isFinite(media.duration) ? media.duration : 0,\r\n bufferedEnd: readBufferedEnd(media),\r\n volume: media.volume,\r\n muted: media.muted,\r\n playbackRate: media.playbackRate,\r\n pictureInPicture:\r\n typeof document !== \"undefined\" && document.pictureInPictureElement === media,\r\n error: media.error,\r\n hasSource: Boolean(media.src || media.currentSrc || media.querySelector(\"source[src]\")),\r\n })\r\n }\r\n\r\n /* ------------------------------ Captions ------------------------------ */\r\n\r\n const readCues = () => {\r\n const active = cueTrack?.activeCues\r\n set({ cues: active ? Array.from(active).map(cueText).filter(Boolean) : [] })\r\n }\r\n\r\n const bindCueTrack = (track: TextTrack | null) => {\r\n if (cueTrack === track) return\r\n cueTrack?.removeEventListener(\"cuechange\", readCues)\r\n cueTrack = track\r\n cueTrack?.addEventListener(\"cuechange\", readCues)\r\n }\r\n\r\n // Native rendering is replaced by our own cue layer: the active track runs\r\n // in \"hidden\" mode (cues load, nothing paints) and every other track is off.\r\n const syncTracks = (requested = state.textTrack) => {\r\n const tracks = captionTracks(media)\r\n /**\r\n * Attaching sets every track's mode before the browser applies `default`,\r\n * which cancels it, so the default is applied here instead.\r\n */\r\n let active = requested === -1 && !trackChosen ? defaultTrack(media, tracks) : requested\r\n tracks.forEach((track, i) => {\r\n if (track.mode === \"showing\") active = i\r\n })\r\n if (active >= tracks.length) active = -1\r\n tracks.forEach((track, i) => {\r\n const mode = i === active ? \"hidden\" : \"disabled\"\r\n if (track.mode !== mode) track.mode = mode\r\n })\r\n bindCueTrack(tracks[active] ?? null)\r\n set({\r\n textTracks: tracks.map((track, i) => ({\r\n label: track.label || track.language || `Track ${i + 1}`,\r\n language: track.language,\r\n kind: track.kind as MediaTextTrack[\"kind\"],\r\n })),\r\n textTrack: active,\r\n })\r\n readCues()\r\n }\r\n\r\n const onTracksChange = () => syncTracks()\r\n\r\n /* ---------------------------- Media Session --------------------------- */\r\n\r\n const applyMediaSession = () => {\r\n if (typeof navigator === \"undefined\" || !(\"mediaSession\" in navigator) || !metadata) return\r\n const session = navigator.mediaSession\r\n session.metadata = new MediaMetadata(metadata)\r\n const handlers: [MediaSessionAction, MediaSessionActionHandler][] = [\r\n [\"play\", () => actions.play()],\r\n [\"pause\", () => actions.pause()],\r\n [\"seekbackward\", (d) => actions.seekBy(-(d.seekOffset ?? 10))],\r\n [\"seekforward\", (d) => actions.seekBy(d.seekOffset ?? 10)],\r\n [\"seekto\", (d) => d.seekTime !== undefined && actions.seek(d.seekTime)],\r\n ]\r\n for (const [action, handler] of handlers) {\r\n try {\r\n session.setActionHandler(action, handler)\r\n } catch {\r\n // Action not supported by this browser.\r\n }\r\n }\r\n }\r\n\r\n const updatePositionState = () => {\r\n if (!metadata || !media || typeof navigator === \"undefined\" || !(\"mediaSession\" in navigator)) return\r\n if (!Number.isFinite(media.duration) || media.duration <= 0) return\r\n try {\r\n navigator.mediaSession.setPositionState({\r\n duration: media.duration,\r\n playbackRate: media.playbackRate,\r\n position: Math.min(media.currentTime, media.duration),\r\n })\r\n } catch {\r\n // Ignore transient invalid states.\r\n }\r\n }\r\n\r\n /* ------------------------------- Events ------------------------------- */\r\n\r\n const savePreferences = () => {\r\n if (!persistKey || !media) return\r\n try {\r\n const prefs: Preferences = {\r\n volume: media.volume,\r\n muted: media.muted,\r\n playbackRate: media.playbackRate,\r\n }\r\n localStorage.setItem(persistKey, JSON.stringify(prefs))\r\n } catch {\r\n // Storage may be full or disabled.\r\n }\r\n }\r\n\r\n const onEvent = (event: Event) => {\r\n switch (event.type) {\r\n case \"emptied\":\r\n set({ started: false, waiting: false })\r\n break\r\n case \"waiting\":\r\n set({ waiting: true })\r\n break\r\n case \"playing\":\r\n case \"canplay\":\r\n case \"pause\":\r\n case \"error\":\r\n set({ waiting: false })\r\n break\r\n case \"play\":\r\n applyMediaSession()\r\n break\r\n case \"volumechange\":\r\n case \"ratechange\":\r\n savePreferences()\r\n break\r\n case \"loadedmetadata\":\r\n syncTracks()\r\n break\r\n }\r\n if (event.type === \"durationchange\" || event.type === \"ratechange\" || event.type === \"seeked\") {\r\n updatePositionState()\r\n }\r\n sync()\r\n }\r\n\r\n const onFullscreenChange = () => {\r\n const el = document.fullscreenElement\r\n const video = media as WebkitVideo | null\r\n set({\r\n fullscreen:\r\n (!!el && (el === container || el === media)) || !!video?.webkitDisplayingFullscreen,\r\n })\r\n }\r\n\r\n const attach = (el: HTMLMediaElement | null) => {\r\n if (media === el) return\r\n if (media) {\r\n MEDIA_EVENTS.forEach((e) => media!.removeEventListener(e, onEvent))\r\n TRACK_EVENTS.forEach((e) => media!.textTracks?.removeEventListener?.(e, onTracksChange))\r\n media.removeEventListener(\"webkitbeginfullscreen\", onFullscreenChange)\r\n media.removeEventListener(\"webkitendfullscreen\", onFullscreenChange)\r\n bindCueTrack(null)\r\n }\r\n media = el\r\n if (!el) {\r\n state = initialState\r\n listeners.forEach((l) => l())\r\n return\r\n }\r\n if (persistKey) {\r\n const prefs = readPreferences(persistKey)\r\n if (typeof prefs.volume === \"number\") el.volume = Math.min(Math.max(prefs.volume, 0), 1)\r\n if (typeof prefs.muted === \"boolean\") el.muted = prefs.muted\r\n if (typeof prefs.playbackRate === \"number\") el.playbackRate = prefs.playbackRate\r\n }\r\n MEDIA_EVENTS.forEach((e) => el.addEventListener(e, onEvent))\r\n TRACK_EVENTS.forEach((e) => el.textTracks?.addEventListener?.(e, onTracksChange))\r\n el.addEventListener(\"webkitbeginfullscreen\", onFullscreenChange)\r\n el.addEventListener(\"webkitendfullscreen\", onFullscreenChange)\r\n sync()\r\n syncTracks()\r\n }\r\n\r\n const setContainer = (el: HTMLElement | null) => {\r\n if (typeof document === \"undefined\") return\r\n document.removeEventListener(\"fullscreenchange\", onFullscreenChange)\r\n container = el\r\n if (el) document.addEventListener(\"fullscreenchange\", onFullscreenChange)\r\n }\r\n\r\n const play = async () => {\r\n if (!media) return\r\n try {\r\n await media.play()\r\n } catch {\r\n // Autoplay policies may reject; the paused state stays accurate.\r\n }\r\n }\r\n\r\n const pause = () => media?.pause()\r\n\r\n const actions = {\r\n play,\r\n pause,\r\n toggle: () => (media?.paused || media?.ended ? play() : pause()),\r\n seek: (time: number) => {\r\n if (!media) return\r\n const max = Number.isFinite(media.duration) ? media.duration : time\r\n media.currentTime = Math.min(Math.max(time, 0), max)\r\n sync()\r\n },\r\n seekBy: (delta: number) => actions.seek((media?.currentTime ?? 0) + delta),\r\n /** Jump to a fraction (0–1) of the duration. */\r\n seekToRatio: (ratio: number) => actions.seek(ratio * (media?.duration || 0)),\r\n setVolume: (volume: number) => {\r\n if (!media) return\r\n media.volume = Math.min(Math.max(volume, 0), 1)\r\n media.muted = media.volume === 0\r\n },\r\n toggleMute: () => {\r\n if (!media) return\r\n if (media.muted && media.volume === 0) media.volume = 1\r\n media.muted = !media.muted\r\n },\r\n setPlaybackRate: (rate: number) => {\r\n if (media) media.playbackRate = rate\r\n },\r\n /** Select a caption track by index, or -1 to turn captions off. */\r\n setTextTrack: (index: number) => syncTracksExplicit(index),\r\n toggleCaptions: () => {\r\n if (!state.textTracks.length) return\r\n syncTracksExplicit(state.textTrack === -1 ? 0 : -1)\r\n },\r\n /** Reload the current source after an error. */\r\n retry: () => {\r\n if (!media) return\r\n media.load()\r\n void play()\r\n },\r\n setMetadata: (next: MediaInfo | null) => {\r\n metadata = next\r\n if (media && !media.paused) applyMediaSession()\r\n },\r\n toggleFullscreen: async () => {\r\n if (typeof document === \"undefined\") return\r\n if (document.fullscreenElement) {\r\n await document.exitFullscreen().catch(() => {})\r\n return\r\n }\r\n if (container?.requestFullscreen) {\r\n await container.requestFullscreen().catch(() => {})\r\n return\r\n }\r\n // iOS Safari only supports fullscreen on the video element itself.\r\n ;(media as WebkitVideo | null)?.webkitEnterFullscreen?.()\r\n },\r\n togglePictureInPicture: async () => {\r\n if (!(media instanceof HTMLVideoElement)) return\r\n try {\r\n if (document.pictureInPictureElement) await document.exitPictureInPicture()\r\n else await media.requestPictureInPicture()\r\n } catch {\r\n // Unsupported or blocked by the browser.\r\n }\r\n },\r\n }\r\n\r\n // An explicit choice must win over a track the browser marked \"showing\".\r\n const syncTracksExplicit = (index: number) => {\r\n trackChosen = true\r\n captionTracks(media).forEach((track, i) => {\r\n if (i !== index && track.mode === \"showing\") track.mode = \"disabled\"\r\n })\r\n syncTracks(index)\r\n }\r\n\r\n return {\r\n getState: () => state,\r\n getServerState: () => initialState,\r\n getMedia: () => media,\r\n subscribe: (listener: Listener) => {\r\n listeners.add(listener)\r\n return () => listeners.delete(listener)\r\n },\r\n attach,\r\n setContainer,\r\n ...actions,\r\n }\r\n}\r\n\r\nexport type MediaStore = ReturnType<typeof createMediaStore>\r\n\r\n/**\r\n * `m:ss`, or `h:mm:ss` from one hour. Pass the duration as `reference` so\r\n * every time in a player uses the same fields: `0:00:05` beside `10:00:01`,\r\n * and the label does not widen when playback passes an hour.\r\n */\r\nexport function formatTime(seconds: number, reference = seconds) {\r\n if (!Number.isFinite(seconds) || seconds < 0) seconds = 0\r\n const s = Math.floor(seconds % 60)\r\n const m = Math.floor((seconds / 60) % 60)\r\n const h = Math.floor(seconds / 3600)\r\n const ss = s.toString().padStart(2, \"0\")\r\n const hours = h > 0 || (Number.isFinite(reference) && reference >= 3600)\r\n return hours ? `${h}:${m.toString().padStart(2, \"0\")}:${ss}` : `${m}:${ss}`\r\n}\r\n\r\n/** Human-readable message for a MediaError code. */\r\nexport function describeMediaError(error: MediaError | null) {\r\n switch (error?.code) {\r\n case 1:\r\n return \"Playback was aborted.\"\r\n case 2:\r\n return \"A network error stopped the download.\"\r\n case 3:\r\n return \"This media could not be decoded.\"\r\n case 4:\r\n return \"This media couldn't be loaded.\"\r\n default:\r\n return \"Something went wrong during playback.\"\r\n }\r\n}\r\n","\"use client\"\r\n\r\nimport * as React from \"react\"\r\n\r\nimport { cn } from \"@/lib/utils\"\r\nimport type { MediaInfo } from \"@/lib/media-store\"\r\nimport { PlayIcon } from \"@/components/ui/media-icons\"\r\nimport {\r\n MediaCaptions,\r\n MediaCaptionsButton,\r\n MediaControls,\r\n MediaErrorMessage,\r\n MediaFullscreenButton,\r\n MediaLoading,\r\n MediaMuteButton,\r\n MediaPictureInPictureButton,\r\n MediaPlayButton,\r\n MediaRoot,\r\n MediaSeek,\r\n MediaSettings,\r\n MediaSurface,\r\n MediaTime,\r\n MediaVideo,\r\n MediaVolume,\r\n type MediaSourceLoader,\r\n} from \"@/components/ui/media\"\r\n\r\ntype VideoTrack = {\r\n src: string\r\n label: string\r\n srcLang: string\r\n kind?: \"captions\" | \"subtitles\"\r\n default?: boolean\r\n}\r\n\r\ntype VideoPlayerProps = Omit<React.ComponentProps<\"video\">, \"controls\"> & {\r\n /** Classes for the outer frame. `className` targets the <video> element. */\r\n frameClassName?: string\r\n /** WebVTT caption or subtitle files. */\r\n tracks?: VideoTrack[]\r\n /** Adapter for sources the browser can't play natively, e.g. HLS. */\r\n loader?: MediaSourceLoader\r\n /** Shown in OS media controls and lock screens. */\r\n metadata?: MediaInfo\r\n /** Remember volume, mute and speed under this localStorage key. */\r\n persistKey?: string\r\n /** Small labels with shortcuts over the controls on hover and focus. On by default. */\r\n tooltips?: boolean\r\n}\r\n\r\nconst reveal = \"transition-[opacity,scale] duration-200 ease-media motion-reduce:transition-none\"\r\nconst visibleWithControls =\r\n \"group-data-active/media:opacity-100 group-has-focus-visible/media:opacity-100 group-data-paused/media:group-data-started/media:opacity-100\"\r\nconst glass = \"bg-media-scrim/70 text-media-foreground ring-1 ring-media-foreground/10 backdrop-blur-xl\"\r\n\r\n/**\r\n * The volume popover opens on hover or keyboard focus, never on a click, so the\r\n * mute button keeps its one job. `has-focus-visible` rather than focus-within,\r\n * so a mouse click on mute does not pin it open.\r\n */\r\nconst volumeOpen =\r\n \"group-hover/volume-menu:pointer-events-auto group-hover/volume-menu:translate-y-0 group-hover/volume-menu:opacity-100 group-has-focus-visible/volume-menu:pointer-events-auto group-has-focus-visible/volume-menu:translate-y-0 group-has-focus-visible/volume-menu:opacity-100\"\r\n\r\nfunction VideoPlayer({\r\n className,\r\n frameClassName,\r\n tracks,\r\n loader,\r\n metadata,\r\n persistKey,\r\n tooltips = true,\r\n children,\r\n crossOrigin,\r\n ...props\r\n}: VideoPlayerProps) {\r\n return (\r\n <MediaRoot\r\n persistKey={persistKey}\r\n metadata={metadata}\r\n tooltips={tooltips}\r\n className={cn(\r\n \"@container/player aspect-video w-full overflow-hidden rounded-xl bg-media-scrim text-media-foreground data-fullscreen:rounded-none\",\r\n frameClassName\r\n )}\r\n >\r\n <MediaVideo\r\n className={cn(\"size-full object-contain\", className)}\r\n loader={loader}\r\n crossOrigin={crossOrigin ?? (tracks?.length ? \"anonymous\" : undefined)}\r\n {...props}\r\n >\r\n {tracks?.map((track) => (\r\n <track\r\n key={track.src}\r\n src={track.src}\r\n label={track.label}\r\n srcLang={track.srcLang}\r\n kind={track.kind ?? \"subtitles\"}\r\n default={track.default}\r\n />\r\n ))}\r\n {children}\r\n </MediaVideo>\r\n\r\n <MediaSurface indicatorClassName={glass} />\r\n\r\n <span\r\n aria-hidden\r\n className={cn(\r\n \"pointer-events-none absolute top-1/2 left-1/2 grid size-12 -translate-1/2 place-items-center rounded-full bg-media-scrim/40 backdrop-blur-md\",\r\n reveal,\r\n \"group-data-started/media:scale-75 group-data-started/media:opacity-0\"\r\n )}\r\n >\r\n <PlayIcon className=\"size-5\" />\r\n </span>\r\n\r\n <span className=\"pointer-events-none absolute inset-0 grid place-items-center\">\r\n <MediaLoading />\r\n </span>\r\n\r\n <MediaCaptions\r\n className={cn(\r\n /* sized by the player, not the viewport, so a small player keeps its cues */\r\n \"absolute inset-x-4 bottom-4 text-xs leading-snug transition-transform duration-200 ease-media motion-reduce:transition-none @xs/player:text-sm @2xl/player:text-base\",\r\n \"group-data-active/media:-translate-y-10 group-has-focus-visible/media:-translate-y-10 group-data-paused/media:group-data-started/media:-translate-y-10\",\r\n \"[&>span]:bg-media-scrim/75 [&>span]:text-media-foreground\"\r\n )}\r\n />\r\n\r\n <div\r\n aria-hidden\r\n className={cn(\r\n \"pointer-events-none absolute inset-x-0 bottom-0 h-24 bg-linear-to-t from-media-scrim/60 to-transparent opacity-0\",\r\n reveal,\r\n visibleWithControls\r\n )}\r\n />\r\n\r\n {/* tooltips match the seek preview: light on the dark bar. The bar stays\r\n left to right in RTL pages, like the seek math */}\r\n <MediaControls\r\n dir=\"ltr\"\r\n className=\"absolute inset-x-2 bottom-2 h-9 gap-1 px-1 **:data-[slot=media-tooltip]:bg-media-foreground **:data-[slot=media-tooltip]:text-media-scrim\">\r\n <MediaPlayButton tooltipAlign=\"start\" />\r\n <MediaSeek className=\"mx-2\" previewClassName=\"bg-media-foreground text-media-scrim\" />\r\n {/* below 20rem the seek bar needs the room more than the label */}\r\n <MediaTime type=\"remaining\" toggle className=\"hidden min-w-10 text-right text-media-foreground/80 @xs/player:inline\" />\r\n\r\n {/* a vertical slider above mute, so opening it never squeezes the seek\r\n bar. Fine pointers only: phones set volume with their own buttons,\r\n and iOS ignores volume changes from the page */}\r\n <div className=\"group/volume-menu relative\">\r\n {/* no tooltip: the volume popover opens in the same spot */}\r\n <MediaMuteButton tooltip={false} />\r\n <div\r\n className={cn(\r\n \"pointer-events-none absolute bottom-full left-1/2 hidden -translate-x-1/2 translate-y-1 pb-2 opacity-0 pointer-fine:block\",\r\n \"transition-[opacity,translate] duration-150 ease-media motion-reduce:transition-none\",\r\n volumeOpen\r\n )}\r\n >\r\n <div className={cn(\"flex h-24 w-7 justify-center rounded-full py-3\", glass)}>\r\n <MediaVolume orientation=\"vertical\" className=\"h-full\" />\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <MediaCaptionsButton />\r\n <MediaSettings panelClassName={glass} />\r\n {/* only where the bar has room for it; the I key works at any size */}\r\n <MediaPictureInPictureButton tooltipAlign=\"end\" className=\"hidden @sm/player:inline-grid\" />\r\n <MediaFullscreenButton tooltipAlign=\"end\" />\r\n </MediaControls>\r\n\r\n <MediaErrorMessage className=\"absolute inset-0 bg-media-scrim/85 px-6 text-media-foreground backdrop-blur-sm\" />\r\n </MediaRoot>\r\n )\r\n}\r\n\r\nexport { VideoPlayer, type VideoPlayerProps, type VideoTrack }\r\n","\"use client\"\r\n\r\nimport * as React from \"react\"\r\n\r\nimport { cn } from \"@/lib/utils\"\r\nimport type { MediaInfo } from \"@/lib/media-store\"\r\nimport {\r\n MediaAudio,\r\n MediaErrorMessage,\r\n MediaLoading,\r\n MediaMuteButton,\r\n MediaPlayButton,\r\n MediaPlaybackRateButton,\r\n MediaRoot,\r\n MediaSeek,\r\n MediaTime,\r\n MediaVolume,\r\n type MediaSourceLoader,\r\n} from \"@/components/ui/media\"\r\n\r\ntype AudioPlayerProps = Omit<React.ComponentProps<\"audio\">, \"controls\" | \"title\"> & {\r\n title?: React.ReactNode\r\n description?: React.ReactNode\r\n /** Cover image shown beside the controls and in OS media controls. */\r\n artwork?: string\r\n /** Show a playback speed toggle. Useful for podcasts and voice notes. */\r\n showSpeed?: boolean\r\n /** Small labels with shortcuts over the controls on hover and focus. On by default. */\r\n tooltips?: boolean\r\n /** Classes for the outer frame. `className` targets the <audio> element. */\r\n frameClassName?: string\r\n /** Adapter for sources the browser can't play natively, e.g. HLS. */\r\n loader?: MediaSourceLoader\r\n /** Overrides metadata derived from `title`, `description` and `artwork`. */\r\n metadata?: MediaInfo\r\n /** Remember volume, mute and speed under this localStorage key. */\r\n persistKey?: string\r\n}\r\n\r\nconst muted = \"text-muted-foreground hover:text-foreground\"\r\n\r\n/**\r\n * The volume popover opens on hover or keyboard focus, never on a click, so the\r\n * mute button keeps its one job. `has-focus-visible` rather than focus-within,\r\n * so a mouse click on mute does not pin it open.\r\n */\r\nconst volumeOpen =\r\n \"group-hover/volume-menu:pointer-events-auto group-hover/volume-menu:translate-y-0 group-hover/volume-menu:opacity-100 group-has-focus-visible/volume-menu:pointer-events-auto group-has-focus-visible/volume-menu:translate-y-0 group-has-focus-visible/volume-menu:opacity-100\"\r\n\r\nfunction AudioPlayer({\r\n title,\r\n description,\r\n artwork,\r\n showSpeed,\r\n tooltips = true,\r\n className,\r\n frameClassName,\r\n loader,\r\n metadata,\r\n persistKey,\r\n ...props\r\n}: AudioPlayerProps) {\r\n const sessionInfo =\r\n metadata ??\r\n (typeof title === \"string\"\r\n ? {\r\n title,\r\n artist: typeof description === \"string\" ? description : undefined,\r\n artwork: artwork ? [{ src: artwork }] : undefined,\r\n }\r\n : undefined)\r\n\r\n const hasInfo = Boolean(title || description)\r\n\r\n /* the src that failed to load, so a new artwork gets a fresh try */\r\n const [failedArtwork, setFailedArtwork] = React.useState<string | null>(null)\r\n const showArtwork = artwork && failedArtwork !== artwork\r\n\r\n return (\r\n <MediaRoot\r\n persistKey={persistKey}\r\n metadata={sessionInfo}\r\n tooltips={tooltips}\r\n className={cn(\r\n \"@container/audio grid w-full grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-x-3 gap-y-1 rounded-xl border bg-card p-2.5 pr-3 text-card-foreground\",\r\n frameClassName\r\n )}\r\n >\r\n <MediaAudio className={className} loader={loader} {...props} />\r\n\r\n {/* with a title, the info stacks over the seek row and the controls\r\n span both. Below 24rem the controls take the first row, then the\r\n title and the seek row each get the full width */}\r\n <div className={cn(\"flex items-center\", hasInfo && \"row-span-2 @max-sm/audio:row-span-1\")}>\r\n <span className=\"relative shrink-0\">\r\n {showArtwork ? (\r\n /**\r\n * The tile presses as one piece. Scaling the button alone pulled\r\n * its scrim in from the edges and bared a band of the image. No\r\n * overflow-hidden, so the play tooltip is not clipped.\r\n */\r\n <span className=\"relative block size-11 rounded-lg bg-muted transition-[scale] duration-150 ease-media motion-safe:has-[button:enabled:active]:scale-[0.96] motion-reduce:transition-none\">\r\n {/* eslint-disable-next-line @next/next/no-img-element */}\r\n <img\r\n src={artwork}\r\n alt=\"\"\r\n draggable={false}\r\n onError={() => setFailedArtwork(artwork)}\r\n\r\n /* a server-rendered image can fail before hydration, when no\r\n onError is attached yet */\r\n ref={(img) => {\r\n if (img?.complete && img.naturalWidth === 0) setFailedArtwork(artwork)\r\n }}\r\n className=\"size-full rounded-lg object-cover outline select-none outline-black/10 -outline-offset-1 dark:outline-white/10\"\r\n />\r\n <MediaPlayButton\r\n tooltipAlign=\"start\"\r\n className=\"absolute inset-0 size-full rounded-lg motion-safe:enabled:active:scale-100 bg-media-scrim/15 text-media-foreground hover:bg-media-scrim/35 focus-visible:bg-media-scrim/35 group-data-playing/media:bg-media-scrim/35 [&_svg]:size-4 [&_svg]:drop-shadow-sm\"\r\n />\r\n </span>\r\n ) : (\r\n <MediaPlayButton\r\n tooltipAlign=\"start\"\r\n className=\"size-10 rounded-full bg-primary text-primary-foreground hover:bg-primary/90 [&_svg]:size-4\"\r\n />\r\n )}\r\n {/* while the stream buffers: a ring around the round button, or a\r\n spinner on the cover, since a spinning square would look broken */}\r\n <MediaLoading\r\n className={\r\n showArtwork\r\n ? \"absolute inset-0 m-auto size-5 text-background\"\r\n : \"absolute -inset-1 size-auto border-current/15 border-t-current text-foreground\"\r\n }\r\n />\r\n </span>\r\n </div>\r\n\r\n {hasInfo && (\r\n <div className=\"col-start-2 flex min-w-0 items-baseline gap-2 self-end text-sm leading-5 @max-sm/audio:col-span-3 @max-sm/audio:col-start-1 @max-sm/audio:row-start-2 @max-sm/audio:pt-1\">\r\n {/* the description gives way first, and the full text sits in a\r\n title for when either one truncates */}\r\n {title && (\r\n <span dir=\"auto\" title={typeof title === \"string\" ? title : undefined} className=\"truncate font-medium\">\r\n {title}\r\n </span>\r\n )}\r\n {description && (\r\n <span\r\n dir=\"auto\"\r\n title={typeof description === \"string\" ? description : undefined}\r\n className=\"min-w-12 shrink-[4] truncate text-muted-foreground\"\r\n >\r\n {description}\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n\r\n <div\r\n dir=\"ltr\"\r\n className={cn(\r\n \"col-start-2 flex items-center gap-2.5 text-muted-foreground\",\r\n hasInfo && \"row-start-2 self-start @max-sm/audio:col-span-3 @max-sm/audio:col-start-1 @max-sm/audio:row-start-3\"\r\n )}\r\n >\r\n <MediaTime className=\"min-w-9\" />\r\n <MediaSeek className=\"text-foreground\" />\r\n <MediaTime type=\"duration\" toggle className=\"min-w-9 text-right\" />\r\n </div>\r\n\r\n <div className={cn(\"col-start-3 flex items-center gap-0.5\", hasInfo && \"row-span-2 row-start-1 @max-sm/audio:row-span-1\")}>\r\n {showSpeed && <MediaPlaybackRateButton rates={[1, 1.5, 2, 0.75]} className={muted} />}\r\n\r\n {/* a vertical slider above mute. Fine pointers only: phones set\r\n volume with their own buttons, and iOS ignores it from the page */}\r\n <div className=\"group/volume-menu relative\">\r\n {/* no tooltip: the volume popover opens in the same spot */}\r\n <MediaMuteButton tooltip={false} className={muted} />\r\n <div\r\n className={cn(\r\n \"pointer-events-none absolute bottom-full left-1/2 z-10 hidden -translate-x-1/2 translate-y-1 pb-2 opacity-0 pointer-fine:block\",\r\n \"transition-[opacity,translate] duration-150 ease-media motion-reduce:transition-none\",\r\n volumeOpen\r\n )}\r\n >\r\n <div className=\"flex h-24 w-7 justify-center rounded-full bg-popover py-3 text-popover-foreground shadow-md ring-1 ring-foreground/10\">\r\n <MediaVolume orientation=\"vertical\" className=\"h-full\" />\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <MediaErrorMessage className=\"absolute inset-0 flex-row gap-4 rounded-[inherit] bg-card/95 px-4 text-card-foreground\" />\r\n </MediaRoot>\r\n )\r\n}\r\n\r\nexport { AudioPlayer, type AudioPlayerProps }\r\n"],"mappings":"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","names":["muted","React","jsx","jsxs","muted","duration","currentTime","jsx","jsxs","React","jsx","jsxs","volumeOpen"]}