webs-sdk 0.19.64 → 0.19.65

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.
Files changed (100) hide show
  1. package/AGENTS.md +44 -0
  2. package/CLAUDE.md +80 -0
  3. package/README.md +513 -490
  4. package/dist/components/ARFilters/ARFilters.js +54 -54
  5. package/dist/components/ArtGenerator.js +141 -141
  6. package/dist/components/AvatarAI.js +14 -14
  7. package/dist/components/CookieConsent/constants.js +31 -31
  8. package/dist/components/CreativeFaceSwap.js +23 -23
  9. package/dist/components/GifsIa.js +76 -76
  10. package/dist/components/MemeGenerator.js +12 -12
  11. package/dist/components/PasswordReset/constants.js +33 -33
  12. package/dist/components/RecoveryPassword/constants.js +27 -27
  13. package/dist/components/SpinningWheel/SpinningWheel.js +7 -7
  14. package/dist/components/SpinningWheel/index.d.ts +1 -1
  15. package/dist/components/SpinningWheel/index.d.ts.map +1 -1
  16. package/dist/components/SpinningWheel/index.js +2 -1
  17. package/dist/components/SpinningWheel/index.js.map +1 -1
  18. package/dist/components/ui/ConfiguredMediaPlayer.d.ts +49 -0
  19. package/dist/components/ui/ConfiguredMediaPlayer.d.ts.map +1 -0
  20. package/dist/components/ui/ConfiguredMediaPlayer.js +111 -0
  21. package/dist/components/ui/ConfiguredMediaPlayer.js.map +1 -0
  22. package/dist/components/ui/badge.d.ts +1 -0
  23. package/dist/components/ui/badge.d.ts.map +1 -1
  24. package/dist/components/ui/badge.js +1 -1
  25. package/dist/components/ui/badge.js.map +1 -1
  26. package/dist/components/ui/compose-refs.d.ts +6 -0
  27. package/dist/components/ui/compose-refs.d.ts.map +1 -0
  28. package/dist/components/ui/compose-refs.js +57 -0
  29. package/dist/components/ui/compose-refs.js.map +1 -0
  30. package/dist/components/ui/dropdown-menu.d.ts +3 -1
  31. package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
  32. package/dist/components/ui/dropdown-menu.js +2 -2
  33. package/dist/components/ui/dropdown-menu.js.map +1 -1
  34. package/dist/components/ui/index.d.ts +2 -0
  35. package/dist/components/ui/index.d.ts.map +1 -1
  36. package/dist/components/ui/index.js +2 -0
  37. package/dist/components/ui/index.js.map +1 -1
  38. package/dist/components/ui/media-player.d.ts +127 -0
  39. package/dist/components/ui/media-player.d.ts.map +1 -0
  40. package/dist/components/ui/media-player.js +1633 -0
  41. package/dist/components/ui/media-player.js.map +1 -0
  42. package/dist/components/ui/tooltip.d.ts +3 -1
  43. package/dist/components/ui/tooltip.d.ts.map +1 -1
  44. package/dist/components/ui/tooltip.js +2 -2
  45. package/dist/components/ui/tooltip.js.map +1 -1
  46. package/dist/components/ui/use-lazy-ref.d.ts +4 -0
  47. package/dist/components/ui/use-lazy-ref.d.ts.map +1 -0
  48. package/dist/components/ui/use-lazy-ref.js +45 -0
  49. package/dist/components/ui/use-lazy-ref.js.map +1 -0
  50. package/dist/index.d.ts +1 -1
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +4 -3
  53. package/dist/index.js.map +1 -1
  54. package/dist/network.d.ts +12 -0
  55. package/dist/network.d.ts.map +1 -0
  56. package/dist/network.js +15 -0
  57. package/dist/network.js.map +1 -0
  58. package/dist/server.d.ts +54 -0
  59. package/dist/server.d.ts.map +1 -0
  60. package/dist/server.js +74 -0
  61. package/dist/server.js.map +1 -0
  62. package/dist/types/media-chrome-store-types.d.ts +107 -0
  63. package/dist/types/media-chrome-store-types.d.ts.map +1 -0
  64. package/dist/types/media-chrome-store-types.js +3 -0
  65. package/dist/types/media-chrome-store-types.js.map +1 -0
  66. package/dist/utils/QRCode.d.ts +1 -0
  67. package/dist/utils/QRCode.d.ts.map +1 -0
  68. package/dist/utils/QRCode.js +2 -0
  69. package/dist/utils/QRCode.js.map +1 -0
  70. package/dist/utils/imageValidation.d.ts +6 -0
  71. package/dist/utils/imageValidation.d.ts.map +1 -0
  72. package/dist/utils/imageValidation.js +64 -0
  73. package/dist/utils/imageValidation.js.map +1 -0
  74. package/docs/CONFIGURED_MEDIA_PLAYER.md +40 -0
  75. package/docs/CORP_AUTH_USAGE.md +687 -0
  76. package/docs/EXAMPLES_WALLPAPERSNAME_IMPLEMENTATION.md +1445 -0
  77. package/docs/EXAMPLE_ENDPOINTS_COMUNICATION.md +55 -0
  78. package/docs/EXTERNAL_SDK_GUIDE.md +1340 -0
  79. package/docs/FOR_YOU_360_FACESWAP_FIX.md +400 -0
  80. package/docs/FOR_YOU_IMPLEMENTATION_GUIDE.md +1413 -0
  81. package/docs/FOR_YOU_QUIZ_ARCHITECTURE.md +753 -0
  82. package/docs/FOR_YOU_SDK_WALLPAPERSNAME_BUG.md +106 -0
  83. package/docs/FOR_YOU_WALLPAPERSNAME_ENHANCEMENT_PLAN.md +210 -0
  84. package/docs/LEGAL_USAGE.md +190 -0
  85. package/docs/STREAK_IMPLEMENTATION_GUIDE.md +430 -0
  86. package/package.json +106 -84
  87. package/public/spin2win/assets/border-afristream.svg +26 -26
  88. package/public/spin2win/assets/border-efc.svg +23 -23
  89. package/public/spin2win/assets/border-streetreel.svg +23 -23
  90. package/public/spin2win/assets/border-totalgym.svg +27 -27
  91. package/public/spin2win/assets/border-womantoday.svg +26 -26
  92. package/public/spin2win/assets/indicator-afristream.svg +28 -28
  93. package/public/spin2win/assets/indicator-efc.svg +29 -29
  94. package/public/spin2win/assets/indicator-streetreel.svg +29 -29
  95. package/public/spin2win/assets/indicator-totalgym.svg +29 -29
  96. package/public/spin2win/fonts/switzer/README.md +22 -22
  97. package/scripts/install-agent-skill.cjs +34 -0
  98. package/skills/webs-sdk-package/SKILL.md +11 -0
  99. package/skills/webs-sdk-package/references/components.md +30 -0
  100. package/skills/webs-sdk-package/references/integration.md +32 -0
@@ -0,0 +1,1633 @@
1
+ "use strict";
2
+ "use client";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ var __rest = (this && this.__rest) || function (s, e) {
37
+ var t = {};
38
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
39
+ t[p] = s[p];
40
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
41
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
42
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
43
+ t[p[i]] = s[p[i]];
44
+ }
45
+ return t;
46
+ };
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.useMediaPlayer = void 0;
49
+ exports.MediaPlayer = MediaPlayer;
50
+ exports.MediaPlayerAudio = MediaPlayerAudio;
51
+ exports.MediaPlayerCaptions = MediaPlayerCaptions;
52
+ exports.MediaPlayerControls = MediaPlayerControls;
53
+ exports.MediaPlayerControlsOverlay = MediaPlayerControlsOverlay;
54
+ exports.MediaPlayerDownload = MediaPlayerDownload;
55
+ exports.MediaPlayerError = MediaPlayerError;
56
+ exports.MediaPlayerFullscreen = MediaPlayerFullscreen;
57
+ exports.MediaPlayerLoading = MediaPlayerLoading;
58
+ exports.MediaPlayerLoop = MediaPlayerLoop;
59
+ exports.MediaPlayerPiP = MediaPlayerPiP;
60
+ exports.MediaPlayerPlay = MediaPlayerPlay;
61
+ exports.MediaPlayerPlaybackSpeed = MediaPlayerPlaybackSpeed;
62
+ exports.MediaPlayerPortal = MediaPlayerPortal;
63
+ exports.MediaPlayerSeek = MediaPlayerSeek;
64
+ exports.MediaPlayerSeekBackward = MediaPlayerSeekBackward;
65
+ exports.MediaPlayerSeekForward = MediaPlayerSeekForward;
66
+ exports.MediaPlayerSettings = MediaPlayerSettings;
67
+ exports.MediaPlayerTime = MediaPlayerTime;
68
+ exports.MediaPlayerTooltip = MediaPlayerTooltip;
69
+ exports.MediaPlayerVideo = MediaPlayerVideo;
70
+ exports.MediaPlayerVolume = MediaPlayerVolume;
71
+ exports.MediaPlayerVolumeIndicator = MediaPlayerVolumeIndicator;
72
+ exports.useMediaPlayerStore = useStore;
73
+ const jsx_runtime_1 = require("react/jsx-runtime");
74
+ const utils_1 = require("../../libraries/utils");
75
+ const media_store_1 = require("media-chrome/react/media-store");
76
+ Object.defineProperty(exports, "useMediaPlayer", { enumerable: true, get: function () { return media_store_1.useMediaSelector; } });
77
+ const DirectionPrimitive = __importStar(require("@radix-ui/react-direction"));
78
+ const SliderPrimitive = __importStar(require("@radix-ui/react-slider"));
79
+ const SlotPrimitive = __importStar(require("@radix-ui/react-slot"));
80
+ const React = __importStar(require("react"));
81
+ const ReactDOM = __importStar(require("react-dom"));
82
+ const compose_refs_1 = require("./compose-refs");
83
+ const use_lazy_ref_1 = require("./use-lazy-ref");
84
+ const badge_1 = require("./badge");
85
+ const dropdown_menu_1 = require("./dropdown-menu");
86
+ const tooltip_1 = require("./tooltip");
87
+ const lucide_react_1 = require("lucide-react");
88
+ const _LUCIDE_ICON_MAP = {
89
+ AlertTriangleIcon: lucide_react_1.AlertTriangleIcon,
90
+ CaptionsOffIcon: lucide_react_1.CaptionsOffIcon,
91
+ CheckIcon: lucide_react_1.CheckIcon,
92
+ DownloadIcon: lucide_react_1.DownloadIcon,
93
+ FastForwardIcon: lucide_react_1.FastForwardIcon,
94
+ Loader2Icon: lucide_react_1.Loader2Icon,
95
+ Maximize2Icon: lucide_react_1.Maximize2Icon,
96
+ Minimize2Icon: lucide_react_1.Minimize2Icon,
97
+ PauseIcon: lucide_react_1.PauseIcon,
98
+ PictureInPicture2Icon: lucide_react_1.PictureInPicture2Icon,
99
+ PictureInPictureIcon: lucide_react_1.PictureInPictureIcon,
100
+ PlayIcon: lucide_react_1.PlayIcon,
101
+ RefreshCcwIcon: lucide_react_1.RefreshCcwIcon,
102
+ RepeatIcon: lucide_react_1.RepeatIcon,
103
+ RewindIcon: lucide_react_1.RewindIcon,
104
+ RotateCcwIcon: lucide_react_1.RotateCcwIcon,
105
+ SettingsIcon: lucide_react_1.SettingsIcon,
106
+ SubtitlesIcon: lucide_react_1.SubtitlesIcon,
107
+ Volume1Icon: lucide_react_1.Volume1Icon,
108
+ Volume2Icon: lucide_react_1.Volume2Icon,
109
+ VolumeXIcon: lucide_react_1.VolumeXIcon,
110
+ };
111
+ function IconPlaceholder(_a) {
112
+ var { lucide, tabler: _t, hugeicons: _h, phosphor: _p, remixicon: _r } = _a, props = __rest(_a, ["lucide", "tabler", "hugeicons", "phosphor", "remixicon"]);
113
+ const Icon = _LUCIDE_ICON_MAP[lucide];
114
+ if (!Icon)
115
+ return null;
116
+ return (0, jsx_runtime_1.jsx)(Icon, Object.assign({}, props));
117
+ }
118
+ function Button(_a) {
119
+ var { variant, size, asChild, className } = _a, props = __rest(_a, ["variant", "size", "asChild", "className"]);
120
+ const Comp = (asChild ? SlotPrimitive.Slot : "button");
121
+ return ((0, jsx_runtime_1.jsx)(Comp, Object.assign({ "data-slot": "button", className: (0, utils_1.cn)("inline-flex cursor-pointer items-center justify-center gap-1.5 rounded-md text-sm font-medium whitespace-nowrap transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50", variant === "ghost" && "hover:bg-white/10 text-white", variant === "secondary" && "bg-zinc-700 text-white hover:bg-zinc-600 px-3 h-8", variant === "outline" && "border border-zinc-600 text-white hover:bg-zinc-700 px-3 h-8", size === "icon" && "size-8", size === "sm" && "h-8 px-2.5 text-xs", className) }, props)));
122
+ }
123
+ const ROOT_NAME = "MediaPlayer";
124
+ const SEEK_NAME = "MediaPlayerSeek";
125
+ const SETTINGS_NAME = "MediaPlayerSettings";
126
+ const VOLUME_NAME = "MediaPlayerVolume";
127
+ const PLAYBACK_SPEED_NAME = "MediaPlayerPlaybackSpeed";
128
+ const FLOATING_MENU_SIDE_OFFSET = 10;
129
+ const SPEEDS = [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];
130
+ const SEEK_STEP_SHORT = 5;
131
+ const SEEK_STEP_LONG = 10;
132
+ const SEEK_COLLISION_PADDING = 10;
133
+ const SEEK_TOOLTIP_WIDTH_FALLBACK = 240;
134
+ const SEEK_HOVER_PERCENT = "--seek-hover-percent";
135
+ const SEEK_TOOLTIP_X = "--seek-tooltip-x";
136
+ const SEEK_TOOLTIP_Y = "--seek-tooltip-y";
137
+ const SPRITE_CONTAINER_WIDTH = 224;
138
+ const SPRITE_CONTAINER_HEIGHT = 128;
139
+ const StoreContext = React.createContext(null);
140
+ function useStoreContext(consumerName) {
141
+ const context = React.useContext(StoreContext);
142
+ if (!context) {
143
+ throw new Error(`\`${consumerName}\` must be used within \`${ROOT_NAME}\``);
144
+ }
145
+ return context;
146
+ }
147
+ function useStore(selector) {
148
+ const store = useStoreContext("useStore");
149
+ const getSnapshot = React.useCallback(() => selector(store.getState()), [store, selector]);
150
+ return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
151
+ }
152
+ const MediaPlayerContext = React.createContext(null);
153
+ function useMediaPlayerContext(consumerName) {
154
+ const context = React.useContext(MediaPlayerContext);
155
+ if (!context) {
156
+ throw new Error(`\`${consumerName}\` must be used within \`${ROOT_NAME}\``);
157
+ }
158
+ return context;
159
+ }
160
+ function MediaPlayer(props) {
161
+ const listenersRef = (0, use_lazy_ref_1.useLazyRef)(() => new Set());
162
+ const stateRef = (0, use_lazy_ref_1.useLazyRef)(() => ({
163
+ controlsVisible: true,
164
+ dragging: false,
165
+ menuOpen: false,
166
+ volumeIndicatorVisible: false,
167
+ }));
168
+ const store = React.useMemo(() => {
169
+ return {
170
+ subscribe: (cb) => {
171
+ listenersRef.current.add(cb);
172
+ return () => listenersRef.current.delete(cb);
173
+ },
174
+ getState: () => stateRef.current,
175
+ setState: (key, value) => {
176
+ if (Object.is(stateRef.current[key], value))
177
+ return;
178
+ stateRef.current[key] = value;
179
+ store.notify();
180
+ },
181
+ notify: () => {
182
+ for (const cb of listenersRef.current) {
183
+ cb();
184
+ }
185
+ },
186
+ };
187
+ }, [listenersRef, stateRef]);
188
+ return ((0, jsx_runtime_1.jsx)(tooltip_1.TooltipProvider, { children: (0, jsx_runtime_1.jsx)(media_store_1.MediaProvider, { children: (0, jsx_runtime_1.jsx)(StoreContext.Provider, { value: store, children: (0, jsx_runtime_1.jsx)(MediaPlayerImpl, Object.assign({}, props)) }) }) }));
189
+ }
190
+ function MediaPlayerImpl(props) {
191
+ var _a, _b;
192
+ const { onPlay, onPause, onEnded, onTimeUpdate, onFullscreenChange, onVolumeChange, onMuted, onMediaError, onPipError, dir: dirProp, label, tooltipDelayDuration = 600, tooltipSideOffset = FLOATING_MENU_SIDE_OFFSET, asChild, autoHide = false, disabled = false, withoutTooltip = false, children, className, ref } = props, rootImplProps = __rest(props, ["onPlay", "onPause", "onEnded", "onTimeUpdate", "onFullscreenChange", "onVolumeChange", "onMuted", "onMediaError", "onPipError", "dir", "label", "tooltipDelayDuration", "tooltipSideOffset", "asChild", "autoHide", "disabled", "withoutTooltip", "children", "className", "ref"]);
193
+ const mediaId = React.useId();
194
+ const labelId = React.useId();
195
+ const descriptionId = React.useId();
196
+ const rootRef = React.useRef(null);
197
+ const fullscreenRef = (0, media_store_1.useMediaFullscreenRef)();
198
+ const composedRef = (0, compose_refs_1.useComposedRefs)(ref, rootRef, fullscreenRef);
199
+ const dir = DirectionPrimitive.useDirection(dirProp);
200
+ const dispatch = (0, media_store_1.useMediaDispatch)();
201
+ const mediaRef = React.useRef(null);
202
+ const store = useStoreContext(ROOT_NAME);
203
+ const controlsVisible = useStore((state) => state.controlsVisible);
204
+ const dragging = useStore((state) => state.dragging);
205
+ const menuOpen = useStore((state) => state.menuOpen);
206
+ const hideControlsTimeoutRef = React.useRef(null);
207
+ const lastMouseMoveRef = React.useRef(Date.now());
208
+ const volumeIndicatorTimeoutRef = React.useRef(null);
209
+ const mediaPaused = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaPaused) !== null && _a !== void 0 ? _a : true; });
210
+ const isFullscreen = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaIsFullscreen) !== null && _a !== void 0 ? _a : false; });
211
+ const [mounted, setMounted] = React.useState(false);
212
+ React.useLayoutEffect(() => {
213
+ setMounted(true);
214
+ }, []);
215
+ const portalContainer = mounted
216
+ ? isFullscreen
217
+ ? rootRef.current
218
+ : globalThis.document.body
219
+ : null;
220
+ const isVideo = (typeof HTMLVideoElement !== "undefined" &&
221
+ mediaRef.current instanceof HTMLVideoElement) ||
222
+ ((_b = (_a = mediaRef.current) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === "mux-player";
223
+ const onControlsShow = React.useCallback(() => {
224
+ store.setState("controlsVisible", true);
225
+ lastMouseMoveRef.current = Date.now();
226
+ if (hideControlsTimeoutRef.current) {
227
+ clearTimeout(hideControlsTimeoutRef.current);
228
+ }
229
+ if (autoHide && !mediaPaused && !menuOpen && !dragging) {
230
+ hideControlsTimeoutRef.current = setTimeout(() => {
231
+ store.setState("controlsVisible", false);
232
+ }, 3000);
233
+ }
234
+ }, [store.setState, autoHide, mediaPaused, menuOpen, dragging]);
235
+ const onVolumeIndicatorTrigger = React.useCallback(() => {
236
+ if (menuOpen)
237
+ return;
238
+ store.setState("volumeIndicatorVisible", true);
239
+ if (volumeIndicatorTimeoutRef.current) {
240
+ clearTimeout(volumeIndicatorTimeoutRef.current);
241
+ }
242
+ volumeIndicatorTimeoutRef.current = setTimeout(() => {
243
+ store.setState("volumeIndicatorVisible", false);
244
+ }, 2000);
245
+ if (autoHide) {
246
+ onControlsShow();
247
+ }
248
+ }, [store.setState, menuOpen, autoHide, onControlsShow]);
249
+ const onMouseLeave = React.useCallback((event) => {
250
+ var _a;
251
+ (_a = rootImplProps.onMouseLeave) === null || _a === void 0 ? void 0 : _a.call(rootImplProps, event);
252
+ if (event.defaultPrevented)
253
+ return;
254
+ if (autoHide && !mediaPaused && !menuOpen && !dragging) {
255
+ store.setState("controlsVisible", false);
256
+ }
257
+ }, [
258
+ store.setState,
259
+ rootImplProps.onMouseLeave,
260
+ autoHide,
261
+ mediaPaused,
262
+ menuOpen,
263
+ dragging,
264
+ ]);
265
+ const onMouseMove = React.useCallback((event) => {
266
+ var _a;
267
+ (_a = rootImplProps.onMouseMove) === null || _a === void 0 ? void 0 : _a.call(rootImplProps, event);
268
+ if (event.defaultPrevented)
269
+ return;
270
+ if (autoHide) {
271
+ onControlsShow();
272
+ }
273
+ }, [autoHide, rootImplProps.onMouseMove, onControlsShow]);
274
+ React.useEffect(() => {
275
+ if (mediaPaused || menuOpen || dragging) {
276
+ store.setState("controlsVisible", true);
277
+ if (hideControlsTimeoutRef.current) {
278
+ clearTimeout(hideControlsTimeoutRef.current);
279
+ }
280
+ return;
281
+ }
282
+ if (autoHide) {
283
+ onControlsShow();
284
+ }
285
+ }, [
286
+ store.setState,
287
+ onControlsShow,
288
+ autoHide,
289
+ menuOpen,
290
+ mediaPaused,
291
+ dragging,
292
+ ]);
293
+ const onKeyDown = React.useCallback((event) => {
294
+ var _a, _b, _c, _d;
295
+ if (disabled)
296
+ return;
297
+ (_a = rootImplProps.onKeyDown) === null || _a === void 0 ? void 0 : _a.call(rootImplProps, event);
298
+ if (event.defaultPrevented)
299
+ return;
300
+ const mediaElement = mediaRef.current;
301
+ if (!mediaElement)
302
+ return;
303
+ const isMediaFocused = document.activeElement === mediaElement;
304
+ const isPlayerFocused = ((_b = document.activeElement) === null || _b === void 0 ? void 0 : _b.closest('[data-slot="media-player"]')) !== null;
305
+ if (!isMediaFocused && !isPlayerFocused)
306
+ return;
307
+ if (autoHide)
308
+ onControlsShow();
309
+ switch (event.key.toLowerCase()) {
310
+ case " ":
311
+ case "k":
312
+ event.preventDefault();
313
+ dispatch({
314
+ type: mediaElement.paused
315
+ ? media_store_1.MediaActionTypes.MEDIA_PLAY_REQUEST
316
+ : media_store_1.MediaActionTypes.MEDIA_PAUSE_REQUEST,
317
+ });
318
+ break;
319
+ case "f":
320
+ event.preventDefault();
321
+ dispatch({
322
+ type: document.fullscreenElement
323
+ ? media_store_1.MediaActionTypes.MEDIA_EXIT_FULLSCREEN_REQUEST
324
+ : media_store_1.MediaActionTypes.MEDIA_ENTER_FULLSCREEN_REQUEST,
325
+ });
326
+ break;
327
+ case "m": {
328
+ event.preventDefault();
329
+ if (isVideo) {
330
+ onVolumeIndicatorTrigger();
331
+ }
332
+ dispatch({
333
+ type: mediaElement.muted
334
+ ? media_store_1.MediaActionTypes.MEDIA_UNMUTE_REQUEST
335
+ : media_store_1.MediaActionTypes.MEDIA_MUTE_REQUEST,
336
+ });
337
+ break;
338
+ }
339
+ case "arrowright":
340
+ event.preventDefault();
341
+ if (isVideo ||
342
+ (mediaElement instanceof HTMLAudioElement && event.shiftKey)) {
343
+ dispatch({
344
+ type: media_store_1.MediaActionTypes.MEDIA_SEEK_REQUEST,
345
+ detail: Math.min(mediaElement.duration, mediaElement.currentTime + SEEK_STEP_SHORT),
346
+ });
347
+ }
348
+ break;
349
+ case "arrowleft":
350
+ event.preventDefault();
351
+ if (isVideo ||
352
+ (mediaElement instanceof HTMLAudioElement && event.shiftKey)) {
353
+ dispatch({
354
+ type: media_store_1.MediaActionTypes.MEDIA_SEEK_REQUEST,
355
+ detail: Math.max(0, mediaElement.currentTime - SEEK_STEP_SHORT),
356
+ });
357
+ }
358
+ break;
359
+ case "arrowup":
360
+ event.preventDefault();
361
+ if (isVideo) {
362
+ onVolumeIndicatorTrigger();
363
+ dispatch({
364
+ type: media_store_1.MediaActionTypes.MEDIA_VOLUME_REQUEST,
365
+ detail: Math.min(1, mediaElement.volume + 0.1),
366
+ });
367
+ }
368
+ break;
369
+ case "arrowdown":
370
+ event.preventDefault();
371
+ if (isVideo) {
372
+ onVolumeIndicatorTrigger();
373
+ dispatch({
374
+ type: media_store_1.MediaActionTypes.MEDIA_VOLUME_REQUEST,
375
+ detail: Math.max(0, mediaElement.volume - 0.1),
376
+ });
377
+ }
378
+ break;
379
+ case "<": {
380
+ event.preventDefault();
381
+ const currentRate = mediaElement.playbackRate;
382
+ const currentIndex = SPEEDS.indexOf(currentRate);
383
+ const newIndex = Math.max(0, currentIndex - 1);
384
+ const newRate = (_c = SPEEDS[newIndex]) !== null && _c !== void 0 ? _c : 1;
385
+ dispatch({
386
+ type: media_store_1.MediaActionTypes.MEDIA_PLAYBACK_RATE_REQUEST,
387
+ detail: newRate,
388
+ });
389
+ break;
390
+ }
391
+ case ">": {
392
+ event.preventDefault();
393
+ const currentRate = mediaElement.playbackRate;
394
+ const currentIndex = SPEEDS.indexOf(currentRate);
395
+ const newIndex = Math.min(SPEEDS.length - 1, currentIndex + 1);
396
+ const newRate = (_d = SPEEDS[newIndex]) !== null && _d !== void 0 ? _d : 1;
397
+ dispatch({
398
+ type: media_store_1.MediaActionTypes.MEDIA_PLAYBACK_RATE_REQUEST,
399
+ detail: newRate,
400
+ });
401
+ break;
402
+ }
403
+ case "c":
404
+ event.preventDefault();
405
+ if (isVideo && mediaElement.textTracks.length > 0) {
406
+ dispatch({
407
+ type: media_store_1.MediaActionTypes.MEDIA_TOGGLE_SUBTITLES_REQUEST,
408
+ });
409
+ }
410
+ break;
411
+ case "d": {
412
+ const hasDownload = mediaElement.querySelector('[data-slot="media-player-download"]');
413
+ if (!hasDownload)
414
+ break;
415
+ event.preventDefault();
416
+ if (mediaElement.currentSrc) {
417
+ const link = document.createElement("a");
418
+ link.href = mediaElement.currentSrc;
419
+ link.download = "";
420
+ document.body.appendChild(link);
421
+ link.click();
422
+ document.body.removeChild(link);
423
+ }
424
+ break;
425
+ }
426
+ case "p": {
427
+ event.preventDefault();
428
+ if (isVideo && "requestPictureInPicture" in mediaElement) {
429
+ const isPip = document.pictureInPictureElement === mediaElement;
430
+ dispatch({
431
+ type: isPip
432
+ ? media_store_1.MediaActionTypes.MEDIA_EXIT_PIP_REQUEST
433
+ : media_store_1.MediaActionTypes.MEDIA_ENTER_PIP_REQUEST,
434
+ });
435
+ if (isPip) {
436
+ document.exitPictureInPicture().catch((error) => {
437
+ onPipError === null || onPipError === void 0 ? void 0 : onPipError(error, "exit");
438
+ });
439
+ }
440
+ else {
441
+ mediaElement.requestPictureInPicture().catch((error) => {
442
+ onPipError === null || onPipError === void 0 ? void 0 : onPipError(error, "enter");
443
+ });
444
+ }
445
+ }
446
+ break;
447
+ }
448
+ case "r": {
449
+ event.preventDefault();
450
+ mediaElement.loop = !mediaElement.loop;
451
+ break;
452
+ }
453
+ case "j": {
454
+ event.preventDefault();
455
+ dispatch({
456
+ type: media_store_1.MediaActionTypes.MEDIA_SEEK_REQUEST,
457
+ detail: Math.max(0, mediaElement.currentTime - SEEK_STEP_LONG),
458
+ });
459
+ break;
460
+ }
461
+ case "l": {
462
+ event.preventDefault();
463
+ dispatch({
464
+ type: media_store_1.MediaActionTypes.MEDIA_SEEK_REQUEST,
465
+ detail: Math.min(mediaElement.duration, mediaElement.currentTime + SEEK_STEP_LONG),
466
+ });
467
+ break;
468
+ }
469
+ case "0":
470
+ case "1":
471
+ case "2":
472
+ case "3":
473
+ case "4":
474
+ case "5":
475
+ case "6":
476
+ case "7":
477
+ case "8":
478
+ case "9": {
479
+ event.preventDefault();
480
+ const percent = Number.parseInt(event.key, 10) / 10;
481
+ const seekTime = mediaElement.duration * percent;
482
+ dispatch({
483
+ type: media_store_1.MediaActionTypes.MEDIA_SEEK_REQUEST,
484
+ detail: seekTime,
485
+ });
486
+ break;
487
+ }
488
+ case "home": {
489
+ event.preventDefault();
490
+ dispatch({
491
+ type: media_store_1.MediaActionTypes.MEDIA_SEEK_REQUEST,
492
+ detail: 0,
493
+ });
494
+ break;
495
+ }
496
+ case "end": {
497
+ event.preventDefault();
498
+ dispatch({
499
+ type: media_store_1.MediaActionTypes.MEDIA_SEEK_REQUEST,
500
+ detail: mediaElement.duration,
501
+ });
502
+ break;
503
+ }
504
+ }
505
+ }, [
506
+ dispatch,
507
+ rootImplProps.onKeyDown,
508
+ onVolumeIndicatorTrigger,
509
+ onPipError,
510
+ disabled,
511
+ isVideo,
512
+ onControlsShow,
513
+ autoHide,
514
+ ]);
515
+ const onKeyUp = React.useCallback((event) => {
516
+ var _a;
517
+ (_a = rootImplProps.onKeyUp) === null || _a === void 0 ? void 0 : _a.call(rootImplProps, event);
518
+ const key = event.key.toLowerCase();
519
+ if (key === "arrowup" || key === "arrowdown" || key === "m") {
520
+ onVolumeIndicatorTrigger();
521
+ }
522
+ }, [rootImplProps.onKeyUp, onVolumeIndicatorTrigger]);
523
+ React.useEffect(() => {
524
+ const mediaElement = mediaRef.current;
525
+ if (!mediaElement)
526
+ return;
527
+ if (onPlay)
528
+ mediaElement.addEventListener("play", onPlay);
529
+ if (onPause)
530
+ mediaElement.addEventListener("pause", onPause);
531
+ if (onEnded)
532
+ mediaElement.addEventListener("ended", onEnded);
533
+ if (onTimeUpdate)
534
+ mediaElement.addEventListener("timeupdate", () => onTimeUpdate === null || onTimeUpdate === void 0 ? void 0 : onTimeUpdate(mediaElement.currentTime));
535
+ if (onVolumeChange)
536
+ mediaElement.addEventListener("volumechange", () => {
537
+ onVolumeChange === null || onVolumeChange === void 0 ? void 0 : onVolumeChange(mediaElement.volume);
538
+ onMuted === null || onMuted === void 0 ? void 0 : onMuted(mediaElement.muted);
539
+ });
540
+ if (onMediaError)
541
+ mediaElement.addEventListener("error", () => onMediaError === null || onMediaError === void 0 ? void 0 : onMediaError(mediaElement.error));
542
+ if (onFullscreenChange) {
543
+ document.addEventListener("fullscreenchange", () => onFullscreenChange === null || onFullscreenChange === void 0 ? void 0 : onFullscreenChange(!!document.fullscreenElement));
544
+ }
545
+ return () => {
546
+ if (onPlay)
547
+ mediaElement.removeEventListener("play", onPlay);
548
+ if (onPause)
549
+ mediaElement.removeEventListener("pause", onPause);
550
+ if (onEnded)
551
+ mediaElement.removeEventListener("ended", onEnded);
552
+ if (onTimeUpdate)
553
+ mediaElement.removeEventListener("timeupdate", () => onTimeUpdate === null || onTimeUpdate === void 0 ? void 0 : onTimeUpdate(mediaElement.currentTime));
554
+ if (onVolumeChange)
555
+ mediaElement.removeEventListener("volumechange", () => {
556
+ onVolumeChange === null || onVolumeChange === void 0 ? void 0 : onVolumeChange(mediaElement.volume);
557
+ onMuted === null || onMuted === void 0 ? void 0 : onMuted(mediaElement.muted);
558
+ });
559
+ if (onMediaError)
560
+ mediaElement.removeEventListener("error", () => onMediaError === null || onMediaError === void 0 ? void 0 : onMediaError(mediaElement.error));
561
+ if (onFullscreenChange) {
562
+ document.removeEventListener("fullscreenchange", () => onFullscreenChange === null || onFullscreenChange === void 0 ? void 0 : onFullscreenChange(!!document.fullscreenElement));
563
+ }
564
+ if (volumeIndicatorTimeoutRef.current) {
565
+ clearTimeout(volumeIndicatorTimeoutRef.current);
566
+ }
567
+ if (hideControlsTimeoutRef.current) {
568
+ clearTimeout(hideControlsTimeoutRef.current);
569
+ }
570
+ };
571
+ }, [
572
+ onPlay,
573
+ onPause,
574
+ onEnded,
575
+ onTimeUpdate,
576
+ onVolumeChange,
577
+ onMuted,
578
+ onMediaError,
579
+ onFullscreenChange,
580
+ ]);
581
+ const contextValue = React.useMemo(() => ({
582
+ mediaId,
583
+ labelId,
584
+ descriptionId,
585
+ dir,
586
+ rootRef,
587
+ mediaRef,
588
+ portalContainer,
589
+ tooltipDelayDuration,
590
+ tooltipSideOffset,
591
+ disabled,
592
+ isVideo,
593
+ withoutTooltip,
594
+ }), [
595
+ mediaId,
596
+ labelId,
597
+ descriptionId,
598
+ dir,
599
+ portalContainer,
600
+ tooltipDelayDuration,
601
+ tooltipSideOffset,
602
+ disabled,
603
+ isVideo,
604
+ withoutTooltip,
605
+ ]);
606
+ const RootPrimitive = asChild ? SlotPrimitive.Slot : "div";
607
+ return ((0, jsx_runtime_1.jsx)(MediaPlayerContext.Provider, { value: contextValue, children: (0, jsx_runtime_1.jsxs)(RootPrimitive, Object.assign({ "aria-labelledby": labelId, "aria-describedby": descriptionId, "aria-disabled": disabled, "data-disabled": disabled ? "" : undefined, "data-controls-visible": controlsVisible ? "" : undefined, "data-slot": "media-player", "data-state": isFullscreen ? "fullscreen" : "windowed", dir: dir, tabIndex: disabled ? undefined : 0 }, rootImplProps, { ref: composedRef, onMouseLeave: onMouseLeave, onMouseMove: onMouseMove, onKeyDown: onKeyDown, onKeyUp: onKeyUp, className: (0, utils_1.cn)("dark relative isolate flex size-full flex-col overflow-hidden rounded-lg bg-background outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_video]:relative [&_video]:object-contain", "in-[:fullscreen]:flex in-[:fullscreen]:h-full in-[:fullscreen]:max-h-screen in-[:fullscreen]:flex-col in-[:fullscreen]:justify-between data-[state=fullscreen]:[&_video]:size-full", "**:data-slider:relative [&_[data-slider]::before]:absolute [&_[data-slider]::before]:inset-x-0 [&_[data-slider]::before]:-top-4 [&_[data-slider]::before]:-bottom-2 [&_[data-slider]::before]:z-10 [&_[data-slider]::before]:h-8 [&_[data-slider]::before]:cursor-pointer [&_[data-slider]::before]:content-[''] [&_[data-slot='media-player-seek']:not([data-hovering])::before]:cursor-default", "[&_video::-webkit-media-text-track-display]:top-auto! [&_video::-webkit-media-text-track-display]:bottom-[4%]! [&_video::-webkit-media-text-track-display]:mb-0! data-controls-visible:[&_video::-webkit-media-text-track-display]:bottom-[13%]! data-[state=fullscreen]:[&_video::-webkit-media-text-track-display]:bottom-[7%]! data-[state=fullscreen]:data-controls-visible:[&_video::-webkit-media-text-track-display]:bottom-[9%]!", className), children: [(0, jsx_runtime_1.jsx)("span", { id: labelId, className: "sr-only", children: label !== null && label !== void 0 ? label : "Media player" }), (0, jsx_runtime_1.jsx)("span", { id: descriptionId, className: "sr-only", children: isVideo
608
+ ? "Video player with custom controls for playback, volume, seeking, and more. Use space bar to play/pause, arrow keys (←/→) to seek, and arrow keys (↑/↓) to adjust volume."
609
+ : "Audio player with custom controls for playback, volume, seeking, and more. Use space bar to play/pause, Shift + arrow keys (←/→) to seek, and arrow keys (↑/↓) to adjust volume." }), children, (0, jsx_runtime_1.jsx)(MediaPlayerVolumeIndicator, {})] })) }));
610
+ }
611
+ function MediaPlayerVideo(props) {
612
+ const { asChild, ref } = props, videoProps = __rest(props, ["asChild", "ref"]);
613
+ const context = useMediaPlayerContext("MediaPlayerVideo");
614
+ const dispatch = (0, media_store_1.useMediaDispatch)();
615
+ const mediaRefCallback = (0, media_store_1.useMediaRef)();
616
+ const composedRef = (0, compose_refs_1.useComposedRefs)(ref, context.mediaRef, mediaRefCallback);
617
+ const onPlayToggle = React.useCallback((event) => {
618
+ var _a;
619
+ (_a = props.onClick) === null || _a === void 0 ? void 0 : _a.call(props, event);
620
+ if (event.defaultPrevented)
621
+ return;
622
+ const mediaElement = event.currentTarget;
623
+ if (!mediaElement)
624
+ return;
625
+ dispatch({
626
+ type: mediaElement.paused
627
+ ? media_store_1.MediaActionTypes.MEDIA_PLAY_REQUEST
628
+ : media_store_1.MediaActionTypes.MEDIA_PAUSE_REQUEST,
629
+ });
630
+ }, [dispatch, props.onClick]);
631
+ const VideoPrimitive = asChild ? SlotPrimitive.Slot : "video";
632
+ return ((0, jsx_runtime_1.jsx)(VideoPrimitive, Object.assign({ "aria-describedby": context.descriptionId, "aria-labelledby": context.labelId, "data-slot": "media-player-video" }, videoProps, { id: context.mediaId, ref: composedRef, onClick: onPlayToggle })));
633
+ }
634
+ function MediaPlayerAudio(props) {
635
+ const { asChild, ref } = props, audioProps = __rest(props, ["asChild", "ref"]);
636
+ const context = useMediaPlayerContext("MediaPlayerAudio");
637
+ const mediaRefCallback = (0, media_store_1.useMediaRef)();
638
+ const composedRef = (0, compose_refs_1.useComposedRefs)(ref, context.mediaRef, mediaRefCallback);
639
+ const AudioPrimitive = asChild ? SlotPrimitive.Slot : "audio";
640
+ return ((0, jsx_runtime_1.jsx)(AudioPrimitive, Object.assign({ "aria-describedby": context.descriptionId, "aria-labelledby": context.labelId, "data-slot": "media-player-audio" }, audioProps, { id: context.mediaId, ref: composedRef })));
641
+ }
642
+ function MediaPlayerControls(props) {
643
+ const { asChild, className } = props, controlsProps = __rest(props, ["asChild", "className"]);
644
+ const context = useMediaPlayerContext("MediaPlayerControls");
645
+ const isFullscreen = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaIsFullscreen) !== null && _a !== void 0 ? _a : false; });
646
+ const controlsVisible = useStore((state) => state.controlsVisible);
647
+ const ControlsPrimitive = asChild ? SlotPrimitive.Slot : "div";
648
+ return ((0, jsx_runtime_1.jsx)(ControlsPrimitive, Object.assign({ "data-disabled": context.disabled ? "" : undefined, "data-slot": "media-player-controls", "data-state": isFullscreen ? "fullscreen" : "windowed", "data-visible": controlsVisible ? "" : undefined, dir: context.dir, className: (0, utils_1.cn)("dark pointer-events-none absolute right-0 bottom-0 left-0 z-50 flex items-center gap-2 px-4 py-3 opacity-0 transition-opacity duration-200 in-[:fullscreen]:px-6 in-[:fullscreen]:py-4 data-visible:pointer-events-auto data-visible:opacity-100", className) }, controlsProps)));
649
+ }
650
+ function MediaPlayerLoading(props) {
651
+ const { delayMs = 500, asChild, className, children } = props, loadingProps = __rest(props, ["delayMs", "asChild", "className", "children"]);
652
+ const isLoading = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaLoading) !== null && _a !== void 0 ? _a : false; });
653
+ const isPaused = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaPaused) !== null && _a !== void 0 ? _a : true; });
654
+ const hasPlayed = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaHasPlayed) !== null && _a !== void 0 ? _a : false; });
655
+ const shouldShowLoading = isLoading && !isPaused;
656
+ const shouldUseDelay = hasPlayed && shouldShowLoading;
657
+ const loadingDelayMs = shouldUseDelay ? delayMs : 0;
658
+ const [shouldRender, setShouldRender] = React.useState(false);
659
+ const timeoutRef = React.useRef(null);
660
+ React.useEffect(() => {
661
+ if (timeoutRef.current) {
662
+ clearTimeout(timeoutRef.current);
663
+ timeoutRef.current = null;
664
+ }
665
+ if (shouldShowLoading) {
666
+ if (loadingDelayMs > 0) {
667
+ timeoutRef.current = setTimeout(() => {
668
+ setShouldRender(true);
669
+ timeoutRef.current = null;
670
+ }, loadingDelayMs);
671
+ }
672
+ else {
673
+ setShouldRender(true);
674
+ }
675
+ }
676
+ else {
677
+ setShouldRender(false);
678
+ }
679
+ return () => {
680
+ if (timeoutRef.current) {
681
+ clearTimeout(timeoutRef.current);
682
+ timeoutRef.current = null;
683
+ }
684
+ };
685
+ }, [shouldShowLoading, loadingDelayMs]);
686
+ if (!shouldRender)
687
+ return null;
688
+ const LoadingPrimitive = asChild ? SlotPrimitive.Slot : "div";
689
+ return ((0, jsx_runtime_1.jsx)(LoadingPrimitive, Object.assign({ role: "status", "aria-live": "polite", "data-slot": "media-player-loading" }, loadingProps, { className: (0, utils_1.cn)("pointer-events-none absolute inset-0 z-50 flex animate-in items-center justify-center duration-200 fade-in-0 zoom-in-95", className), children: children !== null && children !== void 0 ? children : ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "Loader2Icon", tabler: "IconLoader2", hugeicons: "Loading03Icon", phosphor: "SpinnerIcon", remixicon: "RiLoader4Line", className: "size-20 animate-spin stroke-[.0938rem] text-primary" })) })));
690
+ }
691
+ function MediaPlayerError(props) {
692
+ const { error: errorProp, label, description, onRetry: onRetryProp, onReload: onReloadProp, asChild, className, children } = props, errorProps = __rest(props, ["error", "label", "description", "onRetry", "onReload", "asChild", "className", "children"]);
693
+ const context = useMediaPlayerContext("MediaPlayerError");
694
+ const isFullscreen = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaIsFullscreen) !== null && _a !== void 0 ? _a : false; });
695
+ const mediaError = (0, media_store_1.useMediaSelector)((state) => state.mediaError);
696
+ const error = errorProp !== null && errorProp !== void 0 ? errorProp : mediaError;
697
+ const labelId = React.useId();
698
+ const descriptionId = React.useId();
699
+ const [actionState, setActionState] = React.useState({
700
+ retryPending: false,
701
+ reloadPending: false,
702
+ });
703
+ const onRetry = React.useCallback(() => {
704
+ setActionState((prev) => (Object.assign(Object.assign({}, prev), { retryPending: true })));
705
+ requestAnimationFrame(() => {
706
+ var _a;
707
+ const mediaElement = context.mediaRef.current;
708
+ if (!mediaElement) {
709
+ setActionState((prev) => (Object.assign(Object.assign({}, prev), { retryPending: false })));
710
+ return;
711
+ }
712
+ if (onRetryProp) {
713
+ onRetryProp();
714
+ }
715
+ else {
716
+ const currentSrc = (_a = mediaElement.currentSrc) !== null && _a !== void 0 ? _a : mediaElement.src;
717
+ if (currentSrc) {
718
+ mediaElement.load();
719
+ }
720
+ }
721
+ setActionState((prev) => (Object.assign(Object.assign({}, prev), { retryPending: false })));
722
+ });
723
+ }, [context.mediaRef, onRetryProp]);
724
+ const onReload = React.useCallback(() => {
725
+ setActionState((prev) => (Object.assign(Object.assign({}, prev), { reloadPending: true })));
726
+ requestAnimationFrame(() => {
727
+ if (onReloadProp) {
728
+ onReloadProp();
729
+ }
730
+ else {
731
+ window.location.reload();
732
+ }
733
+ });
734
+ }, [onReloadProp]);
735
+ const errorLabel = React.useMemo(() => {
736
+ var _a;
737
+ if (label)
738
+ return label;
739
+ if (!error)
740
+ return "Playback Error";
741
+ const labelMap = {
742
+ [MediaError.MEDIA_ERR_ABORTED]: "Playback Interrupted",
743
+ [MediaError.MEDIA_ERR_NETWORK]: "Connection Problem",
744
+ [MediaError.MEDIA_ERR_DECODE]: "Media Error",
745
+ [MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED]: "Unsupported Format",
746
+ };
747
+ return (_a = labelMap[error.code]) !== null && _a !== void 0 ? _a : "Playback Error";
748
+ }, [label, error]);
749
+ const errorDescription = React.useMemo(() => {
750
+ var _a;
751
+ if (description)
752
+ return description;
753
+ if (!error)
754
+ return "An unknown error occurred";
755
+ const descriptionMap = {
756
+ [MediaError.MEDIA_ERR_ABORTED]: "Media playback was aborted",
757
+ [MediaError.MEDIA_ERR_NETWORK]: "A network error occurred while loading the media",
758
+ [MediaError.MEDIA_ERR_DECODE]: "An error occurred while decoding the media",
759
+ [MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED]: "The media format is not supported",
760
+ };
761
+ return (_a = descriptionMap[error.code]) !== null && _a !== void 0 ? _a : "An unknown error occurred";
762
+ }, [description, error]);
763
+ if (!error)
764
+ return null;
765
+ const ErrorPrimitive = asChild ? SlotPrimitive.Slot : "div";
766
+ return ((0, jsx_runtime_1.jsx)(ErrorPrimitive, Object.assign({ role: "alert", "aria-describedby": descriptionId, "aria-labelledby": labelId, "aria-live": "assertive", "data-slot": "media-player-error", "data-state": isFullscreen ? "fullscreen" : "windowed" }, errorProps, { className: (0, utils_1.cn)("pointer-events-auto absolute inset-0 z-50 flex flex-col items-center justify-center bg-black/80 text-white backdrop-blur-sm", className), children: children !== null && children !== void 0 ? children : ((0, jsx_runtime_1.jsxs)("div", { className: "flex max-w-md flex-col items-center gap-4 px-6 py-8 text-center", children: [(0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "AlertTriangleIcon", tabler: "IconAlertTriangle", hugeicons: "Alert02Icon", phosphor: "WarningIcon", remixicon: "RiAlertLine", className: "size-12 text-destructive" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-px text-center", children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-xl font-semibold tracking-tight", children: errorLabel }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm leading-relaxed text-balance text-muted-foreground", children: errorDescription })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsxs)(Button, { variant: "secondary", size: "sm", onClick: onRetry, disabled: actionState.retryPending, children: [actionState.retryPending ? ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "Loader2Icon", tabler: "IconLoader2", hugeicons: "Loading03Icon", phosphor: "SpinnerIcon", remixicon: "RiLoader4Line", className: "animate-spin" })) : ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "RefreshCcwIcon", tabler: "IconRefresh", hugeicons: "RefreshIcon", phosphor: "ArrowsClockwiseIcon", remixicon: "RiRefreshLine" })), "Try again"] }), (0, jsx_runtime_1.jsxs)(Button, { variant: "outline", size: "sm", onClick: onReload, disabled: actionState.reloadPending, children: [actionState.reloadPending ? ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "Loader2Icon", tabler: "IconLoader2", hugeicons: "Loading03Icon", phosphor: "SpinnerIcon", remixicon: "RiLoader4Line", className: "animate-spin" })) : ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "RotateCcwIcon", tabler: "IconRotate2", hugeicons: "Rotate01Icon", phosphor: "ArrowCounterClockwiseIcon", remixicon: "RiResetLeftLine" })), "Reload page"] })] })] })) })));
767
+ }
768
+ function MediaPlayerVolumeIndicator(props) {
769
+ const { asChild, className } = props, indicatorProps = __rest(props, ["asChild", "className"]);
770
+ const mediaVolume = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaVolume) !== null && _a !== void 0 ? _a : 1; });
771
+ const mediaMuted = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaMuted) !== null && _a !== void 0 ? _a : false; });
772
+ const mediaVolumeLevel = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaVolumeLevel) !== null && _a !== void 0 ? _a : "high"; });
773
+ const volumeIndicatorVisible = useStore((state) => state.volumeIndicatorVisible);
774
+ if (!volumeIndicatorVisible)
775
+ return null;
776
+ const effectiveVolume = mediaMuted ? 0 : mediaVolume;
777
+ const volumePercentage = Math.round(effectiveVolume * 100);
778
+ const barCount = 10;
779
+ const activeBarCount = Math.ceil(effectiveVolume * barCount);
780
+ const VolumeIndicatorPrimitive = asChild ? SlotPrimitive.Slot : "div";
781
+ return ((0, jsx_runtime_1.jsx)(VolumeIndicatorPrimitive, Object.assign({ role: "status", "aria-live": "polite", "aria-label": `Volume ${mediaMuted ? "muted" : `${volumePercentage}%`}`, "data-slot": "media-player-volume-indicator" }, indicatorProps, { className: (0, utils_1.cn)("pointer-events-none absolute inset-0 z-50 flex items-center justify-center", className), children: (0, jsx_runtime_1.jsxs)("div", { className: "flex animate-in flex-col items-center gap-3 rounded-lg bg-black/30 px-6 py-4 text-white backdrop-blur-xs duration-200 fade-in-0 zoom-in-95", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [mediaVolumeLevel === "off" || mediaMuted ? ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "VolumeXIcon", tabler: "IconVolumeOff", hugeicons: "VolumeOffIcon", phosphor: "SpeakerSlashIcon", remixicon: "RiVolumeMuteLine", className: "size-6" })) : mediaVolumeLevel === "high" ? ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "Volume2Icon", tabler: "IconVolume", hugeicons: "VolumeHighIcon", phosphor: "SpeakerHighIcon", remixicon: "RiVolumeUpLine", className: "size-6" })) : ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "Volume1Icon", tabler: "IconVolume2", hugeicons: "VolumeLowIcon", phosphor: "SpeakerLowIcon", remixicon: "RiVolumeDownLine", className: "size-6" })), (0, jsx_runtime_1.jsx)("span", { className: "text-sm font-medium tabular-nums", children: mediaMuted ? "Muted" : `${volumePercentage}%` })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-1", children: Array.from({ length: barCount }, (_, index) => ((0, jsx_runtime_1.jsx)("div", { className: (0, utils_1.cn)("w-1.5 rounded-full transition-all duration-150", index < activeBarCount && !mediaMuted
782
+ ? "scale-100 bg-white"
783
+ : "scale-90 bg-white/30"), style: {
784
+ height: `${12 + index * 2}px`,
785
+ animationDelay: `${index * 50}ms`,
786
+ } }, index))) })] }) })));
787
+ }
788
+ function MediaPlayerControlsOverlay(props) {
789
+ const { asChild, className } = props, overlayProps = __rest(props, ["asChild", "className"]);
790
+ const isFullscreen = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaIsFullscreen) !== null && _a !== void 0 ? _a : false; });
791
+ const controlsVisible = useStore((state) => state.controlsVisible);
792
+ const OverlayPrimitive = asChild ? SlotPrimitive.Slot : "div";
793
+ return ((0, jsx_runtime_1.jsx)(OverlayPrimitive, Object.assign({ "data-slot": "media-player-controls-overlay", "data-state": isFullscreen ? "fullscreen" : "windowed", "data-visible": controlsVisible ? "" : undefined }, overlayProps, { className: (0, utils_1.cn)("pointer-events-none absolute inset-0 -z-10 bg-linear-to-t from-black/80 to-transparent opacity-0 transition-opacity duration-200 data-visible:opacity-100", className) })));
794
+ }
795
+ function MediaPlayerPlay(props) {
796
+ const { children, className, disabled } = props, playButtonProps = __rest(props, ["children", "className", "disabled"]);
797
+ const context = useMediaPlayerContext("MediaPlayerPlay");
798
+ const dispatch = (0, media_store_1.useMediaDispatch)();
799
+ const mediaPaused = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaPaused) !== null && _a !== void 0 ? _a : true; });
800
+ const isDisabled = disabled || context.disabled;
801
+ const onPlayToggle = React.useCallback((event) => {
802
+ var _a;
803
+ (_a = props.onClick) === null || _a === void 0 ? void 0 : _a.call(props, event);
804
+ if (event.defaultPrevented)
805
+ return;
806
+ dispatch({
807
+ type: mediaPaused
808
+ ? media_store_1.MediaActionTypes.MEDIA_PLAY_REQUEST
809
+ : media_store_1.MediaActionTypes.MEDIA_PAUSE_REQUEST,
810
+ });
811
+ }, [dispatch, props.onClick, mediaPaused]);
812
+ return ((0, jsx_runtime_1.jsx)(MediaPlayerTooltip, { tooltip: mediaPaused ? "Play" : "Pause", shortcut: "Space", children: (0, jsx_runtime_1.jsx)(Button, Object.assign({ type: "button", "aria-controls": context.mediaId, "aria-label": mediaPaused ? "Play" : "Pause", "aria-pressed": !mediaPaused, "data-disabled": isDisabled ? "" : undefined, "data-slot": "media-player-play-button", "data-state": mediaPaused ? "off" : "on", disabled: isDisabled }, playButtonProps, { variant: "ghost", size: "icon", className: (0, utils_1.cn)("size-8 [&_svg:not([class*='fill-'])]:fill-current", className), onClick: onPlayToggle, children: children !== null && children !== void 0 ? children : (mediaPaused ? ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "PlayIcon", tabler: "IconPlayerPlay", hugeicons: "PlayIcon", phosphor: "PlayIcon", remixicon: "RiPlayLine" })) : ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "PauseIcon", tabler: "IconPlayerPause", hugeicons: "PauseIcon", phosphor: "PauseIcon", remixicon: "RiPauseLine" }))) })) }));
813
+ }
814
+ function MediaPlayerSeekBackward(props) {
815
+ const { seconds = SEEK_STEP_SHORT, children, className, disabled } = props, seekBackwardProps = __rest(props, ["seconds", "children", "className", "disabled"]);
816
+ const context = useMediaPlayerContext("MediaPlayerSeekBackward");
817
+ const dispatch = (0, media_store_1.useMediaDispatch)();
818
+ const mediaCurrentTime = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaCurrentTime) !== null && _a !== void 0 ? _a : 0; });
819
+ const isDisabled = disabled || context.disabled;
820
+ const onSeekBackward = React.useCallback((event) => {
821
+ var _a;
822
+ (_a = props.onClick) === null || _a === void 0 ? void 0 : _a.call(props, event);
823
+ if (event.defaultPrevented)
824
+ return;
825
+ dispatch({
826
+ type: media_store_1.MediaActionTypes.MEDIA_SEEK_REQUEST,
827
+ detail: Math.max(0, mediaCurrentTime - seconds),
828
+ });
829
+ }, [dispatch, props.onClick, mediaCurrentTime, seconds]);
830
+ return ((0, jsx_runtime_1.jsx)(MediaPlayerTooltip, { tooltip: `Back ${seconds}s`, shortcut: context.isVideo ? ["←"] : ["Shift ←"], children: (0, jsx_runtime_1.jsx)(Button, Object.assign({ type: "button", "aria-controls": context.mediaId, "aria-label": `Back ${seconds} seconds`, "data-disabled": isDisabled ? "" : undefined, "data-slot": "media-player-seek-backward", disabled: isDisabled }, seekBackwardProps, { variant: "ghost", size: "icon", className: (0, utils_1.cn)("size-8", className), onClick: onSeekBackward, children: children !== null && children !== void 0 ? children : ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "RewindIcon", tabler: "IconRewindBackward10", hugeicons: "ReplayIcon", phosphor: "RewindIcon", remixicon: "RiReplay10Line" })) })) }));
831
+ }
832
+ function MediaPlayerSeekForward(props) {
833
+ const { seconds = SEEK_STEP_LONG, children, className, disabled } = props, seekForwardProps = __rest(props, ["seconds", "children", "className", "disabled"]);
834
+ const context = useMediaPlayerContext("MediaPlayerSeekForward");
835
+ const dispatch = (0, media_store_1.useMediaDispatch)();
836
+ const mediaCurrentTime = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaCurrentTime) !== null && _a !== void 0 ? _a : 0; });
837
+ const [, seekableEnd] = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaSeekable) !== null && _a !== void 0 ? _a : [0, 0]; });
838
+ const isDisabled = disabled || context.disabled;
839
+ const onSeekForward = React.useCallback((event) => {
840
+ var _a;
841
+ (_a = props.onClick) === null || _a === void 0 ? void 0 : _a.call(props, event);
842
+ if (event.defaultPrevented)
843
+ return;
844
+ dispatch({
845
+ type: media_store_1.MediaActionTypes.MEDIA_SEEK_REQUEST,
846
+ detail: Math.min(seekableEnd !== null && seekableEnd !== void 0 ? seekableEnd : Number.POSITIVE_INFINITY, mediaCurrentTime + seconds),
847
+ });
848
+ }, [dispatch, props.onClick, mediaCurrentTime, seekableEnd, seconds]);
849
+ return ((0, jsx_runtime_1.jsx)(MediaPlayerTooltip, { tooltip: `Forward ${seconds}s`, shortcut: context.isVideo ? ["→"] : ["Shift →"], children: (0, jsx_runtime_1.jsx)(Button, Object.assign({ type: "button", "aria-controls": context.mediaId, "aria-label": `Forward ${seconds} seconds`, "data-disabled": isDisabled ? "" : undefined, "data-slot": "media-player-seek-forward", disabled: isDisabled }, seekForwardProps, { variant: "ghost", size: "icon", className: (0, utils_1.cn)("size-8", className), onClick: onSeekForward, children: children !== null && children !== void 0 ? children : ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "FastForwardIcon", tabler: "IconPlayerSkipForward", hugeicons: "FastForwardIcon", phosphor: "FastForwardIcon", remixicon: "RiSpeedLine" })) })) }));
850
+ }
851
+ function MediaPlayerSeek(props) {
852
+ var _a;
853
+ const { withTime = false, withoutChapter = false, withoutTooltip = false, tooltipTimeVariant = "current", tooltipThumbnailSrc, tooltipSideOffset, tooltipCollisionPadding = SEEK_COLLISION_PADDING, tooltipCollisionBoundary, className, disabled } = props, seekProps = __rest(props, ["withTime", "withoutChapter", "withoutTooltip", "tooltipTimeVariant", "tooltipThumbnailSrc", "tooltipSideOffset", "tooltipCollisionPadding", "tooltipCollisionBoundary", "className", "disabled"]);
854
+ const context = useMediaPlayerContext(SEEK_NAME);
855
+ const store = useStoreContext(SEEK_NAME);
856
+ const dispatch = (0, media_store_1.useMediaDispatch)();
857
+ const mediaCurrentTime = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaCurrentTime) !== null && _a !== void 0 ? _a : 0; });
858
+ const [seekableStart = 0, seekableEnd = 0] = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaSeekable) !== null && _a !== void 0 ? _a : [0, 0]; });
859
+ const mediaBuffered = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaBuffered) !== null && _a !== void 0 ? _a : []; });
860
+ const mediaEnded = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaEnded) !== null && _a !== void 0 ? _a : false; });
861
+ const chapterCues = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaChaptersCues) !== null && _a !== void 0 ? _a : []; });
862
+ const mediaPreviewTime = (0, media_store_1.useMediaSelector)((state) => state.mediaPreviewTime);
863
+ const mediaPreviewImage = (0, media_store_1.useMediaSelector)((state) => state.mediaPreviewImage);
864
+ const mediaPreviewCoords = (0, media_store_1.useMediaSelector)((state) => state.mediaPreviewCoords);
865
+ const seekRef = React.useRef(null);
866
+ const tooltipRef = React.useRef(null);
867
+ const justCommittedRef = React.useRef(false);
868
+ const hoverTimeRef = React.useRef(0);
869
+ const tooltipXRef = React.useRef(0);
870
+ const tooltipYRef = React.useRef(0);
871
+ const seekRectRef = React.useRef(null);
872
+ const collisionDataRef = React.useRef(null);
873
+ const [seekState, setSeekState] = React.useState({
874
+ isHovering: false,
875
+ pendingSeekTime: null,
876
+ hasInitialPosition: false,
877
+ });
878
+ const rafIdRef = React.useRef(null);
879
+ const seekThrottleRef = React.useRef(null);
880
+ const hoverTimeoutRef = React.useRef(null);
881
+ const lastPointerXRef = React.useRef(0);
882
+ const lastPointerYRef = React.useRef(0);
883
+ const previewDebounceRef = React.useRef(null);
884
+ const pointerEnterTimeRef = React.useRef(0);
885
+ const horizontalMovementRef = React.useRef(0);
886
+ const verticalMovementRef = React.useRef(0);
887
+ const lastSeekCommitTimeRef = React.useRef(0);
888
+ const timeCache = React.useRef(new Map());
889
+ const displayValue = (_a = seekState.pendingSeekTime) !== null && _a !== void 0 ? _a : mediaCurrentTime;
890
+ const isDisabled = disabled || context.disabled;
891
+ const tooltipDisabled = withoutTooltip || context.withoutTooltip || store.getState().menuOpen;
892
+ const currentTooltipSideOffset = tooltipSideOffset !== null && tooltipSideOffset !== void 0 ? tooltipSideOffset : context.tooltipSideOffset;
893
+ const getCachedTime = React.useCallback((time, duration) => {
894
+ const roundedTime = Math.floor(time);
895
+ const key = roundedTime + duration * 10000;
896
+ if (timeCache.current.has(key)) {
897
+ return timeCache.current.get(key);
898
+ }
899
+ const formatted = media_store_1.timeUtils.formatTime(time, duration);
900
+ timeCache.current.set(key, formatted);
901
+ if (timeCache.current.size > 100) {
902
+ timeCache.current.clear();
903
+ }
904
+ return formatted;
905
+ }, []);
906
+ const currentTime = getCachedTime(displayValue, seekableEnd);
907
+ const duration = getCachedTime(seekableEnd, seekableEnd);
908
+ const remainingTime = getCachedTime(seekableEnd - displayValue, seekableEnd);
909
+ const onCollisionDataUpdate = React.useCallback(() => {
910
+ if (collisionDataRef.current)
911
+ return collisionDataRef.current;
912
+ const padding = typeof tooltipCollisionPadding === "number"
913
+ ? {
914
+ top: tooltipCollisionPadding,
915
+ right: tooltipCollisionPadding,
916
+ bottom: tooltipCollisionPadding,
917
+ left: tooltipCollisionPadding,
918
+ }
919
+ : Object.assign({ top: 0, right: 0, bottom: 0, left: 0 }, tooltipCollisionPadding);
920
+ const boundaries = tooltipCollisionBoundary
921
+ ? Array.isArray(tooltipCollisionBoundary)
922
+ ? tooltipCollisionBoundary
923
+ : [tooltipCollisionBoundary]
924
+ : [context.rootRef.current].filter(Boolean);
925
+ collisionDataRef.current = { padding, boundaries };
926
+ return collisionDataRef.current;
927
+ }, [tooltipCollisionPadding, tooltipCollisionBoundary, context.rootRef]);
928
+ const getCurrentChapterCue = React.useCallback((time) => {
929
+ if (withoutChapter || chapterCues.length === 0)
930
+ return null;
931
+ return chapterCues.find((c) => time >= c.startTime && time < c.endTime);
932
+ }, [chapterCues, withoutChapter]);
933
+ const getThumbnail = React.useCallback((time) => {
934
+ if (tooltipDisabled)
935
+ return null;
936
+ if (tooltipThumbnailSrc) {
937
+ const src = typeof tooltipThumbnailSrc === "function"
938
+ ? tooltipThumbnailSrc(time)
939
+ : tooltipThumbnailSrc;
940
+ return { src, coords: null };
941
+ }
942
+ if (mediaPreviewTime !== undefined &&
943
+ Math.abs(time - mediaPreviewTime) < 0.1 &&
944
+ mediaPreviewImage) {
945
+ return {
946
+ src: mediaPreviewImage,
947
+ coords: mediaPreviewCoords !== null && mediaPreviewCoords !== void 0 ? mediaPreviewCoords : null,
948
+ };
949
+ }
950
+ return null;
951
+ }, [
952
+ tooltipThumbnailSrc,
953
+ mediaPreviewTime,
954
+ mediaPreviewImage,
955
+ mediaPreviewCoords,
956
+ tooltipDisabled,
957
+ ]);
958
+ const onPreviewUpdate = React.useCallback((time) => {
959
+ if (tooltipDisabled)
960
+ return;
961
+ if (previewDebounceRef.current) {
962
+ cancelAnimationFrame(previewDebounceRef.current);
963
+ }
964
+ previewDebounceRef.current = requestAnimationFrame(() => {
965
+ dispatch({
966
+ type: media_store_1.MediaActionTypes.MEDIA_PREVIEW_REQUEST,
967
+ detail: time,
968
+ });
969
+ previewDebounceRef.current = null;
970
+ });
971
+ }, [dispatch, tooltipDisabled]);
972
+ const onTooltipPositionUpdate = React.useCallback((clientX) => {
973
+ var _a, _b, _c, _d;
974
+ if (!seekRef.current)
975
+ return;
976
+ const tooltipWidth = (_b = (_a = tooltipRef.current) === null || _a === void 0 ? void 0 : _a.offsetWidth) !== null && _b !== void 0 ? _b : SEEK_TOOLTIP_WIDTH_FALLBACK;
977
+ let x = clientX;
978
+ const y = (_d = (_c = seekRectRef.current) === null || _c === void 0 ? void 0 : _c.top) !== null && _d !== void 0 ? _d : 0;
979
+ const collisionData = onCollisionDataUpdate();
980
+ const halfTooltipWidth = tooltipWidth / 2;
981
+ let minLeft = 0;
982
+ let maxRight = window.innerWidth;
983
+ for (const boundary of collisionData.boundaries) {
984
+ const boundaryRect = boundary.getBoundingClientRect();
985
+ minLeft = Math.max(minLeft, boundaryRect.left + collisionData.padding.left);
986
+ maxRight = Math.min(maxRight, boundaryRect.right - collisionData.padding.right);
987
+ }
988
+ if (x - halfTooltipWidth < minLeft) {
989
+ x = minLeft + halfTooltipWidth;
990
+ }
991
+ else if (x + halfTooltipWidth > maxRight) {
992
+ x = maxRight - halfTooltipWidth;
993
+ }
994
+ const viewportPadding = SEEK_COLLISION_PADDING;
995
+ if (x - halfTooltipWidth < viewportPadding) {
996
+ x = viewportPadding + halfTooltipWidth;
997
+ }
998
+ else if (x + halfTooltipWidth > window.innerWidth - viewportPadding) {
999
+ x = window.innerWidth - viewportPadding - halfTooltipWidth;
1000
+ }
1001
+ tooltipXRef.current = x;
1002
+ tooltipYRef.current = y;
1003
+ if (tooltipRef.current) {
1004
+ tooltipRef.current.style.setProperty(SEEK_TOOLTIP_X, `${x}px`);
1005
+ tooltipRef.current.style.setProperty(SEEK_TOOLTIP_Y, `${y}px`);
1006
+ }
1007
+ if (!seekState.hasInitialPosition) {
1008
+ setSeekState((prev) => (Object.assign(Object.assign({}, prev), { hasInitialPosition: true })));
1009
+ }
1010
+ }, [onCollisionDataUpdate, seekState.hasInitialPosition]);
1011
+ const onHoverProgressUpdate = React.useCallback(() => {
1012
+ if (!seekRef.current || seekableEnd <= 0)
1013
+ return;
1014
+ const hoverPercent = Math.min(100, (hoverTimeRef.current / seekableEnd) * 100);
1015
+ seekRef.current.style.setProperty(SEEK_HOVER_PERCENT, `${hoverPercent.toFixed(4)}%`);
1016
+ }, [seekableEnd]);
1017
+ React.useEffect(() => {
1018
+ if (seekState.pendingSeekTime !== null) {
1019
+ const diff = Math.abs(mediaCurrentTime - seekState.pendingSeekTime);
1020
+ if (diff < 0.5) {
1021
+ setSeekState((prev) => (Object.assign(Object.assign({}, prev), { pendingSeekTime: null })));
1022
+ }
1023
+ }
1024
+ }, [mediaCurrentTime, seekState.pendingSeekTime]);
1025
+ React.useEffect(() => {
1026
+ if (!seekState.isHovering || tooltipDisabled)
1027
+ return;
1028
+ function onScroll() {
1029
+ setSeekState((prev) => (Object.assign(Object.assign({}, prev), { isHovering: false, hasInitialPosition: false })));
1030
+ dispatch({
1031
+ type: media_store_1.MediaActionTypes.MEDIA_PREVIEW_REQUEST,
1032
+ detail: undefined,
1033
+ });
1034
+ }
1035
+ document.addEventListener("scroll", onScroll, { passive: true });
1036
+ return () => {
1037
+ document.removeEventListener("scroll", onScroll);
1038
+ };
1039
+ }, [dispatch, seekState.isHovering, tooltipDisabled]);
1040
+ const bufferedProgress = React.useMemo(() => {
1041
+ if (mediaBuffered.length === 0 || seekableEnd <= 0)
1042
+ return 0;
1043
+ if (mediaEnded)
1044
+ return 1;
1045
+ const containingRange = mediaBuffered.find(([start, end]) => start <= mediaCurrentTime && mediaCurrentTime <= end);
1046
+ if (containingRange) {
1047
+ return Math.min(1, containingRange[1] / seekableEnd);
1048
+ }
1049
+ return Math.min(1, seekableStart / seekableEnd);
1050
+ }, [mediaBuffered, mediaCurrentTime, seekableEnd, mediaEnded, seekableStart]);
1051
+ const onPointerEnter = React.useCallback(() => {
1052
+ if (seekRef.current) {
1053
+ seekRectRef.current = seekRef.current.getBoundingClientRect();
1054
+ }
1055
+ collisionDataRef.current = null;
1056
+ pointerEnterTimeRef.current = Date.now();
1057
+ horizontalMovementRef.current = 0;
1058
+ verticalMovementRef.current = 0;
1059
+ if (seekableEnd > 0) {
1060
+ if (hoverTimeoutRef.current) {
1061
+ clearTimeout(hoverTimeoutRef.current);
1062
+ }
1063
+ if (!tooltipDisabled) {
1064
+ if (lastPointerXRef.current && seekRectRef.current) {
1065
+ const clientX = Math.max(seekRectRef.current.left, Math.min(lastPointerXRef.current, seekRectRef.current.right));
1066
+ onTooltipPositionUpdate(clientX);
1067
+ }
1068
+ }
1069
+ }
1070
+ }, [seekableEnd, onTooltipPositionUpdate, tooltipDisabled]);
1071
+ const onPointerLeave = React.useCallback(() => {
1072
+ if (hoverTimeoutRef.current) {
1073
+ clearTimeout(hoverTimeoutRef.current);
1074
+ hoverTimeoutRef.current = null;
1075
+ }
1076
+ if (rafIdRef.current) {
1077
+ cancelAnimationFrame(rafIdRef.current);
1078
+ rafIdRef.current = null;
1079
+ }
1080
+ if (previewDebounceRef.current) {
1081
+ cancelAnimationFrame(previewDebounceRef.current);
1082
+ previewDebounceRef.current = null;
1083
+ }
1084
+ setSeekState((prev) => (Object.assign(Object.assign({}, prev), { isHovering: false, hasInitialPosition: false })));
1085
+ justCommittedRef.current = false;
1086
+ seekRectRef.current = null;
1087
+ collisionDataRef.current = null;
1088
+ pointerEnterTimeRef.current = 0;
1089
+ horizontalMovementRef.current = 0;
1090
+ verticalMovementRef.current = 0;
1091
+ lastPointerXRef.current = 0;
1092
+ lastPointerYRef.current = 0;
1093
+ lastSeekCommitTimeRef.current = 0;
1094
+ if (!tooltipDisabled) {
1095
+ dispatch({
1096
+ type: media_store_1.MediaActionTypes.MEDIA_PREVIEW_REQUEST,
1097
+ detail: undefined,
1098
+ });
1099
+ }
1100
+ }, [dispatch, tooltipDisabled]);
1101
+ const onPointerMove = React.useCallback((event) => {
1102
+ if (seekableEnd <= 0)
1103
+ return;
1104
+ if (!seekRectRef.current && seekRef.current) {
1105
+ seekRectRef.current = seekRef.current.getBoundingClientRect();
1106
+ }
1107
+ if (!seekRectRef.current)
1108
+ return;
1109
+ const currentX = event.clientX;
1110
+ const currentY = event.clientY;
1111
+ if (lastPointerXRef.current !== 0 && lastPointerYRef.current !== 0) {
1112
+ const deltaX = Math.abs(currentX - lastPointerXRef.current);
1113
+ const deltaY = Math.abs(currentY - lastPointerYRef.current);
1114
+ horizontalMovementRef.current += deltaX;
1115
+ verticalMovementRef.current += deltaY;
1116
+ }
1117
+ lastPointerXRef.current = currentX;
1118
+ lastPointerYRef.current = currentY;
1119
+ if (rafIdRef.current) {
1120
+ cancelAnimationFrame(rafIdRef.current);
1121
+ }
1122
+ rafIdRef.current = requestAnimationFrame(() => {
1123
+ const wasJustCommitted = justCommittedRef.current;
1124
+ if (wasJustCommitted) {
1125
+ justCommittedRef.current = false;
1126
+ }
1127
+ const seekRect = seekRectRef.current;
1128
+ if (!seekRect) {
1129
+ rafIdRef.current = null;
1130
+ return;
1131
+ }
1132
+ const clientX = lastPointerXRef.current;
1133
+ const offsetXOnSeekBar = Math.max(0, Math.min(clientX - seekRect.left, seekRect.width));
1134
+ const relativeX = offsetXOnSeekBar / seekRect.width;
1135
+ const calculatedHoverTime = relativeX * seekableEnd;
1136
+ hoverTimeRef.current = calculatedHoverTime;
1137
+ onHoverProgressUpdate();
1138
+ const wasHovering = seekState.isHovering;
1139
+ const isCurrentlyHovering = clientX >= seekRect.left && clientX <= seekRect.right;
1140
+ const timeHovering = Date.now() - pointerEnterTimeRef.current;
1141
+ const totalMovement = horizontalMovementRef.current + verticalMovementRef.current;
1142
+ const horizontalRatio = totalMovement > 0 ? horizontalMovementRef.current / totalMovement : 0;
1143
+ const timeSinceSeekCommit = Date.now() - lastSeekCommitTimeRef.current;
1144
+ const isInSeekCooldown = timeSinceSeekCommit < 300;
1145
+ const shouldShowTooltip = !wasJustCommitted &&
1146
+ !isInSeekCooldown &&
1147
+ (timeHovering > 150 ||
1148
+ horizontalRatio > 0.6 ||
1149
+ (totalMovement < 10 && timeHovering > 50));
1150
+ if (!wasHovering &&
1151
+ isCurrentlyHovering &&
1152
+ shouldShowTooltip &&
1153
+ !tooltipDisabled) {
1154
+ setSeekState((prev) => (Object.assign(Object.assign({}, prev), { isHovering: true })));
1155
+ }
1156
+ if (!tooltipDisabled) {
1157
+ onPreviewUpdate(calculatedHoverTime);
1158
+ if (isCurrentlyHovering && (wasHovering || shouldShowTooltip)) {
1159
+ onTooltipPositionUpdate(clientX);
1160
+ }
1161
+ }
1162
+ rafIdRef.current = null;
1163
+ });
1164
+ }, [
1165
+ onPreviewUpdate,
1166
+ onTooltipPositionUpdate,
1167
+ onHoverProgressUpdate,
1168
+ seekableEnd,
1169
+ seekState.isHovering,
1170
+ tooltipDisabled,
1171
+ ]);
1172
+ const onSeek = React.useCallback((value) => {
1173
+ var _a;
1174
+ const time = (_a = value[0]) !== null && _a !== void 0 ? _a : 0;
1175
+ setSeekState((prev) => (Object.assign(Object.assign({}, prev), { pendingSeekTime: time })));
1176
+ if (!store.getState().dragging) {
1177
+ store.setState("dragging", true);
1178
+ }
1179
+ if (seekThrottleRef.current) {
1180
+ cancelAnimationFrame(seekThrottleRef.current);
1181
+ }
1182
+ seekThrottleRef.current = requestAnimationFrame(() => {
1183
+ dispatch({
1184
+ type: media_store_1.MediaActionTypes.MEDIA_SEEK_REQUEST,
1185
+ detail: time,
1186
+ });
1187
+ seekThrottleRef.current = null;
1188
+ });
1189
+ }, [dispatch, store.getState, store.setState]);
1190
+ const onSeekCommit = React.useCallback((value) => {
1191
+ var _a;
1192
+ const time = (_a = value[0]) !== null && _a !== void 0 ? _a : 0;
1193
+ if (seekThrottleRef.current) {
1194
+ cancelAnimationFrame(seekThrottleRef.current);
1195
+ seekThrottleRef.current = null;
1196
+ }
1197
+ if (hoverTimeoutRef.current) {
1198
+ clearTimeout(hoverTimeoutRef.current);
1199
+ hoverTimeoutRef.current = null;
1200
+ }
1201
+ if (rafIdRef.current) {
1202
+ cancelAnimationFrame(rafIdRef.current);
1203
+ rafIdRef.current = null;
1204
+ }
1205
+ if (previewDebounceRef.current) {
1206
+ cancelAnimationFrame(previewDebounceRef.current);
1207
+ previewDebounceRef.current = null;
1208
+ }
1209
+ setSeekState((prev) => (Object.assign(Object.assign({}, prev), { pendingSeekTime: time, isHovering: false, hasInitialPosition: false })));
1210
+ justCommittedRef.current = true;
1211
+ collisionDataRef.current = null;
1212
+ lastSeekCommitTimeRef.current = Date.now();
1213
+ pointerEnterTimeRef.current = Date.now();
1214
+ horizontalMovementRef.current = 0;
1215
+ verticalMovementRef.current = 0;
1216
+ if (store.getState().dragging) {
1217
+ store.setState("dragging", false);
1218
+ }
1219
+ dispatch({
1220
+ type: media_store_1.MediaActionTypes.MEDIA_SEEK_REQUEST,
1221
+ detail: time,
1222
+ });
1223
+ dispatch({
1224
+ type: media_store_1.MediaActionTypes.MEDIA_PREVIEW_REQUEST,
1225
+ detail: undefined,
1226
+ });
1227
+ }, [dispatch, store.getState, store.setState]);
1228
+ React.useEffect(() => {
1229
+ return () => {
1230
+ if (seekThrottleRef.current) {
1231
+ cancelAnimationFrame(seekThrottleRef.current);
1232
+ }
1233
+ if (hoverTimeoutRef.current) {
1234
+ clearTimeout(hoverTimeoutRef.current);
1235
+ }
1236
+ if (rafIdRef.current) {
1237
+ cancelAnimationFrame(rafIdRef.current);
1238
+ }
1239
+ if (previewDebounceRef.current) {
1240
+ cancelAnimationFrame(previewDebounceRef.current);
1241
+ }
1242
+ };
1243
+ }, []);
1244
+ const currentChapterCue = getCurrentChapterCue(hoverTimeRef.current);
1245
+ const thumbnail = getThumbnail(hoverTimeRef.current);
1246
+ const hoverTime = getCachedTime(hoverTimeRef.current, seekableEnd);
1247
+ const chapterSeparators = React.useMemo(() => {
1248
+ if (withoutChapter || chapterCues.length <= 1 || seekableEnd <= 0) {
1249
+ return null;
1250
+ }
1251
+ return chapterCues.slice(1).map((chapterCue, index) => {
1252
+ const position = (chapterCue.startTime / seekableEnd) * 100;
1253
+ return ((0, jsx_runtime_1.jsx)("div", { role: "presentation", "aria-hidden": "true", "data-slot": "media-player-seek-chapter-separator", className: "absolute top-0 h-full bg-zinc-50 dark:bg-zinc-950", style: {
1254
+ width: ".1563rem",
1255
+ left: `${position}%`,
1256
+ transform: "translateX(-50%)",
1257
+ } }, `chapter-${index}-${chapterCue.startTime}`));
1258
+ });
1259
+ }, [chapterCues, seekableEnd, withoutChapter]);
1260
+ const spriteStyle = React.useMemo(() => {
1261
+ var _a, _b;
1262
+ if (!(thumbnail === null || thumbnail === void 0 ? void 0 : thumbnail.coords) || !(thumbnail === null || thumbnail === void 0 ? void 0 : thumbnail.src)) {
1263
+ return {};
1264
+ }
1265
+ const coordX = thumbnail.coords[0];
1266
+ const coordY = thumbnail.coords[1];
1267
+ const spriteWidth = Number.parseFloat((_a = thumbnail.coords[2]) !== null && _a !== void 0 ? _a : "0");
1268
+ const spriteHeight = Number.parseFloat((_b = thumbnail.coords[3]) !== null && _b !== void 0 ? _b : "0");
1269
+ const scaleX = spriteWidth > 0 ? SPRITE_CONTAINER_WIDTH / spriteWidth : 1;
1270
+ const scaleY = spriteHeight > 0 ? SPRITE_CONTAINER_HEIGHT / spriteHeight : 1;
1271
+ const scale = Math.min(scaleX, scaleY);
1272
+ return {
1273
+ width: `${spriteWidth}px`,
1274
+ height: `${spriteHeight}px`,
1275
+ backgroundImage: `url(${thumbnail.src})`,
1276
+ backgroundPosition: `-${coordX}px -${coordY}px`,
1277
+ backgroundRepeat: "no-repeat",
1278
+ transform: `scale(${scale})`,
1279
+ transformOrigin: "top left",
1280
+ };
1281
+ }, [thumbnail === null || thumbnail === void 0 ? void 0 : thumbnail.coords, thumbnail === null || thumbnail === void 0 ? void 0 : thumbnail.src]);
1282
+ const SeekSlider = ((0, jsx_runtime_1.jsxs)("div", { "data-slot": "media-player-seek-container", className: "relative w-full", children: [(0, jsx_runtime_1.jsxs)(SliderPrimitive.Root, Object.assign({ "aria-controls": context.mediaId, "aria-valuetext": `${currentTime} of ${duration}`, "data-hovering": seekState.isHovering ? "" : undefined, "data-slider": "", "data-slot": "media-player-seek", disabled: isDisabled }, seekProps, { ref: seekRef, min: seekableStart, max: seekableEnd, step: 0.01, className: (0, utils_1.cn)("relative flex w-full touch-none items-center select-none data-disabled:pointer-events-none data-disabled:opacity-50", className), value: [displayValue], onValueChange: onSeek, onValueCommit: onSeekCommit, onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, onPointerMove: onPointerMove, children: [(0, jsx_runtime_1.jsxs)(SliderPrimitive.Track, { className: "relative h-1 w-full grow overflow-hidden rounded-full bg-primary/40", children: [(0, jsx_runtime_1.jsx)("div", { "data-slot": "media-player-seek-buffered", className: "absolute h-full bg-primary/70 will-change-[width]", style: {
1283
+ width: `${bufferedProgress * 100}%`,
1284
+ } }), (0, jsx_runtime_1.jsx)(SliderPrimitive.Range, { className: "absolute h-full bg-primary will-change-[width]" }), seekState.isHovering && seekableEnd > 0 && ((0, jsx_runtime_1.jsx)("div", { "data-slot": "media-player-seek-hover-range", className: "absolute h-full bg-primary/70 will-change-[width,opacity]", style: {
1285
+ width: `var(${SEEK_HOVER_PERCENT}, 0%)`,
1286
+ transition: "opacity 150ms ease-out",
1287
+ } })), chapterSeparators] }), (0, jsx_runtime_1.jsx)(SliderPrimitive.Thumb, { className: "relative z-10 block size-2.5 shrink-0 rounded-full bg-primary shadow-sm ring-ring/50 transition-[color,box-shadow] will-change-transform hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50" })] })), !withoutTooltip &&
1288
+ !context.withoutTooltip &&
1289
+ seekState.isHovering &&
1290
+ seekableEnd > 0 && ((0, jsx_runtime_1.jsx)(MediaPlayerPortal, { children: (0, jsx_runtime_1.jsx)("div", { ref: tooltipRef, className: "pointer-events-none z-50 contain-[layout_style] [transition:opacity_150ms_ease-in-out] backface-hidden", style: {
1291
+ position: "fixed",
1292
+ left: `var(${SEEK_TOOLTIP_X}, 0rem)`,
1293
+ top: `var(${SEEK_TOOLTIP_Y}, 0rem)`,
1294
+ transform: `translateX(-50%) translateY(calc(-100% - ${currentTooltipSideOffset}px))`,
1295
+ visibility: seekState.hasInitialPosition ? "visible" : "hidden",
1296
+ opacity: seekState.hasInitialPosition ? 1 : 0,
1297
+ }, children: (0, jsx_runtime_1.jsxs)("div", { className: (0, utils_1.cn)("flex flex-col items-center gap-1.5 rounded-md border bg-background text-foreground shadow-sm dark:bg-zinc-900", thumbnail && "min-h-10", !thumbnail && currentChapterCue && "px-3 py-1.5"), children: [(thumbnail === null || thumbnail === void 0 ? void 0 : thumbnail.src) && ((0, jsx_runtime_1.jsx)("div", { "data-slot": "media-player-seek-thumbnail", className: "overflow-hidden rounded-md rounded-b-none", style: {
1298
+ width: `${SPRITE_CONTAINER_WIDTH}px`,
1299
+ height: `${SPRITE_CONTAINER_HEIGHT}px`,
1300
+ }, children: thumbnail.coords ? ((0, jsx_runtime_1.jsx)("div", { style: spriteStyle })) : ((0, jsx_runtime_1.jsx)("img", { src: thumbnail.src, alt: `Preview at ${hoverTime}`, className: "size-full object-cover" })) })), currentChapterCue && ((0, jsx_runtime_1.jsx)("div", { "data-slot": "media-player-seek-chapter-title", className: "line-clamp-2 max-w-48 text-center text-xs text-balance", children: currentChapterCue.text })), (0, jsx_runtime_1.jsx)("div", { "data-slot": "media-player-seek-time", className: (0, utils_1.cn)("text-center text-xs whitespace-nowrap tabular-nums", thumbnail && "pb-1.5", !(thumbnail || currentChapterCue) && "px-2.5 py-1"), children: tooltipTimeVariant === "progress"
1301
+ ? `${hoverTime} / ${duration}`
1302
+ : hoverTime })] }) }) }))] }));
1303
+ if (withTime) {
1304
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "flex w-full items-center gap-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-sm tabular-nums", children: currentTime }), SeekSlider, (0, jsx_runtime_1.jsx)("span", { className: "text-sm tabular-nums", children: remainingTime })] }));
1305
+ }
1306
+ return SeekSlider;
1307
+ }
1308
+ function MediaPlayerVolume(props) {
1309
+ const { expandable = false, className, disabled } = props, volumeProps = __rest(props, ["expandable", "className", "disabled"]);
1310
+ const context = useMediaPlayerContext(VOLUME_NAME);
1311
+ const store = useStoreContext(VOLUME_NAME);
1312
+ const dispatch = (0, media_store_1.useMediaDispatch)();
1313
+ const mediaVolume = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaVolume) !== null && _a !== void 0 ? _a : 1; });
1314
+ const mediaMuted = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaMuted) !== null && _a !== void 0 ? _a : false; });
1315
+ const mediaVolumeLevel = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaVolumeLevel) !== null && _a !== void 0 ? _a : "high"; });
1316
+ const sliderId = React.useId();
1317
+ const volumeTriggerId = React.useId();
1318
+ const isDisabled = disabled || context.disabled;
1319
+ const onMute = React.useCallback(() => {
1320
+ dispatch({
1321
+ type: mediaMuted
1322
+ ? media_store_1.MediaActionTypes.MEDIA_UNMUTE_REQUEST
1323
+ : media_store_1.MediaActionTypes.MEDIA_MUTE_REQUEST,
1324
+ });
1325
+ }, [dispatch, mediaMuted]);
1326
+ const onVolumeChange = React.useCallback((value) => {
1327
+ var _a;
1328
+ const volume = (_a = value[0]) !== null && _a !== void 0 ? _a : 0;
1329
+ if (!store.getState().dragging) {
1330
+ store.setState("dragging", true);
1331
+ }
1332
+ dispatch({
1333
+ type: media_store_1.MediaActionTypes.MEDIA_VOLUME_REQUEST,
1334
+ detail: volume,
1335
+ });
1336
+ }, [dispatch, store.getState, store.setState]);
1337
+ const onVolumeCommit = React.useCallback((value) => {
1338
+ var _a;
1339
+ const volume = (_a = value[0]) !== null && _a !== void 0 ? _a : 0;
1340
+ if (store.getState().dragging) {
1341
+ store.setState("dragging", false);
1342
+ }
1343
+ dispatch({
1344
+ type: media_store_1.MediaActionTypes.MEDIA_VOLUME_REQUEST,
1345
+ detail: volume,
1346
+ });
1347
+ }, [dispatch, store]);
1348
+ const effectiveVolume = mediaMuted ? 0 : mediaVolume;
1349
+ return ((0, jsx_runtime_1.jsxs)("div", { "data-disabled": isDisabled ? "" : undefined, "data-slot": "media-player-volume-container", className: (0, utils_1.cn)("group flex items-center", expandable
1350
+ ? "gap-0 group-focus-within:gap-2 group-hover:gap-1.5"
1351
+ : "gap-1.5", className), children: [(0, jsx_runtime_1.jsx)(MediaPlayerTooltip, { tooltip: "Volume", shortcut: "M", children: (0, jsx_runtime_1.jsx)(Button, { id: volumeTriggerId, type: "button", "aria-controls": `${context.mediaId} ${sliderId}`, "aria-label": mediaMuted ? "Unmute" : "Mute", "aria-pressed": mediaMuted, "data-slot": "media-player-volume-trigger", "data-state": mediaMuted ? "on" : "off", variant: "ghost", size: "icon", className: "size-8", disabled: isDisabled, onClick: onMute, children: mediaVolumeLevel === "off" || mediaMuted ? ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "VolumeXIcon", tabler: "IconVolumeOff", hugeicons: "VolumeOffIcon", phosphor: "SpeakerSlashIcon", remixicon: "RiVolumeMuteLine" })) : mediaVolumeLevel === "high" ? ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "Volume2Icon", tabler: "IconVolume", hugeicons: "VolumeHighIcon", phosphor: "SpeakerHighIcon", remixicon: "RiVolumeUpLine" })) : ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "Volume1Icon", tabler: "IconVolume2", hugeicons: "VolumeLowIcon", phosphor: "SpeakerLowIcon", remixicon: "RiVolumeDownLine" })) }) }), (0, jsx_runtime_1.jsxs)(SliderPrimitive.Root, Object.assign({ id: sliderId, "aria-controls": context.mediaId, "aria-valuetext": `${Math.round(effectiveVolume * 100)}% volume`, "data-slider": "", "data-slot": "media-player-volume" }, volumeProps, { min: 0, max: 1, step: 0.1, className: (0, utils_1.cn)("relative flex touch-none items-center select-none", expandable
1352
+ ? "w-0 opacity-0 transition-[width,opacity] duration-200 ease-in-out group-focus-within:w-16 group-focus-within:opacity-100 group-hover:w-16 group-hover:opacity-100"
1353
+ : "w-16", className), disabled: isDisabled, value: [effectiveVolume], onValueChange: onVolumeChange, onValueCommit: onVolumeCommit, children: [(0, jsx_runtime_1.jsx)(SliderPrimitive.Track, { className: "relative h-1 w-full grow overflow-hidden rounded-full bg-zinc-500", children: (0, jsx_runtime_1.jsx)(SliderPrimitive.Range, { className: "absolute h-full bg-primary will-change-[width]" }) }), (0, jsx_runtime_1.jsx)(SliderPrimitive.Thumb, { className: "block size-2.5 shrink-0 rounded-full bg-primary shadow-sm ring-ring/50 transition-[color,box-shadow] will-change-transform hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50" })] }))] }));
1354
+ }
1355
+ function MediaPlayerTime(props) {
1356
+ const { variant = "progress", asChild, className } = props, timeProps = __rest(props, ["variant", "asChild", "className"]);
1357
+ const context = useMediaPlayerContext("MediaPlayerTime");
1358
+ const mediaCurrentTime = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaCurrentTime) !== null && _a !== void 0 ? _a : 0; });
1359
+ const [, seekableEnd = 0] = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaSeekable) !== null && _a !== void 0 ? _a : [0, 0]; });
1360
+ const times = React.useMemo(() => {
1361
+ if (variant === "remaining") {
1362
+ return {
1363
+ remaining: media_store_1.timeUtils.formatTime(seekableEnd - mediaCurrentTime, seekableEnd),
1364
+ };
1365
+ }
1366
+ if (variant === "duration") {
1367
+ return {
1368
+ duration: media_store_1.timeUtils.formatTime(seekableEnd, seekableEnd),
1369
+ };
1370
+ }
1371
+ return {
1372
+ current: media_store_1.timeUtils.formatTime(mediaCurrentTime, seekableEnd),
1373
+ duration: media_store_1.timeUtils.formatTime(seekableEnd, seekableEnd),
1374
+ };
1375
+ }, [variant, mediaCurrentTime, seekableEnd]);
1376
+ const TimePrimitive = asChild ? SlotPrimitive.Slot : "div";
1377
+ if (variant === "remaining" || variant === "duration") {
1378
+ return ((0, jsx_runtime_1.jsx)(TimePrimitive, Object.assign({ "data-slot": "media-player-time", "data-variant": variant, dir: context.dir }, timeProps, { className: (0, utils_1.cn)("text-sm text-foreground/80 tabular-nums", className), children: times[variant] })));
1379
+ }
1380
+ return ((0, jsx_runtime_1.jsxs)(TimePrimitive, Object.assign({ "data-slot": "media-player-time", "data-variant": variant, dir: context.dir }, timeProps, { className: (0, utils_1.cn)("flex items-center gap-1 text-sm text-foreground/80", className), children: [(0, jsx_runtime_1.jsx)("span", { className: "tabular-nums", children: times.current }), (0, jsx_runtime_1.jsx)("span", { role: "separator", "aria-hidden": "true", "aria-valuenow": 0, tabIndex: -1, children: "/" }), (0, jsx_runtime_1.jsx)("span", { className: "tabular-nums", children: times.duration })] })));
1381
+ }
1382
+ function MediaPlayerPlaybackSpeed(props) {
1383
+ const { open, defaultOpen, onOpenChange: onOpenChangeProp, sideOffset = FLOATING_MENU_SIDE_OFFSET, speeds = SPEEDS, modal = false, className, disabled } = props, playbackSpeedProps = __rest(props, ["open", "defaultOpen", "onOpenChange", "sideOffset", "speeds", "modal", "className", "disabled"]);
1384
+ const context = useMediaPlayerContext(PLAYBACK_SPEED_NAME);
1385
+ const store = useStoreContext(PLAYBACK_SPEED_NAME);
1386
+ const dispatch = (0, media_store_1.useMediaDispatch)();
1387
+ const mediaPlaybackRate = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaPlaybackRate) !== null && _a !== void 0 ? _a : 1; });
1388
+ const isDisabled = disabled || context.disabled;
1389
+ const onPlaybackRateChange = React.useCallback((rate) => {
1390
+ dispatch({
1391
+ type: media_store_1.MediaActionTypes.MEDIA_PLAYBACK_RATE_REQUEST,
1392
+ detail: rate,
1393
+ });
1394
+ }, [dispatch]);
1395
+ const onOpenChange = React.useCallback((open) => {
1396
+ store.setState("menuOpen", open);
1397
+ onOpenChangeProp === null || onOpenChangeProp === void 0 ? void 0 : onOpenChangeProp(open);
1398
+ }, [store.setState, onOpenChangeProp]);
1399
+ return ((0, jsx_runtime_1.jsxs)(dropdown_menu_1.DropdownMenu, { modal: modal, open: open, defaultOpen: defaultOpen, onOpenChange: onOpenChange, children: [(0, jsx_runtime_1.jsx)(MediaPlayerTooltip, { tooltip: "Playback speed", shortcut: ["<", ">"], children: (0, jsx_runtime_1.jsx)(dropdown_menu_1.DropdownMenuTrigger, { asChild: true, children: (0, jsx_runtime_1.jsxs)(Button, Object.assign({ type: "button", "aria-controls": context.mediaId, disabled: isDisabled }, playbackSpeedProps, { variant: "ghost", size: "icon", className: (0, utils_1.cn)("h-8 w-16 aria-expanded:bg-accent/50", className), children: [mediaPlaybackRate, "x"] })) }) }), (0, jsx_runtime_1.jsx)(dropdown_menu_1.DropdownMenuContent, { container: context.portalContainer, sideOffset: sideOffset, align: "center", className: "min-w-(--radix-dropdown-menu-trigger-width) data-[side=top]:mb-3.5", children: speeds.map((speed) => ((0, jsx_runtime_1.jsxs)(dropdown_menu_1.DropdownMenuItem, { className: "justify-between", onSelect: () => onPlaybackRateChange(speed), children: [speed, "x", mediaPlaybackRate === speed && ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "CheckIcon", tabler: "IconCheck", hugeicons: "Tick02Icon", phosphor: "CheckIcon", remixicon: "RiCheckLine" }))] }, speed))) })] }));
1400
+ }
1401
+ function MediaPlayerLoop(props) {
1402
+ const { children, className, disabled } = props, loopProps = __rest(props, ["children", "className", "disabled"]);
1403
+ const context = useMediaPlayerContext("MediaPlayerLoop");
1404
+ const isDisabled = disabled || context.disabled;
1405
+ const [isLooping, setIsLooping] = React.useState(() => {
1406
+ var _a;
1407
+ const mediaElement = context.mediaRef.current;
1408
+ return (_a = mediaElement === null || mediaElement === void 0 ? void 0 : mediaElement.loop) !== null && _a !== void 0 ? _a : false;
1409
+ });
1410
+ React.useEffect(() => {
1411
+ const mediaElement = context.mediaRef.current;
1412
+ if (!mediaElement)
1413
+ return;
1414
+ setIsLooping(mediaElement.loop);
1415
+ const checkLoop = () => setIsLooping(mediaElement.loop);
1416
+ const observer = new MutationObserver(checkLoop);
1417
+ observer.observe(mediaElement, {
1418
+ attributes: true,
1419
+ attributeFilter: ["loop"],
1420
+ });
1421
+ return () => observer.disconnect();
1422
+ }, [context.mediaRef]);
1423
+ const onLoopToggle = React.useCallback((event) => {
1424
+ var _a;
1425
+ (_a = props.onClick) === null || _a === void 0 ? void 0 : _a.call(props, event);
1426
+ if (event.defaultPrevented)
1427
+ return;
1428
+ const mediaElement = context.mediaRef.current;
1429
+ if (mediaElement) {
1430
+ const newLoopState = !mediaElement.loop;
1431
+ mediaElement.loop = newLoopState;
1432
+ setIsLooping(newLoopState);
1433
+ }
1434
+ }, [context.mediaRef, props.onClick]);
1435
+ return ((0, jsx_runtime_1.jsx)(MediaPlayerTooltip, { tooltip: isLooping ? "Disable loop" : "Enable loop", shortcut: "R", children: (0, jsx_runtime_1.jsx)(Button, Object.assign({ type: "button", "aria-controls": context.mediaId, "aria-label": isLooping ? "Disable loop" : "Enable loop", "aria-pressed": isLooping, "data-disabled": isDisabled ? "" : undefined, "data-slot": "media-player-loop", "data-state": isLooping ? "on" : "off", disabled: isDisabled }, loopProps, { variant: "ghost", size: "icon", className: (0, utils_1.cn)("size-8", className), onClick: onLoopToggle, children: children !== null && children !== void 0 ? children : (isLooping ? ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "RepeatIcon", tabler: "IconRepeat", hugeicons: "RepeatIcon", phosphor: "RepeatIcon", remixicon: "RiRepeatLine", className: "text-muted-foreground" })) : ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "RepeatIcon", tabler: "IconRepeat", hugeicons: "RepeatIcon", phosphor: "RepeatIcon", remixicon: "RiRepeatLine" }))) })) }));
1436
+ }
1437
+ function MediaPlayerFullscreen(props) {
1438
+ const { children, className, disabled } = props, fullscreenProps = __rest(props, ["children", "className", "disabled"]);
1439
+ const context = useMediaPlayerContext("MediaPlayerFullscreen");
1440
+ const dispatch = (0, media_store_1.useMediaDispatch)();
1441
+ const isFullscreen = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaIsFullscreen) !== null && _a !== void 0 ? _a : false; });
1442
+ const isDisabled = disabled || context.disabled;
1443
+ const onFullscreen = React.useCallback((event) => {
1444
+ var _a;
1445
+ (_a = props.onClick) === null || _a === void 0 ? void 0 : _a.call(props, event);
1446
+ if (event.defaultPrevented)
1447
+ return;
1448
+ dispatch({
1449
+ type: isFullscreen
1450
+ ? media_store_1.MediaActionTypes.MEDIA_EXIT_FULLSCREEN_REQUEST
1451
+ : media_store_1.MediaActionTypes.MEDIA_ENTER_FULLSCREEN_REQUEST,
1452
+ });
1453
+ }, [dispatch, props.onClick, isFullscreen]);
1454
+ return ((0, jsx_runtime_1.jsx)(MediaPlayerTooltip, { tooltip: "Fullscreen", shortcut: "F", children: (0, jsx_runtime_1.jsx)(Button, Object.assign({ type: "button", "aria-label": isFullscreen ? "Exit fullscreen" : "Enter fullscreen", "data-disabled": isDisabled ? "" : undefined, "data-slot": "media-player-fullscreen", "data-state": isFullscreen ? "on" : "off", disabled: isDisabled }, fullscreenProps, { variant: "ghost", size: "icon", className: (0, utils_1.cn)("size-8", className), onClick: onFullscreen, children: children !== null && children !== void 0 ? children : (isFullscreen ? ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "Minimize2Icon", tabler: "IconMinimize", hugeicons: "MinimizeIcon", phosphor: "ArrowsInIcon", remixicon: "RiFullscreenExitLine" })) : ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "Maximize2Icon", tabler: "IconMaximize", hugeicons: "MaximizeIcon", phosphor: "ArrowsOutIcon", remixicon: "RiFullscreenLine" }))) })) }));
1455
+ }
1456
+ function MediaPlayerPiP(props) {
1457
+ const { children, className, onPipError, disabled } = props, pipButtonProps = __rest(props, ["children", "className", "onPipError", "disabled"]);
1458
+ const context = useMediaPlayerContext("MediaPlayerPiP");
1459
+ const dispatch = (0, media_store_1.useMediaDispatch)();
1460
+ const isPictureInPicture = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaIsPip) !== null && _a !== void 0 ? _a : false; });
1461
+ const isDisabled = disabled || context.disabled;
1462
+ const onPictureInPicture = React.useCallback((event) => {
1463
+ var _a;
1464
+ (_a = props.onClick) === null || _a === void 0 ? void 0 : _a.call(props, event);
1465
+ if (event.defaultPrevented)
1466
+ return;
1467
+ dispatch({
1468
+ type: isPictureInPicture
1469
+ ? media_store_1.MediaActionTypes.MEDIA_EXIT_PIP_REQUEST
1470
+ : media_store_1.MediaActionTypes.MEDIA_ENTER_PIP_REQUEST,
1471
+ });
1472
+ const mediaElement = context.mediaRef.current;
1473
+ if (mediaElement instanceof HTMLVideoElement) {
1474
+ if (isPictureInPicture) {
1475
+ document.exitPictureInPicture().catch((error) => {
1476
+ onPipError === null || onPipError === void 0 ? void 0 : onPipError(error, "exit");
1477
+ });
1478
+ }
1479
+ else {
1480
+ mediaElement.requestPictureInPicture().catch((error) => {
1481
+ onPipError === null || onPipError === void 0 ? void 0 : onPipError(error, "enter");
1482
+ });
1483
+ }
1484
+ }
1485
+ }, [dispatch, props.onClick, isPictureInPicture, onPipError, context.mediaRef]);
1486
+ return ((0, jsx_runtime_1.jsx)(MediaPlayerTooltip, { tooltip: "Picture in picture", shortcut: "P", children: (0, jsx_runtime_1.jsx)(Button, Object.assign({ type: "button", "aria-controls": context.mediaId, "aria-label": isPictureInPicture ? "Exit pip" : "Enter pip", "data-disabled": isDisabled ? "" : undefined, "data-slot": "media-player-pip", "data-state": isPictureInPicture ? "on" : "off", disabled: isDisabled }, pipButtonProps, { variant: "ghost", size: "icon", className: (0, utils_1.cn)("size-8", className), onClick: onPictureInPicture, children: typeof children === "function"
1487
+ ? children(isPictureInPicture)
1488
+ : (children !== null && children !== void 0 ? children : (isPictureInPicture ? ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "PictureInPicture2Icon", tabler: "IconPictureInPictureOn", hugeicons: "PictureInPictureOnIcon", phosphor: "PictureInPictureIcon", remixicon: "RiPictureInPicture2Line" })) : ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "PictureInPictureIcon", tabler: "IconPictureInPicture", hugeicons: "PictureInPictureOnIcon", phosphor: "PictureInPictureIcon", remixicon: "RiPictureInPictureLine" })))) })) }));
1489
+ }
1490
+ function MediaPlayerCaptions(props) {
1491
+ const { children, className, disabled } = props, captionsProps = __rest(props, ["children", "className", "disabled"]);
1492
+ const context = useMediaPlayerContext("MediaPlayerCaptions");
1493
+ const dispatch = (0, media_store_1.useMediaDispatch)();
1494
+ const isSubtitlesActive = (0, media_store_1.useMediaSelector)((state) => { var _a; return ((_a = state.mediaSubtitlesShowing) !== null && _a !== void 0 ? _a : []).length > 0; });
1495
+ const isDisabled = disabled || context.disabled;
1496
+ const onCaptionsToggle = React.useCallback((event) => {
1497
+ var _a;
1498
+ (_a = props.onClick) === null || _a === void 0 ? void 0 : _a.call(props, event);
1499
+ if (event.defaultPrevented)
1500
+ return;
1501
+ dispatch({
1502
+ type: media_store_1.MediaActionTypes.MEDIA_TOGGLE_SUBTITLES_REQUEST,
1503
+ });
1504
+ }, [dispatch, props.onClick]);
1505
+ return ((0, jsx_runtime_1.jsx)(MediaPlayerTooltip, { tooltip: "Captions", shortcut: "C", children: (0, jsx_runtime_1.jsx)(Button, Object.assign({ type: "button", "aria-controls": context.mediaId, "aria-label": isSubtitlesActive ? "Disable captions" : "Enable captions", "aria-pressed": isSubtitlesActive, "data-disabled": isDisabled ? "" : undefined, "data-slot": "media-player-captions", "data-state": isSubtitlesActive ? "on" : "off", disabled: isDisabled }, captionsProps, { variant: "ghost", size: "icon", className: (0, utils_1.cn)("size-8", className), onClick: onCaptionsToggle, children: children !== null && children !== void 0 ? children : (isSubtitlesActive ? ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "SubtitlesIcon", tabler: "IconCaptions", hugeicons: "ClosedCaptionIcon", phosphor: "ClosedCaptioningIcon", remixicon: "RiClosedCaptioningLine" })) : ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "CaptionsOffIcon", tabler: "IconCaptionsOff", hugeicons: "ClosedCaptionIcon", phosphor: "ClosedCaptioningIcon", remixicon: "RiClosedCaptioningOffLine" }))) })) }));
1506
+ }
1507
+ function MediaPlayerDownload(props) {
1508
+ const { children, className, disabled } = props, downloadProps = __rest(props, ["children", "className", "disabled"]);
1509
+ const context = useMediaPlayerContext("MediaPlayerDownload");
1510
+ const isDisabled = disabled || context.disabled;
1511
+ const onDownload = React.useCallback((event) => {
1512
+ var _a;
1513
+ (_a = props.onClick) === null || _a === void 0 ? void 0 : _a.call(props, event);
1514
+ if (event.defaultPrevented)
1515
+ return;
1516
+ const mediaElement = context.mediaRef.current;
1517
+ if (!(mediaElement === null || mediaElement === void 0 ? void 0 : mediaElement.currentSrc))
1518
+ return;
1519
+ const link = document.createElement("a");
1520
+ link.href = mediaElement.currentSrc;
1521
+ link.download = "";
1522
+ document.body.appendChild(link);
1523
+ link.click();
1524
+ document.body.removeChild(link);
1525
+ }, [context.mediaRef, props.onClick]);
1526
+ return ((0, jsx_runtime_1.jsx)(MediaPlayerTooltip, { tooltip: "Download", shortcut: "D", children: (0, jsx_runtime_1.jsx)(Button, Object.assign({ type: "button", "aria-controls": context.mediaId, "aria-label": "Download", "data-disabled": isDisabled ? "" : undefined, "data-slot": "media-player-download", disabled: isDisabled }, downloadProps, { variant: "ghost", size: "icon", className: (0, utils_1.cn)("size-8", className), onClick: onDownload, children: children !== null && children !== void 0 ? children : ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "DownloadIcon", tabler: "IconDownload", hugeicons: "Download01Icon", phosphor: "DownloadSimpleIcon", remixicon: "RiDownloadLine" })) })) }));
1527
+ }
1528
+ function MediaPlayerSettings(props) {
1529
+ const { open, defaultOpen, onOpenChange: onOpenChangeProp, sideOffset = FLOATING_MENU_SIDE_OFFSET, speeds = SPEEDS, modal = false, className, disabled } = props, settingsProps = __rest(props, ["open", "defaultOpen", "onOpenChange", "sideOffset", "speeds", "modal", "className", "disabled"]);
1530
+ const context = useMediaPlayerContext(SETTINGS_NAME);
1531
+ const store = useStoreContext(SETTINGS_NAME);
1532
+ const dispatch = (0, media_store_1.useMediaDispatch)();
1533
+ const mediaPlaybackRate = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaPlaybackRate) !== null && _a !== void 0 ? _a : 1; });
1534
+ const mediaSubtitlesList = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaSubtitlesList) !== null && _a !== void 0 ? _a : []; });
1535
+ const mediaSubtitlesShowing = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaSubtitlesShowing) !== null && _a !== void 0 ? _a : []; });
1536
+ const mediaRenditionList = (0, media_store_1.useMediaSelector)((state) => { var _a; return (_a = state.mediaRenditionList) !== null && _a !== void 0 ? _a : []; });
1537
+ const selectedRenditionId = (0, media_store_1.useMediaSelector)((state) => state.mediaRenditionSelected);
1538
+ const isDisabled = disabled || context.disabled;
1539
+ const isSubtitlesActive = mediaSubtitlesShowing.length > 0;
1540
+ const onPlaybackRateChange = React.useCallback((rate) => {
1541
+ dispatch({
1542
+ type: media_store_1.MediaActionTypes.MEDIA_PLAYBACK_RATE_REQUEST,
1543
+ detail: rate,
1544
+ });
1545
+ }, [dispatch]);
1546
+ const onRenditionChange = React.useCallback((renditionId) => {
1547
+ dispatch({
1548
+ type: media_store_1.MediaActionTypes.MEDIA_RENDITION_REQUEST,
1549
+ detail: renditionId === "auto" ? undefined : renditionId,
1550
+ });
1551
+ }, [dispatch]);
1552
+ const onSubtitlesToggle = React.useCallback(() => {
1553
+ dispatch({
1554
+ type: media_store_1.MediaActionTypes.MEDIA_TOGGLE_SUBTITLES_REQUEST,
1555
+ detail: false,
1556
+ });
1557
+ }, [dispatch]);
1558
+ const onShowSubtitleTrack = React.useCallback((subtitleTrack) => {
1559
+ dispatch({
1560
+ type: media_store_1.MediaActionTypes.MEDIA_TOGGLE_SUBTITLES_REQUEST,
1561
+ detail: false,
1562
+ });
1563
+ dispatch({
1564
+ type: media_store_1.MediaActionTypes.MEDIA_SHOW_SUBTITLES_REQUEST,
1565
+ detail: subtitleTrack,
1566
+ });
1567
+ }, [dispatch]);
1568
+ const selectedSubtitleLabel = React.useMemo(() => {
1569
+ var _a, _b;
1570
+ if (!isSubtitlesActive)
1571
+ return "Off";
1572
+ if (mediaSubtitlesShowing.length > 0) {
1573
+ return (_b = (_a = mediaSubtitlesShowing[0]) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : "On";
1574
+ }
1575
+ return "Off";
1576
+ }, [isSubtitlesActive, mediaSubtitlesShowing]);
1577
+ const selectedRenditionLabel = React.useMemo(() => {
1578
+ var _a;
1579
+ if (!selectedRenditionId)
1580
+ return "Auto";
1581
+ const currentRendition = mediaRenditionList === null || mediaRenditionList === void 0 ? void 0 : mediaRenditionList.find((rendition) => rendition.id === selectedRenditionId);
1582
+ if (!currentRendition)
1583
+ return "Auto";
1584
+ if (currentRendition.height)
1585
+ return `${currentRendition.height}p`;
1586
+ if (currentRendition.width)
1587
+ return `${currentRendition.width}p`;
1588
+ return (_a = currentRendition.id) !== null && _a !== void 0 ? _a : "Auto";
1589
+ }, [selectedRenditionId, mediaRenditionList]);
1590
+ const onOpenChange = React.useCallback((open) => {
1591
+ store.setState("menuOpen", open);
1592
+ onOpenChangeProp === null || onOpenChangeProp === void 0 ? void 0 : onOpenChangeProp(open);
1593
+ }, [store.setState, onOpenChangeProp]);
1594
+ return ((0, jsx_runtime_1.jsxs)(dropdown_menu_1.DropdownMenu, { modal: modal, open: open, defaultOpen: defaultOpen, onOpenChange: onOpenChange, children: [(0, jsx_runtime_1.jsx)(MediaPlayerTooltip, { tooltip: "Settings", children: (0, jsx_runtime_1.jsx)(dropdown_menu_1.DropdownMenuTrigger, { asChild: true, children: (0, jsx_runtime_1.jsx)(Button, Object.assign({ type: "button", "aria-controls": context.mediaId, "aria-label": "Settings", "data-disabled": isDisabled ? "" : undefined, "data-slot": "media-player-settings", disabled: isDisabled }, settingsProps, { variant: "ghost", size: "icon", className: (0, utils_1.cn)("size-8 aria-expanded:bg-accent/50", className), children: (0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "SettingsIcon", tabler: "IconSettings", hugeicons: "Settings01Icon", phosphor: "GearIcon", remixicon: "RiSettings3Line" }) })) }) }), (0, jsx_runtime_1.jsxs)(dropdown_menu_1.DropdownMenuContent, { align: "end", side: "top", sideOffset: sideOffset, container: context.portalContainer, className: "w-56 data-[side=top]:mb-3.5", children: [(0, jsx_runtime_1.jsx)(dropdown_menu_1.DropdownMenuLabel, { className: "sr-only", children: "Settings" }), (0, jsx_runtime_1.jsxs)(dropdown_menu_1.DropdownMenuSub, { children: [(0, jsx_runtime_1.jsxs)(dropdown_menu_1.DropdownMenuSubTrigger, { children: [(0, jsx_runtime_1.jsx)("span", { className: "flex-1", children: "Speed" }), (0, jsx_runtime_1.jsxs)(badge_1.Badge, { variant: "outline", className: "rounded-sm", children: [mediaPlaybackRate, "x"] })] }), (0, jsx_runtime_1.jsx)(dropdown_menu_1.DropdownMenuSubContent, { children: speeds.map((speed) => ((0, jsx_runtime_1.jsxs)(dropdown_menu_1.DropdownMenuItem, { className: "justify-between", onSelect: () => onPlaybackRateChange(speed), children: [speed, "x", mediaPlaybackRate === speed && ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "CheckIcon", tabler: "IconCheck", hugeicons: "Tick02Icon", phosphor: "CheckIcon", remixicon: "RiCheckLine" }))] }, speed))) })] }), context.isVideo && mediaRenditionList.length > 0 && ((0, jsx_runtime_1.jsxs)(dropdown_menu_1.DropdownMenuSub, { children: [(0, jsx_runtime_1.jsxs)(dropdown_menu_1.DropdownMenuSubTrigger, { children: [(0, jsx_runtime_1.jsx)("span", { className: "flex-1", children: "Quality" }), (0, jsx_runtime_1.jsx)(badge_1.Badge, { variant: "outline", className: "rounded-sm", children: selectedRenditionLabel })] }), (0, jsx_runtime_1.jsxs)(dropdown_menu_1.DropdownMenuSubContent, { children: [(0, jsx_runtime_1.jsxs)(dropdown_menu_1.DropdownMenuItem, { className: "justify-between", onSelect: () => onRenditionChange("auto"), children: ["Auto", !selectedRenditionId && ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "CheckIcon", tabler: "IconCheck", hugeicons: "Tick02Icon", phosphor: "CheckIcon", remixicon: "RiCheckLine" }))] }), mediaRenditionList
1595
+ .slice()
1596
+ .sort((a, b) => {
1597
+ var _a, _b;
1598
+ const aHeight = (_a = a.height) !== null && _a !== void 0 ? _a : 0;
1599
+ const bHeight = (_b = b.height) !== null && _b !== void 0 ? _b : 0;
1600
+ return bHeight - aHeight;
1601
+ })
1602
+ .map((rendition) => {
1603
+ var _a;
1604
+ const label = rendition.height
1605
+ ? `${rendition.height}p`
1606
+ : rendition.width
1607
+ ? `${rendition.width}p`
1608
+ : ((_a = rendition.id) !== null && _a !== void 0 ? _a : "Unknown");
1609
+ const selected = rendition.id === selectedRenditionId;
1610
+ return ((0, jsx_runtime_1.jsxs)(dropdown_menu_1.DropdownMenuItem, { className: "justify-between", onSelect: () => { var _a; return onRenditionChange((_a = rendition.id) !== null && _a !== void 0 ? _a : ""); }, children: [label, selected && ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "CheckIcon", tabler: "IconCheck", hugeicons: "Tick02Icon", phosphor: "CheckIcon", remixicon: "RiCheckLine" }))] }, rendition.id));
1611
+ })] })] })), (0, jsx_runtime_1.jsxs)(dropdown_menu_1.DropdownMenuSub, { children: [(0, jsx_runtime_1.jsxs)(dropdown_menu_1.DropdownMenuSubTrigger, { children: [(0, jsx_runtime_1.jsx)("span", { className: "flex-1", children: "Captions" }), (0, jsx_runtime_1.jsx)(badge_1.Badge, { variant: "outline", className: "rounded-sm", children: selectedSubtitleLabel })] }), (0, jsx_runtime_1.jsxs)(dropdown_menu_1.DropdownMenuSubContent, { children: [(0, jsx_runtime_1.jsxs)(dropdown_menu_1.DropdownMenuItem, { className: "justify-between", onSelect: onSubtitlesToggle, children: ["Off", !isSubtitlesActive && ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "CheckIcon", tabler: "IconCheck", hugeicons: "Tick02Icon", phosphor: "CheckIcon", remixicon: "RiCheckLine" }))] }), mediaSubtitlesList.map((subtitleTrack) => {
1612
+ const isSelected = mediaSubtitlesShowing.some((showingSubtitle) => showingSubtitle.label === subtitleTrack.label);
1613
+ return ((0, jsx_runtime_1.jsxs)(dropdown_menu_1.DropdownMenuItem, { className: "justify-between", onSelect: () => onShowSubtitleTrack(subtitleTrack), children: [subtitleTrack.label, isSelected && ((0, jsx_runtime_1.jsx)(IconPlaceholder, { lucide: "CheckIcon", tabler: "IconCheck", hugeicons: "Tick02Icon", phosphor: "CheckIcon", remixicon: "RiCheckLine" }))] }, `${subtitleTrack.kind}-${subtitleTrack.label}-${subtitleTrack.language}`));
1614
+ }), mediaSubtitlesList.length === 0 && ((0, jsx_runtime_1.jsx)(dropdown_menu_1.DropdownMenuItem, { disabled: true, children: "No captions available" }))] })] })] })] }));
1615
+ }
1616
+ function MediaPlayerPortal(props) {
1617
+ const { container: containerProp, children } = props;
1618
+ const context = useMediaPlayerContext("MediaPlayerPortal");
1619
+ const container = containerProp !== null && containerProp !== void 0 ? containerProp : context.portalContainer;
1620
+ if (!container)
1621
+ return null;
1622
+ return ReactDOM.createPortal(children, container);
1623
+ }
1624
+ function MediaPlayerTooltip(props) {
1625
+ const { tooltip, shortcut, delayDuration, sideOffset, children } = props, tooltipProps = __rest(props, ["tooltip", "shortcut", "delayDuration", "sideOffset", "children"]);
1626
+ const context = useMediaPlayerContext("MediaPlayerTooltip");
1627
+ const tooltipDelayDuration = delayDuration !== null && delayDuration !== void 0 ? delayDuration : context.tooltipDelayDuration;
1628
+ const tooltipSideOffset = sideOffset !== null && sideOffset !== void 0 ? sideOffset : context.tooltipSideOffset;
1629
+ if ((!tooltip && !shortcut) || context.withoutTooltip)
1630
+ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children });
1631
+ return ((0, jsx_runtime_1.jsxs)(tooltip_1.Tooltip, Object.assign({}, tooltipProps, { delayDuration: tooltipDelayDuration, children: [(0, jsx_runtime_1.jsx)(tooltip_1.TooltipTrigger, { className: "text-foreground focus-visible:ring-ring/50", asChild: true, children: children }), (0, jsx_runtime_1.jsxs)(tooltip_1.TooltipContent, { container: context.portalContainer, sideOffset: tooltipSideOffset, className: "flex items-center gap-2 border bg-accent px-2 py-1 font-medium text-foreground data-[side=top]:mb-3.5 dark:bg-zinc-900 [&>span]:hidden", children: [(0, jsx_runtime_1.jsx)("p", { children: tooltip }), Array.isArray(shortcut) ? ((0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-1", children: shortcut.map((shortcutKey) => ((0, jsx_runtime_1.jsx)("kbd", { className: "rounded border bg-secondary px-1.5 py-0.5 font-mono text-[11.2px] text-foreground shadow-xs select-none", children: (0, jsx_runtime_1.jsx)("abbr", { title: shortcutKey, className: "no-underline", children: shortcutKey }) }, shortcutKey))) })) : (shortcut && ((0, jsx_runtime_1.jsx)("kbd", { className: "rounded border bg-secondary px-1.5 py-px font-mono text-[11.2px] text-foreground shadow-xs select-none", children: (0, jsx_runtime_1.jsx)("abbr", { title: shortcut, className: "no-underline", children: shortcut }) }, shortcut)))] })] })));
1632
+ }
1633
+ //# sourceMappingURL=media-player.js.map