@hanifhan1f/vidstack-react 1.12.19 → 1.12.21

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 (59) hide show
  1. package/dev/chunks/vidstack-BPymmnxm.js +551 -0
  2. package/dev/chunks/vidstack-BSpAxhO6.js +643 -0
  3. package/dev/chunks/vidstack-BoLZuw80.js +34 -0
  4. package/dev/chunks/vidstack-CFOPpDTy.js +476 -0
  5. package/dev/chunks/vidstack-CXrXBlpD.js +11692 -0
  6. package/dev/chunks/vidstack-DOtIyh4c.js +288 -0
  7. package/dev/chunks/vidstack-DY2iivhG.js +84 -0
  8. package/dev/chunks/vidstack-Dov8gjdq.js +401 -0
  9. package/dev/chunks/vidstack-DwdhbP5c.js +189 -0
  10. package/dev/chunks/vidstack-DweQYzVw.js +180 -0
  11. package/dev/chunks/vidstack-H9OdEKUQ.js +375 -0
  12. package/dev/chunks/vidstack-KntYDWMe.js +668 -0
  13. package/dev/chunks/vidstack-NCBSBZE-.js +61 -0
  14. package/dev/chunks/vidstack-UWMPvwsa.js +1537 -0
  15. package/dev/player/vidstack-default-components.js +5 -5
  16. package/dev/player/vidstack-default-layout.js +5 -5
  17. package/dev/player/vidstack-plyr-layout.js +4 -4
  18. package/dev/player/vidstack-remotion.js +3 -3
  19. package/dev/vidstack.js +7 -7
  20. package/package.json +1 -1
  21. package/player/styles/default/layouts/video.css +94 -77
  22. package/prod/chunks/vidstack-B9nEslvl.js +11197 -0
  23. package/prod/chunks/vidstack-BOObgZd8.js +504 -0
  24. package/prod/chunks/vidstack-B_wD853-.js +386 -0
  25. package/prod/chunks/vidstack-C4tNkfXj.js +470 -0
  26. package/prod/chunks/vidstack-CNJwYQRW.js +84 -0
  27. package/prod/chunks/vidstack-CVzVtf1j.js +61 -0
  28. package/prod/chunks/vidstack-CiQEyk_l.js +189 -0
  29. package/prod/chunks/vidstack-CiTWSpv_.js +34 -0
  30. package/prod/chunks/vidstack-CzjHdPIT.js +375 -0
  31. package/prod/chunks/vidstack-Djmla_FM.js +545 -0
  32. package/prod/chunks/vidstack-DpQw1Y33.js +663 -0
  33. package/prod/chunks/vidstack-VTpvHAdU.js +1537 -0
  34. package/prod/chunks/vidstack-dbLRgf2L.js +159 -0
  35. package/prod/chunks/vidstack-rHvQ8f6c.js +288 -0
  36. package/prod/player/vidstack-default-components.js +5 -5
  37. package/prod/player/vidstack-default-layout.js +5 -5
  38. package/prod/player/vidstack-plyr-layout.js +4 -4
  39. package/prod/player/vidstack-remotion.js +3 -3
  40. package/prod/vidstack.js +7 -7
  41. package/server/chunks/vidstack-BV_VpWlJ.js +1537 -0
  42. package/server/chunks/vidstack-ByG5MvLs.js +545 -0
  43. package/server/chunks/vidstack-C0xOpWYR.js +470 -0
  44. package/server/chunks/vidstack-C481iXqe.js +386 -0
  45. package/server/chunks/vidstack-CbNRZgUA.js +11197 -0
  46. package/server/chunks/vidstack-D7D9kiW6.js +34 -0
  47. package/server/chunks/vidstack-DJJmNib6.js +504 -0
  48. package/server/chunks/vidstack-DWt5LAKE.js +375 -0
  49. package/server/chunks/vidstack-DsZKgA8y.js +663 -0
  50. package/server/chunks/vidstack-NXcLNXxO.js +84 -0
  51. package/server/chunks/vidstack-OKdxH1xx.js +189 -0
  52. package/server/chunks/vidstack-iVN8uBAv.js +288 -0
  53. package/server/chunks/vidstack-m8aA99tE.js +159 -0
  54. package/server/chunks/vidstack-rUHVQoo3.js +61 -0
  55. package/server/player/vidstack-default-components.js +5 -5
  56. package/server/player/vidstack-default-layout.js +5 -5
  57. package/server/player/vidstack-plyr-layout.js +4 -4
  58. package/server/player/vidstack-remotion.js +3 -3
  59. package/server/vidstack.js +7 -7
@@ -0,0 +1,1537 @@
1
+ "use client"
2
+
3
+ import * as React from 'react';
4
+ import { useSignal, composeRefs, isBoolean, uppercaseFirstChar, isUndefined, isString, signal, camelToKebabCase, onDispose, scoped, keysOf, effect, isArray, isKeyboardClick, listenEvent, toggleClass, useContext } from './vidstack-D_bWd66h.js';
5
+ import { createComputed, createSignal, MediaAnnouncer, Root, Trigger, Content, GoogleCastButton, Captions, useChapterOptions, Root$1 as Root$5, Root$2 as Root$6, Root$3 as Root$7, useScoped, Root$4 as Root$a, Group, useChapterTitle, createEffect, useActiveTextTrack, ChapterTitle as ChapterTitle$1, Title, Root$5 as Root$b, Track as Track$1, TrackFill as TrackFill$1 } from './vidstack-H9OdEKUQ.js';
6
+ import { useColorSchemePreference, useActive, useResizeObserver, useLayoutName, useTransitionActive } from './vidstack-DY2iivhG.js';
7
+ import { useMediaContext, MuteButton, SeekButton, LiveButton, PlayButton, CaptionButton, appendParamsToURL, PIPButton, FullscreenButton, AirPlayButton, Items, Root$3 as Root$1, Item, Root as Root$2, Img, Root$2 as Root$3, Button, Portal, Track, TrackFill, Thumb, Steps, useMediaPlayer, Root$5 as Root$4, useAudioOptions, useCaptionOptions, Root$4 as Root$8, Preview, Value, Root$1 as Root$9, Chapters, Progress, Thumbnail, ChapterTitle, Time, Gesture } from './vidstack-KntYDWMe.js';
8
+ import { useMediaState, isTrackCaptionKind, getDownloadFile, isRemotionSrc, IS_SERVER, useMediaContext as useMediaContext$1, sortVideoQualities, Primitive, mediaContext } from './vidstack-CXrXBlpD.js';
9
+ import { flushSync } from 'react-dom';
10
+ import { RemotionThumbnail, RemotionSliderThumbnail } from './vidstack-D-hQD1eE.js';
11
+
12
+ const DefaultLayoutContext = React.createContext({});
13
+ DefaultLayoutContext.displayName = "DefaultLayoutContext";
14
+ function useDefaultLayoutContext() {
15
+ return React.useContext(DefaultLayoutContext);
16
+ }
17
+ function useDefaultLayoutWord(word) {
18
+ const { translations } = useDefaultLayoutContext();
19
+ return i18n(translations, word);
20
+ }
21
+ function i18n(translations, word) {
22
+ return translations?.[word] ?? word;
23
+ }
24
+
25
+ function useColorSchemeClass(colorScheme) {
26
+ const systemColorPreference = useColorSchemePreference();
27
+ if (colorScheme === "default") {
28
+ return null;
29
+ } else if (colorScheme === "system") {
30
+ return systemColorPreference;
31
+ } else {
32
+ return colorScheme;
33
+ }
34
+ }
35
+
36
+ function createDefaultMediaLayout({
37
+ type,
38
+ smLayoutWhen,
39
+ renderLayout
40
+ }) {
41
+ const Layout = React.forwardRef(
42
+ ({
43
+ children,
44
+ className,
45
+ disableTimeSlider = false,
46
+ hideQualityBitrate = false,
47
+ icons,
48
+ colorScheme = "system",
49
+ download = null,
50
+ menuContainer = null,
51
+ menuGroup = "bottom",
52
+ noAudioGain = false,
53
+ audioGains = { min: 0, max: 300, step: 25 },
54
+ noGestures = false,
55
+ noKeyboardAnimations = false,
56
+ noModal = false,
57
+ noScrubGesture,
58
+ playbackRates = { min: 0, max: 2, step: 0.25 },
59
+ seekStep = 10,
60
+ episodes = null,
61
+ episodesTitle = "Episodes",
62
+ showMenuDelay,
63
+ showTooltipDelay = 700,
64
+ sliderChaptersMinWidth = 325,
65
+ slots,
66
+ smallLayoutWhen = smLayoutWhen,
67
+ thumbnails = null,
68
+ translations,
69
+ ...props
70
+ }, forwardRef) => {
71
+ const media = useMediaContext(), $load = useSignal(media.$props.load), $canLoad = useMediaState("canLoad"), $viewType = useMediaState("viewType"), $streamType = useMediaState("streamType"), $smallWhen = createComputed(() => {
72
+ return isBoolean(smallLayoutWhen) ? smallLayoutWhen : smallLayoutWhen(media.player.state);
73
+ }, [smallLayoutWhen]), userPrefersAnnouncements = createSignal(true), userPrefersKeyboardAnimations = createSignal(true), isMatch = $viewType === type, isSmallLayout = $smallWhen(), isForcedLayout = isBoolean(smallLayoutWhen), isLoadLayout = $load === "play" && !$canLoad, canRender = $canLoad || isForcedLayout || isLoadLayout, colorSchemeClass = useColorSchemeClass(colorScheme), layoutEl = createSignal(null);
74
+ useSignal($smallWhen);
75
+ return /* @__PURE__ */ React.createElement(
76
+ "div",
77
+ {
78
+ ...props,
79
+ className: `vds-${type}-layout` + (colorSchemeClass ? ` ${colorSchemeClass}` : "") + (className ? ` ${className}` : ""),
80
+ "data-match": isMatch ? "" : null,
81
+ "data-sm": isSmallLayout ? "" : null,
82
+ "data-lg": !isSmallLayout ? "" : null,
83
+ "data-size": isSmallLayout ? "sm" : "lg",
84
+ "data-no-scrub-gesture": noScrubGesture ? "" : null,
85
+ ref: composeRefs(layoutEl.set, forwardRef)
86
+ },
87
+ canRender && isMatch ? /* @__PURE__ */ React.createElement(
88
+ DefaultLayoutContext.Provider,
89
+ {
90
+ value: {
91
+ disableTimeSlider,
92
+ hideQualityBitrate,
93
+ icons,
94
+ colorScheme,
95
+ download,
96
+ isSmallLayout,
97
+ menuContainer,
98
+ menuGroup,
99
+ noAudioGain,
100
+ audioGains,
101
+ layoutEl,
102
+ noGestures,
103
+ noKeyboardAnimations,
104
+ noModal,
105
+ noScrubGesture,
106
+ showMenuDelay,
107
+ showTooltipDelay,
108
+ sliderChaptersMinWidth,
109
+ slots,
110
+ seekStep,
111
+ episodes,
112
+ episodesTitle,
113
+ playbackRates,
114
+ thumbnails,
115
+ translations,
116
+ userPrefersAnnouncements,
117
+ userPrefersKeyboardAnimations
118
+ }
119
+ },
120
+ renderLayout({ streamType: $streamType, isSmallLayout, isLoadLayout }),
121
+ children
122
+ ) : null
123
+ );
124
+ }
125
+ );
126
+ Layout.displayName = "DefaultMediaLayout";
127
+ return Layout;
128
+ }
129
+
130
+ function useDefaultAudioLayoutSlots() {
131
+ return React.useContext(DefaultLayoutContext).slots;
132
+ }
133
+ function useDefaultVideoLayoutSlots() {
134
+ return React.useContext(DefaultLayoutContext).slots;
135
+ }
136
+ function slot(slots, name, defaultValue) {
137
+ const slot2 = slots?.[name], capitalizedName = uppercaseFirstChar(name);
138
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, slots?.[`before${capitalizedName}`], isUndefined(slot2) ? defaultValue : slot2, slots?.[`after${capitalizedName}`]);
139
+ }
140
+
141
+ function DefaultAnnouncer() {
142
+ const { userPrefersAnnouncements, translations } = useDefaultLayoutContext(), $userPrefersAnnouncements = useSignal(userPrefersAnnouncements);
143
+ if (!$userPrefersAnnouncements) return null;
144
+ return /* @__PURE__ */ React.createElement(MediaAnnouncer, { translations });
145
+ }
146
+ DefaultAnnouncer.displayName = "DefaultAnnouncer";
147
+
148
+ function DefaultTooltip({ content, placement, children }) {
149
+ const { showTooltipDelay } = useDefaultLayoutContext();
150
+ return /* @__PURE__ */ React.createElement(Root, { showDelay: showTooltipDelay }, /* @__PURE__ */ React.createElement(Trigger, { asChild: true }, children), /* @__PURE__ */ React.createElement(Content, { className: "vds-tooltip-content", placement }, content));
151
+ }
152
+ DefaultTooltip.displayName = "DefaultTooltip";
153
+
154
+ function DefaultPlayButton({ tooltip }) {
155
+ const { icons: Icons } = useDefaultLayoutContext(), playText = useDefaultLayoutWord("Play"), pauseText = useDefaultLayoutWord("Pause"), $paused = useMediaState("paused"), $ended = useMediaState("ended");
156
+ return /* @__PURE__ */ React.createElement(DefaultTooltip, { content: $paused ? playText : pauseText, placement: tooltip }, /* @__PURE__ */ React.createElement(PlayButton, { className: "vds-play-button vds-button", "aria-label": playText }, $ended ? /* @__PURE__ */ React.createElement(Icons.PlayButton.Replay, { className: "vds-icon" }) : $paused ? /* @__PURE__ */ React.createElement(Icons.PlayButton.Play, { className: "vds-icon" }) : /* @__PURE__ */ React.createElement(Icons.PlayButton.Pause, { className: "vds-icon" })));
157
+ }
158
+ DefaultPlayButton.displayName = "DefaultPlayButton";
159
+ const DefaultMuteButton = React.forwardRef(
160
+ ({ tooltip }, forwardRef) => {
161
+ const { icons: Icons } = useDefaultLayoutContext(), muteText = useDefaultLayoutWord("Mute"), unmuteText = useDefaultLayoutWord("Unmute"), $muted = useMediaState("muted"), $volume = useMediaState("volume");
162
+ return /* @__PURE__ */ React.createElement(DefaultTooltip, { content: $muted ? unmuteText : muteText, placement: tooltip }, /* @__PURE__ */ React.createElement(MuteButton, { className: "vds-mute-button vds-button", "aria-label": muteText, ref: forwardRef }, $muted || $volume == 0 ? /* @__PURE__ */ React.createElement(Icons.MuteButton.Mute, { className: "vds-icon" }) : $volume < 0.5 ? /* @__PURE__ */ React.createElement(Icons.MuteButton.VolumeLow, { className: "vds-icon" }) : /* @__PURE__ */ React.createElement(Icons.MuteButton.VolumeHigh, { className: "vds-icon" })));
163
+ }
164
+ );
165
+ DefaultMuteButton.displayName = "DefaultMuteButton";
166
+ function DefaultCaptionButton({ tooltip }) {
167
+ const { icons: Icons } = useDefaultLayoutContext(), captionsText = useDefaultLayoutWord("Captions"), onText = useDefaultLayoutWord("Closed-Captions On"), offText = useDefaultLayoutWord("Closed-Captions Off"), $track = useMediaState("textTrack"), isOn = $track && isTrackCaptionKind($track);
168
+ return /* @__PURE__ */ React.createElement(DefaultTooltip, { content: isOn ? onText : offText, placement: tooltip }, /* @__PURE__ */ React.createElement(CaptionButton, { className: "vds-caption-button vds-button", "aria-label": captionsText }, isOn ? /* @__PURE__ */ React.createElement(Icons.CaptionButton.On, { className: "vds-icon" }) : /* @__PURE__ */ React.createElement(Icons.CaptionButton.Off, { className: "vds-icon" })));
169
+ }
170
+ DefaultCaptionButton.displayName = "DefaultCaptionButton";
171
+ function DefaultPIPButton({ tooltip }) {
172
+ const { icons: Icons } = useDefaultLayoutContext(), pipText = useDefaultLayoutWord("PiP"), enterText = useDefaultLayoutWord("Enter PiP"), exitText = useDefaultLayoutWord("Exit PiP"), $pip = useMediaState("pictureInPicture");
173
+ return /* @__PURE__ */ React.createElement(DefaultTooltip, { content: $pip ? exitText : enterText, placement: tooltip }, /* @__PURE__ */ React.createElement(PIPButton, { className: "vds-pip-button vds-button", "aria-label": pipText }, $pip ? /* @__PURE__ */ React.createElement(Icons.PIPButton.Exit, { className: "vds-icon" }) : /* @__PURE__ */ React.createElement(Icons.PIPButton.Enter, { className: "vds-icon" })));
174
+ }
175
+ DefaultPIPButton.displayName = "DefaultPIPButton";
176
+ function DefaultFullscreenButton({ tooltip }) {
177
+ const { icons: Icons } = useDefaultLayoutContext(), fullscreenText = useDefaultLayoutWord("Fullscreen"), enterText = useDefaultLayoutWord("Enter Fullscreen"), exitText = useDefaultLayoutWord("Exit Fullscreen"), $fullscreen = useMediaState("fullscreen");
178
+ return /* @__PURE__ */ React.createElement(DefaultTooltip, { content: $fullscreen ? exitText : enterText, placement: tooltip }, /* @__PURE__ */ React.createElement(FullscreenButton, { className: "vds-fullscreen-button vds-button", "aria-label": fullscreenText }, $fullscreen ? /* @__PURE__ */ React.createElement(Icons.FullscreenButton.Exit, { className: "vds-icon" }) : /* @__PURE__ */ React.createElement(Icons.FullscreenButton.Enter, { className: "vds-icon" })));
179
+ }
180
+ DefaultFullscreenButton.displayName = "DefaultFullscreenButton";
181
+ function DefaultSeekButton({
182
+ backward,
183
+ tooltip
184
+ }) {
185
+ const { icons: Icons, seekStep } = useDefaultLayoutContext(), seekForwardText = useDefaultLayoutWord("Seek Forward"), seekBackwardText = useDefaultLayoutWord("Seek Backward"), seconds = (backward ? -1 : 1) * seekStep, label = seconds >= 0 ? seekForwardText : seekBackwardText;
186
+ return /* @__PURE__ */ React.createElement(DefaultTooltip, { content: label, placement: tooltip }, /* @__PURE__ */ React.createElement(SeekButton, { className: "vds-seek-button vds-button", seconds, "aria-label": label }, seconds >= 0 ? /* @__PURE__ */ React.createElement(Icons.SeekButton.Forward, { className: "vds-icon" }) : /* @__PURE__ */ React.createElement(Icons.SeekButton.Backward, { className: "vds-icon" })));
187
+ }
188
+ DefaultSeekButton.displayName = "DefaultSeekButton";
189
+ function DefaultEpisodeButton({ tooltip, onPress }) {
190
+ const { icons: Icons } = useDefaultLayoutContext(), episodesText = useDefaultLayoutWord("Episodes");
191
+ return /* @__PURE__ */ React.createElement(DefaultTooltip, { content: episodesText, placement: tooltip }, /* @__PURE__ */ React.createElement(
192
+ "button",
193
+ {
194
+ type: "button",
195
+ className: "vds-episode-button vds-button",
196
+ "aria-label": episodesText,
197
+ onPointerUp: (event) => {
198
+ event.stopPropagation();
199
+ onPress?.(event);
200
+ }
201
+ },
202
+ /* @__PURE__ */ React.createElement(Icons.Menu.Chapters, { className: "vds-icon" })
203
+ ));
204
+ }
205
+ DefaultEpisodeButton.displayName = "DefaultEpisodeButton";
206
+ function DefaultAirPlayButton({ tooltip }) {
207
+ const { icons: Icons } = useDefaultLayoutContext(), airPlayText = useDefaultLayoutWord("AirPlay"), $state = useMediaState("remotePlaybackState"), stateText = useDefaultLayoutWord(uppercaseFirstChar($state)), label = `${airPlayText} ${stateText}`, Icon = ($state === "connecting" ? Icons.AirPlayButton.Connecting : $state === "connected" ? Icons.AirPlayButton.Connected : null) ?? Icons.AirPlayButton.Default;
208
+ return /* @__PURE__ */ React.createElement(DefaultTooltip, { content: airPlayText, placement: tooltip }, /* @__PURE__ */ React.createElement(AirPlayButton, { className: "vds-airplay-button vds-button", "aria-label": label }, /* @__PURE__ */ React.createElement(Icon, { className: "vds-icon" })));
209
+ }
210
+ DefaultAirPlayButton.displayName = "DefaultAirPlayButton";
211
+ function DefaultGoogleCastButton({ tooltip }) {
212
+ const { icons: Icons } = useDefaultLayoutContext(), googleCastText = useDefaultLayoutWord("Google Cast"), $state = useMediaState("remotePlaybackState"), stateText = useDefaultLayoutWord(uppercaseFirstChar($state)), label = `${googleCastText} ${stateText}`, Icon = ($state === "connecting" ? Icons.GoogleCastButton.Connecting : $state === "connected" ? Icons.GoogleCastButton.Connected : null) ?? Icons.GoogleCastButton.Default;
213
+ return /* @__PURE__ */ React.createElement(DefaultTooltip, { content: googleCastText, placement: tooltip }, /* @__PURE__ */ React.createElement(GoogleCastButton, { className: "vds-google-cast-button vds-button", "aria-label": label }, /* @__PURE__ */ React.createElement(Icon, { className: "vds-icon" })));
214
+ }
215
+ DefaultGoogleCastButton.displayName = "DefaultGoogleCastButton";
216
+ function DefaultLiveButton() {
217
+ const $live = useMediaState("live"), label = useDefaultLayoutWord("Skip To Live"), liveText = useDefaultLayoutWord("LIVE");
218
+ return $live ? /* @__PURE__ */ React.createElement(LiveButton, { className: "vds-live-button", "aria-label": label }, /* @__PURE__ */ React.createElement("span", { className: "vds-live-button-text" }, liveText)) : null;
219
+ }
220
+ DefaultLiveButton.displayName = "DefaultLiveButton";
221
+ function DefaultDownloadButton() {
222
+ const { download, icons: Icons } = useDefaultLayoutContext(), $src = useMediaState("source"), $title = useMediaState("title"), file = getDownloadFile({
223
+ title: $title,
224
+ src: $src,
225
+ download
226
+ }), downloadText = useDefaultLayoutWord("Download");
227
+ return isString(file?.url) ? /* @__PURE__ */ React.createElement(DefaultTooltip, { content: downloadText, placement: "top" }, /* @__PURE__ */ React.createElement(
228
+ "a",
229
+ {
230
+ role: "button",
231
+ className: "vds-download-button vds-button",
232
+ "aria-label": downloadText,
233
+ href: appendParamsToURL(file.url, { download: file.name }),
234
+ download: file.name,
235
+ target: "_blank"
236
+ },
237
+ Icons.DownloadButton ? /* @__PURE__ */ React.createElement(Icons.DownloadButton.Default, { className: "vds-icon" }) : null
238
+ )) : null;
239
+ }
240
+ DefaultDownloadButton.displayName = "DefaultDownloadButton";
241
+
242
+ function DefaultCaptions() {
243
+ const exampleText = useDefaultLayoutWord("Captions look like this");
244
+ return /* @__PURE__ */ React.createElement(Captions, { className: "vds-captions", exampleText });
245
+ }
246
+ DefaultCaptions.displayName = "DefaultCaptions";
247
+
248
+ function DefaultControlsSpacer() {
249
+ return /* @__PURE__ */ React.createElement("div", { className: "vds-controls-spacer" });
250
+ }
251
+ DefaultControlsSpacer.displayName = "DefaultControlsSpacer";
252
+
253
+ function useParentDialogEl() {
254
+ const { layoutEl } = useDefaultLayoutContext(), $layoutEl = useSignal(layoutEl);
255
+ return React.useMemo(() => $layoutEl?.closest("dialog"), [$layoutEl]);
256
+ }
257
+
258
+ function DefaultChaptersMenu({ tooltip, placement, portalClass = "" }) {
259
+ const {
260
+ showMenuDelay,
261
+ noModal,
262
+ isSmallLayout,
263
+ icons: Icons,
264
+ menuGroup,
265
+ menuContainer,
266
+ colorScheme
267
+ } = useDefaultLayoutContext(), chaptersText = useDefaultLayoutWord("Chapters"), options = useChapterOptions(), disabled = !options.length, { thumbnails } = useDefaultLayoutContext(), $src = useMediaState("currentSrc"), $viewType = useMediaState("viewType"), $offset = !isSmallLayout && menuGroup === "bottom" && $viewType === "video" ? 26 : 0, $RemotionThumbnail = useSignal(RemotionThumbnail), colorSchemeClass = useColorSchemeClass(colorScheme), [isOpen, setIsOpen] = React.useState(false), dialogEl = useParentDialogEl();
268
+ if (disabled) return null;
269
+ function onOpen() {
270
+ flushSync(() => {
271
+ setIsOpen(true);
272
+ });
273
+ }
274
+ function onClose() {
275
+ setIsOpen(false);
276
+ }
277
+ const Content = /* @__PURE__ */ React.createElement(
278
+ Items,
279
+ {
280
+ className: "vds-chapters-menu-items vds-menu-items",
281
+ placement,
282
+ offset: $offset
283
+ },
284
+ isOpen ? /* @__PURE__ */ React.createElement(
285
+ Root$1,
286
+ {
287
+ className: "vds-chapters-radio-group vds-radio-group",
288
+ value: options.selectedValue,
289
+ "data-thumbnails": thumbnails ? "" : null
290
+ },
291
+ options.map(
292
+ ({ cue, label, value, startTimeText, durationText, select, setProgressVar }) => /* @__PURE__ */ React.createElement(
293
+ Item,
294
+ {
295
+ className: "vds-chapter-radio vds-radio",
296
+ value,
297
+ key: value,
298
+ onSelect: select,
299
+ ref: setProgressVar
300
+ },
301
+ thumbnails ? /* @__PURE__ */ React.createElement(Root$2, { src: thumbnails, className: "vds-thumbnail", time: cue.startTime }, /* @__PURE__ */ React.createElement(Img, null)) : $RemotionThumbnail && isRemotionSrc($src) ? /* @__PURE__ */ React.createElement($RemotionThumbnail, { className: "vds-thumbnail", frame: cue.startTime * $src.fps }) : null,
302
+ /* @__PURE__ */ React.createElement("div", { className: "vds-chapter-radio-content" }, /* @__PURE__ */ React.createElement("span", { className: "vds-chapter-radio-label" }, label), /* @__PURE__ */ React.createElement("span", { className: "vds-chapter-radio-start-time" }, startTimeText), /* @__PURE__ */ React.createElement("span", { className: "vds-chapter-radio-duration" }, durationText))
303
+ )
304
+ )
305
+ ) : null
306
+ );
307
+ return /* @__PURE__ */ React.createElement(
308
+ Root$3,
309
+ {
310
+ className: "vds-chapters-menu vds-menu",
311
+ showDelay: showMenuDelay,
312
+ onOpen,
313
+ onClose
314
+ },
315
+ /* @__PURE__ */ React.createElement(DefaultTooltip, { content: chaptersText, placement: tooltip }, /* @__PURE__ */ React.createElement(
316
+ Button,
317
+ {
318
+ className: "vds-menu-button vds-button",
319
+ disabled,
320
+ "aria-label": chaptersText
321
+ },
322
+ /* @__PURE__ */ React.createElement(Icons.Menu.Chapters, { className: "vds-icon" })
323
+ )),
324
+ noModal || !isSmallLayout ? Content : /* @__PURE__ */ React.createElement(
325
+ Portal,
326
+ {
327
+ container: menuContainer ?? dialogEl,
328
+ className: portalClass + (colorSchemeClass ? ` ${colorSchemeClass}` : ""),
329
+ disabled: "fullscreen",
330
+ "data-sm": isSmallLayout ? "" : null,
331
+ "data-lg": !isSmallLayout ? "" : null,
332
+ "data-size": isSmallLayout ? "sm" : "lg"
333
+ },
334
+ Content
335
+ )
336
+ );
337
+ }
338
+ DefaultChaptersMenu.displayName = "DefaultChaptersMenu";
339
+
340
+ const FONT_COLOR_OPTION = {
341
+ type: "color"
342
+ };
343
+ const FONT_FAMILY_OPTION = {
344
+ type: "radio",
345
+ values: {
346
+ "Monospaced Serif": "mono-serif",
347
+ "Proportional Serif": "pro-serif",
348
+ "Monospaced Sans-Serif": "mono-sans",
349
+ "Proportional Sans-Serif": "pro-sans",
350
+ Casual: "casual",
351
+ Cursive: "cursive",
352
+ "Small Capitals": "capitals"
353
+ }
354
+ };
355
+ const FONT_SIZE_OPTION = {
356
+ type: "slider",
357
+ min: 0,
358
+ max: 400,
359
+ step: 25,
360
+ upIcon: null,
361
+ downIcon: null
362
+ };
363
+ const FONT_OPACITY_OPTION = {
364
+ type: "slider",
365
+ min: 0,
366
+ max: 100,
367
+ step: 5,
368
+ upIcon: null,
369
+ downIcon: null
370
+ };
371
+ const FONT_TEXT_SHADOW_OPTION = {
372
+ type: "radio",
373
+ values: ["None", "Drop Shadow", "Raised", "Depressed", "Outline"]
374
+ };
375
+ const FONT_DEFAULTS = {
376
+ fontFamily: "pro-sans",
377
+ fontSize: "100%",
378
+ textColor: "#ffffff",
379
+ textOpacity: "100%",
380
+ textShadow: "none",
381
+ textBg: "#000000",
382
+ textBgOpacity: "100%",
383
+ displayBg: "#000000",
384
+ displayBgOpacity: "0%"
385
+ };
386
+ const FONT_SIGNALS = Object.keys(FONT_DEFAULTS).reduce(
387
+ (prev, type) => ({
388
+ ...prev,
389
+ [type]: signal(FONT_DEFAULTS[type])
390
+ }),
391
+ {}
392
+ );
393
+ if (!IS_SERVER) {
394
+ for (const type of Object.keys(FONT_SIGNALS)) {
395
+ const value = localStorage.getItem(`vds-player:${camelToKebabCase(type)}`);
396
+ if (isString(value)) FONT_SIGNALS[type].set(value);
397
+ }
398
+ }
399
+ function onFontReset() {
400
+ for (const type of Object.keys(FONT_SIGNALS)) {
401
+ const defaultValue = FONT_DEFAULTS[type];
402
+ FONT_SIGNALS[type].set(defaultValue);
403
+ }
404
+ }
405
+
406
+ function hexToRgb(hex) {
407
+ const { style } = new Option();
408
+ style.color = hex;
409
+ return style.color.match(/\((.*?)\)/)[1].replace(/,/g, " ");
410
+ }
411
+
412
+ let isWatchingVars = false, players = /* @__PURE__ */ new Set();
413
+ function updateFontCssVars() {
414
+ if (IS_SERVER) return;
415
+ const { player } = useMediaContext$1();
416
+ players.add(player);
417
+ onDispose(() => players.delete(player));
418
+ if (!isWatchingVars) {
419
+ scoped(() => {
420
+ for (const type of keysOf(FONT_SIGNALS)) {
421
+ const $value = FONT_SIGNALS[type], defaultValue = FONT_DEFAULTS[type], varName = `--media-user-${camelToKebabCase(type)}`, storageKey = `vds-player:${camelToKebabCase(type)}`;
422
+ effect(() => {
423
+ const value = $value(), isDefaultVarValue = value === defaultValue, varValue = !isDefaultVarValue ? getCssVarValue(player, type, value) : null;
424
+ for (const player2 of players) {
425
+ player2.el?.style.setProperty(varName, varValue);
426
+ }
427
+ if (isDefaultVarValue) {
428
+ localStorage.removeItem(storageKey);
429
+ } else {
430
+ localStorage.setItem(storageKey, value);
431
+ }
432
+ });
433
+ }
434
+ }, null);
435
+ isWatchingVars = true;
436
+ }
437
+ }
438
+ function getCssVarValue(player, type, value) {
439
+ switch (type) {
440
+ case "fontFamily":
441
+ const fontVariant = value === "capitals" ? "small-caps" : "";
442
+ player.el?.style.setProperty("--media-user-font-variant", fontVariant);
443
+ return getFontFamilyCSSVarValue(value);
444
+ case "fontSize":
445
+ case "textOpacity":
446
+ case "textBgOpacity":
447
+ case "displayBgOpacity":
448
+ return percentToRatio(value);
449
+ case "textColor":
450
+ return `rgb(${hexToRgb(value)} / var(--media-user-text-opacity, 1))`;
451
+ case "textShadow":
452
+ return getTextShadowCssVarValue(value);
453
+ case "textBg":
454
+ return `rgb(${hexToRgb(value)} / var(--media-user-text-bg-opacity, 1))`;
455
+ case "displayBg":
456
+ return `rgb(${hexToRgb(value)} / var(--media-user-display-bg-opacity, 1))`;
457
+ }
458
+ }
459
+ function percentToRatio(value) {
460
+ return (parseInt(value) / 100).toString();
461
+ }
462
+ function getFontFamilyCSSVarValue(value) {
463
+ switch (value) {
464
+ case "mono-serif":
465
+ return '"Courier New", Courier, "Nimbus Mono L", "Cutive Mono", monospace';
466
+ case "mono-sans":
467
+ return '"Deja Vu Sans Mono", "Lucida Console", Monaco, Consolas, "PT Mono", monospace';
468
+ case "pro-sans":
469
+ return 'Roboto, "Arial Unicode Ms", Arial, Helvetica, Verdana, "PT Sans Caption", sans-serif';
470
+ case "casual":
471
+ return '"Comic Sans MS", Impact, Handlee, fantasy';
472
+ case "cursive":
473
+ return '"Monotype Corsiva", "URW Chancery L", "Apple Chancery", "Dancing Script", cursive';
474
+ case "capitals":
475
+ return '"Arial Unicode Ms", Arial, Helvetica, Verdana, "Marcellus SC", sans-serif + font-variant=small-caps';
476
+ default:
477
+ return '"Times New Roman", Times, Georgia, Cambria, "PT Serif Caption", serif';
478
+ }
479
+ }
480
+ function getTextShadowCssVarValue(value) {
481
+ switch (value) {
482
+ case "drop shadow":
483
+ return "rgb(34, 34, 34) 1.86389px 1.86389px 2.79583px, rgb(34, 34, 34) 1.86389px 1.86389px 3.72778px, rgb(34, 34, 34) 1.86389px 1.86389px 4.65972px";
484
+ case "raised":
485
+ return "rgb(34, 34, 34) 1px 1px, rgb(34, 34, 34) 2px 2px";
486
+ case "depressed":
487
+ return "rgb(204, 204, 204) 1px 1px, rgb(34, 34, 34) -1px -1px";
488
+ case "outline":
489
+ return "rgb(34, 34, 34) 0px 0px 1.86389px, rgb(34, 34, 34) 0px 0px 1.86389px, rgb(34, 34, 34) 0px 0px 1.86389px, rgb(34, 34, 34) 0px 0px 1.86389px, rgb(34, 34, 34) 0px 0px 1.86389px";
490
+ default:
491
+ return "";
492
+ }
493
+ }
494
+
495
+ function DefaultMenuSection({ label, value, children }) {
496
+ const id = React.useId();
497
+ if (!label) {
498
+ return /* @__PURE__ */ React.createElement("div", { className: "vds-menu-section" }, /* @__PURE__ */ React.createElement("div", { className: "vds-menu-section-body" }, children));
499
+ }
500
+ return /* @__PURE__ */ React.createElement("section", { className: "vds-menu-section", role: "group", "aria-labelledby": id }, /* @__PURE__ */ React.createElement("div", { className: "vds-menu-section-title" }, /* @__PURE__ */ React.createElement("header", { id }, label), value ? /* @__PURE__ */ React.createElement("div", { className: "vds-menu-section-value" }, value) : null), /* @__PURE__ */ React.createElement("div", { className: "vds-menu-section-body" }, children));
501
+ }
502
+ DefaultMenuSection.displayName = "DefaultMenuSection";
503
+ function DefaultMenuButton({ label, hint = "", Icon, disabled = false }) {
504
+ const { icons: Icons } = React.useContext(DefaultLayoutContext);
505
+ return /* @__PURE__ */ React.createElement(Button, { className: "vds-menu-item", disabled }, /* @__PURE__ */ React.createElement(Icons.Menu.ArrowLeft, { className: "vds-menu-close-icon vds-icon" }), Icon ? /* @__PURE__ */ React.createElement(Icon, { className: "vds-menu-item-icon vds-icon" }) : null, /* @__PURE__ */ React.createElement("span", { className: "vds-menu-item-label" }, label), /* @__PURE__ */ React.createElement("span", { className: "vds-menu-item-hint" }, hint), /* @__PURE__ */ React.createElement(Icons.Menu.ArrowRight, { className: "vds-menu-open-icon vds-icon" }));
506
+ }
507
+ DefaultMenuButton.displayName = "DefaultMenuButton";
508
+ function DefaultMenuItem({ label, children }) {
509
+ return /* @__PURE__ */ React.createElement("div", { className: "vds-menu-item" }, /* @__PURE__ */ React.createElement("div", { className: "vds-menu-item-label" }, label), children);
510
+ }
511
+ DefaultMenuItem.displayName = "DefaultMenuItem";
512
+ function DefaultMenuRadioGroup({ value, options, onChange }) {
513
+ const { icons: Icons } = useDefaultLayoutContext();
514
+ return /* @__PURE__ */ React.createElement(Root$1, { className: "vds-radio-group", value, onChange }, options.map((option) => /* @__PURE__ */ React.createElement(Item, { className: "vds-radio", value: option.value, key: option.value }, /* @__PURE__ */ React.createElement(Icons.Menu.RadioCheck, { className: "vds-icon" }), /* @__PURE__ */ React.createElement("span", { className: "vds-radio-label", "data-part": "label" }, option.label))));
515
+ }
516
+ DefaultMenuRadioGroup.displayName = "DefaultMenuRadioGroup";
517
+ function createRadioOptions(entries) {
518
+ return React.useMemo(
519
+ () => isArray(entries) ? entries.map((entry) => ({ label: entry, value: entry.toLowerCase() })) : Object.keys(entries).map((label) => ({ label, value: entries[label] })),
520
+ [entries]
521
+ );
522
+ }
523
+
524
+ function DefaultMenuSliderItem({
525
+ label,
526
+ value,
527
+ UpIcon,
528
+ DownIcon,
529
+ children,
530
+ isMin,
531
+ isMax
532
+ }) {
533
+ const hasTitle = label || value, Content = /* @__PURE__ */ React.createElement(React.Fragment, null, DownIcon ? /* @__PURE__ */ React.createElement(DownIcon, { className: "vds-icon down" }) : null, children, UpIcon ? /* @__PURE__ */ React.createElement(UpIcon, { className: "vds-icon up" }) : null);
534
+ return /* @__PURE__ */ React.createElement(
535
+ "div",
536
+ {
537
+ className: `vds-menu-item vds-menu-slider-item${hasTitle ? " group" : ""}`,
538
+ "data-min": isMin ? "" : null,
539
+ "data-max": isMax ? "" : null
540
+ },
541
+ hasTitle ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "vds-menu-slider-title" }, label ? /* @__PURE__ */ React.createElement("div", null, label) : null, value ? /* @__PURE__ */ React.createElement("div", null, value) : null), /* @__PURE__ */ React.createElement("div", { className: "vds-menu-slider-body" }, Content)) : Content
542
+ );
543
+ }
544
+ DefaultMenuSliderItem.displayName = "DefaultMenuSliderItem";
545
+ function DefaultSliderParts() {
546
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Track, { className: "vds-slider-track" }), /* @__PURE__ */ React.createElement(TrackFill, { className: "vds-slider-track-fill vds-slider-track" }), /* @__PURE__ */ React.createElement(Thumb, { className: "vds-slider-thumb" }));
547
+ }
548
+ DefaultSliderParts.displayName = "DefaultSliderParts";
549
+ function DefaultSliderSteps() {
550
+ return /* @__PURE__ */ React.createElement(Steps, { className: "vds-slider-steps" }, (step) => /* @__PURE__ */ React.createElement("div", { className: "vds-slider-step", key: String(step) }));
551
+ }
552
+ DefaultSliderSteps.displayName = "DefaultSliderSteps";
553
+
554
+ function DefaultFontMenu() {
555
+ const label = useDefaultLayoutWord("Caption Styles"), $hasCaptions = useMediaState("hasCaptions"), fontSectionLabel = useDefaultLayoutWord("Font"), textSectionLabel = useDefaultLayoutWord("Text"), textBgSectionLabel = useDefaultLayoutWord("Text Background"), displayBgSectionLabel = useDefaultLayoutWord("Display Background");
556
+ if (!$hasCaptions) return null;
557
+ return /* @__PURE__ */ React.createElement(Root$3, { className: "vds-font-menu vds-menu" }, /* @__PURE__ */ React.createElement(DefaultMenuButton, { label }), /* @__PURE__ */ React.createElement(Items, { className: "vds-font-style-items vds-menu-items" }, /* @__PURE__ */ React.createElement(DefaultMenuSection, { label: fontSectionLabel }, /* @__PURE__ */ React.createElement(DefaultFontFamilyMenu, null), /* @__PURE__ */ React.createElement(DefaultFontSizeSlider, null)), /* @__PURE__ */ React.createElement(DefaultMenuSection, { label: textSectionLabel }, /* @__PURE__ */ React.createElement(DefaultTextColorInput, null), /* @__PURE__ */ React.createElement(DefaultTextShadowMenu, null), /* @__PURE__ */ React.createElement(DefaultTextOpacitySlider, null)), /* @__PURE__ */ React.createElement(DefaultMenuSection, { label: textBgSectionLabel }, /* @__PURE__ */ React.createElement(DefaultTextBgInput, null), /* @__PURE__ */ React.createElement(DefaultTextBgOpacitySlider, null)), /* @__PURE__ */ React.createElement(DefaultMenuSection, { label: displayBgSectionLabel }, /* @__PURE__ */ React.createElement(DefaultDisplayBgInput, null), /* @__PURE__ */ React.createElement(DefaultDisplayBgOpacitySlider, null)), /* @__PURE__ */ React.createElement(DefaultMenuSection, null, /* @__PURE__ */ React.createElement(DefaultResetMenuItem, null))));
558
+ }
559
+ DefaultFontMenu.displayName = "DefaultFontMenu";
560
+ function DefaultFontFamilyMenu() {
561
+ return /* @__PURE__ */ React.createElement(DefaultFontSetting, { label: "Family", type: "fontFamily", option: FONT_FAMILY_OPTION });
562
+ }
563
+ DefaultFontFamilyMenu.displayName = "DefaultFontFamilyMenu";
564
+ function DefaultFontSizeSlider() {
565
+ const { icons: Icons } = useDefaultLayoutContext(), option = {
566
+ ...FONT_SIZE_OPTION,
567
+ upIcon: Icons.Menu.FontSizeUp,
568
+ downIcon: Icons.Menu.FontSizeDown
569
+ };
570
+ return /* @__PURE__ */ React.createElement(DefaultFontSetting, { label: "Size", type: "fontSize", option });
571
+ }
572
+ DefaultFontSizeSlider.displayName = "DefaultFontSizeSlider";
573
+ function DefaultTextColorInput() {
574
+ return /* @__PURE__ */ React.createElement(DefaultFontSetting, { label: "Color", type: "textColor", option: FONT_COLOR_OPTION });
575
+ }
576
+ DefaultTextColorInput.displayName = "DefaultTextColorInput";
577
+ function DefaultTextOpacitySlider() {
578
+ const { icons: Icons } = useDefaultLayoutContext(), option = {
579
+ ...FONT_OPACITY_OPTION,
580
+ upIcon: Icons.Menu.OpacityUp,
581
+ downIcon: Icons.Menu.OpacityDown
582
+ };
583
+ return /* @__PURE__ */ React.createElement(DefaultFontSetting, { label: "Opacity", type: "textOpacity", option });
584
+ }
585
+ DefaultTextOpacitySlider.displayName = "DefaultTextOpacitySlider";
586
+ function DefaultTextShadowMenu() {
587
+ return /* @__PURE__ */ React.createElement(DefaultFontSetting, { label: "Shadow", type: "textShadow", option: FONT_TEXT_SHADOW_OPTION });
588
+ }
589
+ DefaultTextShadowMenu.displayName = "DefaultTextShadowMenu";
590
+ function DefaultTextBgInput() {
591
+ return /* @__PURE__ */ React.createElement(DefaultFontSetting, { label: "Color", type: "textBg", option: FONT_COLOR_OPTION });
592
+ }
593
+ DefaultTextBgInput.displayName = "DefaultTextBgInput";
594
+ function DefaultTextBgOpacitySlider() {
595
+ const { icons: Icons } = useDefaultLayoutContext(), option = {
596
+ ...FONT_OPACITY_OPTION,
597
+ upIcon: Icons.Menu.OpacityUp,
598
+ downIcon: Icons.Menu.OpacityDown
599
+ };
600
+ return /* @__PURE__ */ React.createElement(DefaultFontSetting, { label: "Opacity", type: "textBgOpacity", option });
601
+ }
602
+ DefaultTextBgOpacitySlider.displayName = "DefaultTextBgOpacitySlider";
603
+ function DefaultDisplayBgInput() {
604
+ return /* @__PURE__ */ React.createElement(DefaultFontSetting, { label: "Color", type: "displayBg", option: FONT_COLOR_OPTION });
605
+ }
606
+ DefaultDisplayBgInput.displayName = "DefaultDisplayBgInput";
607
+ function DefaultDisplayBgOpacitySlider() {
608
+ const { icons: Icons } = useDefaultLayoutContext(), option = {
609
+ ...FONT_OPACITY_OPTION,
610
+ upIcon: Icons.Menu.OpacityUp,
611
+ downIcon: Icons.Menu.OpacityDown
612
+ };
613
+ return /* @__PURE__ */ React.createElement(DefaultFontSetting, { label: "Opacity", type: "displayBgOpacity", option });
614
+ }
615
+ DefaultDisplayBgOpacitySlider.displayName = "DefaultDisplayBgOpacitySlider";
616
+ function DefaultFontSetting({ label, option, type }) {
617
+ const player = useMediaPlayer(), $currentValue = FONT_SIGNALS[type], $value = useSignal($currentValue), translatedLabel = useDefaultLayoutWord(label);
618
+ const notify = React.useCallback(() => {
619
+ player?.dispatchEvent(new Event("vds-font-change"));
620
+ }, [player]);
621
+ const onChange = React.useCallback(
622
+ (newValue) => {
623
+ $currentValue.set(newValue);
624
+ notify();
625
+ },
626
+ [$currentValue, notify]
627
+ );
628
+ if (option.type === "color") {
629
+ let onColorChange2 = function(event) {
630
+ onChange(event.target.value);
631
+ };
632
+ return /* @__PURE__ */ React.createElement(DefaultMenuItem, { label: translatedLabel }, /* @__PURE__ */ React.createElement("input", { className: "vds-color-picker", type: "color", value: $value, onChange: onColorChange2 }));
633
+ }
634
+ if (option.type === "slider") {
635
+ let onSliderValueChange2 = function(value) {
636
+ onChange(value + "%");
637
+ };
638
+ const { min, max, step, upIcon, downIcon } = option;
639
+ return /* @__PURE__ */ React.createElement(
640
+ DefaultMenuSliderItem,
641
+ {
642
+ label: translatedLabel,
643
+ value: $value,
644
+ UpIcon: upIcon,
645
+ DownIcon: downIcon,
646
+ isMin: $value === min + "%",
647
+ isMax: $value === max + "%"
648
+ },
649
+ /* @__PURE__ */ React.createElement(
650
+ Root$4,
651
+ {
652
+ className: "vds-slider",
653
+ min,
654
+ max,
655
+ step,
656
+ keyStep: step,
657
+ value: parseInt($value),
658
+ "aria-label": translatedLabel,
659
+ onValueChange: onSliderValueChange2,
660
+ onDragValueChange: onSliderValueChange2
661
+ },
662
+ /* @__PURE__ */ React.createElement(DefaultSliderParts, null),
663
+ /* @__PURE__ */ React.createElement(DefaultSliderSteps, null)
664
+ )
665
+ );
666
+ }
667
+ if (option.type === "radio") {
668
+ return /* @__PURE__ */ React.createElement(
669
+ DefaultFontRadioGroup,
670
+ {
671
+ id: camelToKebabCase(type),
672
+ label: translatedLabel,
673
+ value: $value,
674
+ values: option.values,
675
+ onChange
676
+ }
677
+ );
678
+ }
679
+ return null;
680
+ }
681
+ DefaultFontSetting.displayName = "DefaultFontSetting";
682
+ function DefaultFontRadioGroup({ id, label, value, values, onChange }) {
683
+ const radioOptions = createRadioOptions(values), { translations } = useDefaultLayoutContext(), hint = React.useMemo(() => {
684
+ const label2 = radioOptions.find((radio) => radio.value === value)?.label || "";
685
+ return i18n(translations, label2);
686
+ }, [value, radioOptions]);
687
+ return /* @__PURE__ */ React.createElement(Root$3, { className: `vds-${id}-menu vds-menu` }, /* @__PURE__ */ React.createElement(DefaultMenuButton, { label, hint }), /* @__PURE__ */ React.createElement(Items, { className: "vds-menu-items" }, /* @__PURE__ */ React.createElement(DefaultMenuRadioGroup, { value, options: radioOptions, onChange })));
688
+ }
689
+ DefaultFontRadioGroup.displayName = "DefaultFontRadioGroup";
690
+ function DefaultResetMenuItem() {
691
+ const resetText = useDefaultLayoutWord("Reset");
692
+ return /* @__PURE__ */ React.createElement("button", { className: "vds-menu-item", role: "menuitem", onClick: onFontReset }, /* @__PURE__ */ React.createElement("span", { className: "vds-menu-item-label" }, resetText));
693
+ }
694
+ DefaultResetMenuItem.displayName = "DefaultResetMenuItem";
695
+
696
+ function DefaultMenuCheckbox({
697
+ label,
698
+ checked,
699
+ storageKey,
700
+ defaultChecked = false,
701
+ onChange
702
+ }) {
703
+ const [isChecked, setIsChecked] = React.useState(defaultChecked), [isActive, setIsActive] = React.useState(false);
704
+ React.useEffect(() => {
705
+ const savedValue = storageKey ? localStorage.getItem(storageKey) : null, checked2 = !!(savedValue ?? defaultChecked);
706
+ setIsChecked(checked2);
707
+ onChange?.(checked2);
708
+ }, []);
709
+ React.useEffect(() => {
710
+ if (isBoolean(checked)) setIsChecked(checked);
711
+ }, [checked]);
712
+ function onPress(event) {
713
+ if (event && "button" in event && event?.button === 1) return;
714
+ const toggledCheck = !isChecked;
715
+ setIsChecked(toggledCheck);
716
+ if (storageKey) localStorage.setItem(storageKey, toggledCheck ? "1" : "");
717
+ onChange?.(toggledCheck, event?.nativeEvent);
718
+ setIsActive(false);
719
+ }
720
+ function onActive(event) {
721
+ if (event.button !== 0) return;
722
+ setIsActive(true);
723
+ }
724
+ function onKeyDown(event) {
725
+ if (isKeyboardClick(event.nativeEvent)) onPress();
726
+ }
727
+ return /* @__PURE__ */ React.createElement(
728
+ "div",
729
+ {
730
+ className: "vds-menu-checkbox",
731
+ role: "menuitemcheckbox",
732
+ tabIndex: 0,
733
+ "aria-label": label,
734
+ "aria-checked": isChecked ? "true" : "false",
735
+ "data-active": isActive ? "" : null,
736
+ onPointerUp: onPress,
737
+ onPointerDown: onActive,
738
+ onKeyDown
739
+ }
740
+ );
741
+ }
742
+ DefaultMenuCheckbox.displayName = "DefaultMenuCheckbox";
743
+
744
+ function DefaultAccessibilityMenu({ slots }) {
745
+ const label = useDefaultLayoutWord("Accessibility"), { icons: Icons } = useDefaultLayoutContext();
746
+ return /* @__PURE__ */ React.createElement(Root$3, { className: "vds-accessibility-menu vds-menu" }, /* @__PURE__ */ React.createElement(DefaultMenuButton, { label, Icon: Icons.Menu.Accessibility }), /* @__PURE__ */ React.createElement(Items, { className: "vds-menu-items" }, slot(slots, "accessibilityMenuItemsStart", null), /* @__PURE__ */ React.createElement(DefaultMenuSection, null, /* @__PURE__ */ React.createElement(DefaultAnnouncementsMenuCheckbox, null), /* @__PURE__ */ React.createElement(DefaultKeyboardAnimationsMenuCheckbox, null)), /* @__PURE__ */ React.createElement(DefaultMenuSection, null, /* @__PURE__ */ React.createElement(DefaultFontMenu, null)), slot(slots, "accessibilityMenuItemsEnd", null)));
747
+ }
748
+ DefaultAccessibilityMenu.displayName = "DefaultAccessibilityMenu";
749
+ function DefaultAnnouncementsMenuCheckbox() {
750
+ const { userPrefersAnnouncements } = useDefaultLayoutContext(), label = useDefaultLayoutWord("Announcements");
751
+ function onChange(checked) {
752
+ userPrefersAnnouncements.set(checked);
753
+ }
754
+ return /* @__PURE__ */ React.createElement(DefaultMenuItem, { label }, /* @__PURE__ */ React.createElement(
755
+ DefaultMenuCheckbox,
756
+ {
757
+ label,
758
+ defaultChecked: true,
759
+ storageKey: "vds-player::announcements",
760
+ onChange
761
+ }
762
+ ));
763
+ }
764
+ DefaultAnnouncementsMenuCheckbox.displayName = "DefaultAnnouncementsMenuCheckbox";
765
+ function DefaultKeyboardAnimationsMenuCheckbox() {
766
+ const $viewType = useMediaState("viewType"), { userPrefersKeyboardAnimations, noKeyboardAnimations } = useDefaultLayoutContext(), label = useDefaultLayoutWord("Keyboard Animations");
767
+ if ($viewType !== "video" || noKeyboardAnimations) return null;
768
+ function onChange(checked) {
769
+ userPrefersKeyboardAnimations.set(checked);
770
+ }
771
+ return /* @__PURE__ */ React.createElement(DefaultMenuItem, { label }, /* @__PURE__ */ React.createElement(
772
+ DefaultMenuCheckbox,
773
+ {
774
+ label,
775
+ defaultChecked: true,
776
+ storageKey: "vds-player::keyboard-animations",
777
+ onChange
778
+ }
779
+ ));
780
+ }
781
+ DefaultKeyboardAnimationsMenuCheckbox.displayName = "DefaultKeyboardAnimationsMenuCheckbox";
782
+
783
+ function DefaultAudioMenu({ slots }) {
784
+ const label = useDefaultLayoutWord("Audio"), $canSetAudioGain = useMediaState("canSetAudioGain"), $audioTracks = useMediaState("audioTracks"), { noAudioGain, icons: Icons } = useDefaultLayoutContext(), hasGainSlider = $canSetAudioGain && !noAudioGain, $disabled = !hasGainSlider && $audioTracks.length <= 1;
785
+ if ($disabled) return null;
786
+ return /* @__PURE__ */ React.createElement(Root$3, { className: "vds-audio-menu vds-menu" }, /* @__PURE__ */ React.createElement(DefaultMenuButton, { label, Icon: Icons.Menu.Audio }), /* @__PURE__ */ React.createElement(Items, { className: "vds-menu-items" }, slot(slots, "audioMenuItemsStart", null), /* @__PURE__ */ React.createElement(DefaultAudioTracksMenu, null), hasGainSlider ? /* @__PURE__ */ React.createElement(DefaultAudioBoostMenuSection, null) : null, slot(slots, "audioMenuItemsEnd", null)));
787
+ }
788
+ DefaultAudioMenu.displayName = "DefaultAudioMenu";
789
+ function DefaultAudioBoostMenuSection() {
790
+ const $audioGain = useMediaState("audioGain"), label = useDefaultLayoutWord("Boost"), value = Math.round((($audioGain ?? 1) - 1) * 100) + "%", $canSetAudioGain = useMediaState("canSetAudioGain"), { noAudioGain, icons: Icons } = useDefaultLayoutContext(), $disabled = !$canSetAudioGain || noAudioGain, min = useGainMin(), max = useGainMax();
791
+ if ($disabled) return null;
792
+ return /* @__PURE__ */ React.createElement(DefaultMenuSection, { label, value }, /* @__PURE__ */ React.createElement(
793
+ DefaultMenuSliderItem,
794
+ {
795
+ UpIcon: Icons.Menu.AudioBoostUp,
796
+ DownIcon: Icons.Menu.AudioBoostDown,
797
+ isMin: (($audioGain ?? 1) - 1) * 100 <= min,
798
+ isMax: (($audioGain ?? 1) - 1) * 100 === max
799
+ },
800
+ /* @__PURE__ */ React.createElement(DefaultAudioGainSlider, null)
801
+ ));
802
+ }
803
+ DefaultAudioBoostMenuSection.displayName = "DefaultAudioBoostMenuSection";
804
+ function useGainMin() {
805
+ const { audioGains } = useDefaultLayoutContext(), min = isArray(audioGains) ? audioGains[0] : audioGains?.min;
806
+ return min ?? 0;
807
+ }
808
+ function useGainMax() {
809
+ const { audioGains } = useDefaultLayoutContext(), max = isArray(audioGains) ? audioGains[audioGains.length - 1] : audioGains?.max;
810
+ return max ?? 300;
811
+ }
812
+ function useGainStep() {
813
+ const { audioGains } = useDefaultLayoutContext(), step = isArray(audioGains) ? audioGains[1] - audioGains[0] : audioGains?.step;
814
+ return step || 25;
815
+ }
816
+ function DefaultAudioGainSlider() {
817
+ const label = useDefaultLayoutWord("Audio Boost"), min = useGainMin(), max = useGainMax(), step = useGainStep();
818
+ return /* @__PURE__ */ React.createElement(
819
+ Root$5,
820
+ {
821
+ className: "vds-audio-gain-slider vds-slider",
822
+ "aria-label": label,
823
+ min,
824
+ max,
825
+ step,
826
+ keyStep: step
827
+ },
828
+ /* @__PURE__ */ React.createElement(DefaultSliderParts, null),
829
+ /* @__PURE__ */ React.createElement(DefaultSliderSteps, null)
830
+ );
831
+ }
832
+ DefaultAudioGainSlider.displayName = "DefaultAudioGainSlider";
833
+ function DefaultAudioTracksMenu() {
834
+ const { icons: Icons } = useDefaultLayoutContext(), label = useDefaultLayoutWord("Track"), defaultText = useDefaultLayoutWord("Default"), $track = useMediaState("audioTrack"), options = useAudioOptions();
835
+ if (options.disabled) return null;
836
+ return /* @__PURE__ */ React.createElement(Root$3, { className: "vds-audio-track-menu vds-menu" }, /* @__PURE__ */ React.createElement(
837
+ DefaultMenuButton,
838
+ {
839
+ label,
840
+ hint: $track?.label ?? defaultText,
841
+ disabled: options.disabled,
842
+ Icon: Icons.Menu.Audio
843
+ }
844
+ ), /* @__PURE__ */ React.createElement(Items, { className: "vds-menu-items" }, /* @__PURE__ */ React.createElement(
845
+ Root$1,
846
+ {
847
+ className: "vds-audio-radio-group vds-radio-group",
848
+ value: options.selectedValue
849
+ },
850
+ options.map(({ label: label2, value, select }) => /* @__PURE__ */ React.createElement(
851
+ Item,
852
+ {
853
+ className: "vds-audio-radio vds-radio",
854
+ value,
855
+ onSelect: select,
856
+ key: value
857
+ },
858
+ /* @__PURE__ */ React.createElement(Icons.Menu.RadioCheck, { className: "vds-icon" }),
859
+ /* @__PURE__ */ React.createElement("span", { className: "vds-radio-label" }, label2)
860
+ ))
861
+ )));
862
+ }
863
+ DefaultAudioTracksMenu.displayName = "DefaultAudioTracksMenu";
864
+
865
+ function DefaultCaptionMenu({ slots }) {
866
+ const { icons: Icons } = useDefaultLayoutContext(), label = useDefaultLayoutWord("Captions"), offText = useDefaultLayoutWord("Off"), options = useCaptionOptions({ off: offText }), hint = options.selectedTrack?.label ?? offText;
867
+ if (options.disabled) return null;
868
+ return /* @__PURE__ */ React.createElement(Root$3, { className: "vds-captions-menu vds-menu" }, /* @__PURE__ */ React.createElement(
869
+ DefaultMenuButton,
870
+ {
871
+ label,
872
+ hint,
873
+ disabled: options.disabled,
874
+ Icon: Icons.Menu.Captions
875
+ }
876
+ ), /* @__PURE__ */ React.createElement(Items, { className: "vds-menu-items" }, slot(slots, "captionsMenuItemsStart", null), /* @__PURE__ */ React.createElement(
877
+ Root$1,
878
+ {
879
+ className: "vds-captions-radio-group vds-radio-group",
880
+ value: options.selectedValue
881
+ },
882
+ options.map(({ label: label2, value, select }) => /* @__PURE__ */ React.createElement(
883
+ Item,
884
+ {
885
+ className: "vds-caption-radio vds-radio",
886
+ value,
887
+ onSelect: select,
888
+ key: value
889
+ },
890
+ /* @__PURE__ */ React.createElement(Icons.Menu.RadioCheck, { className: "vds-icon" }),
891
+ /* @__PURE__ */ React.createElement("span", { className: "vds-radio-label" }, label2)
892
+ ))
893
+ ), slot(slots, "captionsMenuItemsEnd", null)));
894
+ }
895
+ DefaultCaptionMenu.displayName = "DefaultCaptionMenu";
896
+
897
+ function DefaultPlaybackMenu({ slots }) {
898
+ const label = useDefaultLayoutWord("Playback"), { icons: Icons } = useDefaultLayoutContext();
899
+ return /* @__PURE__ */ React.createElement(Root$3, { className: "vds-playback-menu vds-menu" }, /* @__PURE__ */ React.createElement(DefaultMenuButton, { label, Icon: Icons.Menu.Playback }), /* @__PURE__ */ React.createElement(Items, { className: "vds-menu-items" }, slot(slots, "playbackMenuItemsStart", null), /* @__PURE__ */ React.createElement(DefaultMenuSection, null, slot(slots, "playbackMenuLoop", /* @__PURE__ */ React.createElement(DefaultLoopMenuCheckbox, null))), /* @__PURE__ */ React.createElement(DefaultSpeedMenuSection, null), /* @__PURE__ */ React.createElement(DefaultQualityMenuSection, null), slot(slots, "playbackMenuItemsEnd", null)));
900
+ }
901
+ DefaultPlaybackMenu.displayName = "DefaultPlaybackMenu";
902
+ function DefaultLoopMenuCheckbox() {
903
+ const { remote } = useMediaContext(), label = useDefaultLayoutWord("Loop");
904
+ function onChange(checked, trigger) {
905
+ remote.userPrefersLoopChange(checked, trigger);
906
+ }
907
+ return /* @__PURE__ */ React.createElement(DefaultMenuItem, { label }, /* @__PURE__ */ React.createElement(DefaultMenuCheckbox, { label, storageKey: "vds-player::user-loop", onChange }));
908
+ }
909
+ DefaultLoopMenuCheckbox.displayName = "DefaultLoopMenuCheckbox";
910
+ function DefaultAutoQualityMenuCheckbox() {
911
+ const { remote, qualities } = useMediaContext(), $autoQuality = useMediaState("autoQuality"), label = useDefaultLayoutWord("Auto");
912
+ function onChange(checked, trigger) {
913
+ if (checked) {
914
+ remote.requestAutoQuality(trigger);
915
+ } else {
916
+ remote.changeQuality(qualities.selectedIndex, trigger);
917
+ }
918
+ }
919
+ return /* @__PURE__ */ React.createElement(DefaultMenuItem, { label }, /* @__PURE__ */ React.createElement(
920
+ DefaultMenuCheckbox,
921
+ {
922
+ label,
923
+ checked: $autoQuality,
924
+ onChange,
925
+ defaultChecked: $autoQuality
926
+ }
927
+ ));
928
+ }
929
+ DefaultAutoQualityMenuCheckbox.displayName = "DefaultAutoQualityMenuCheckbox";
930
+ function DefaultQualityMenuSection() {
931
+ const { hideQualityBitrate, icons: Icons } = useDefaultLayoutContext(), $canSetQuality = useMediaState("canSetQuality"), $qualities = useMediaState("qualities"), $quality = useMediaState("quality"), label = useDefaultLayoutWord("Quality"), autoText = useDefaultLayoutWord("Auto"), sortedQualities = React.useMemo(() => sortVideoQualities($qualities), [$qualities]);
932
+ if (!$canSetQuality || $qualities.length <= 1) return null;
933
+ const height = $quality?.height, bitrate = !hideQualityBitrate ? $quality?.bitrate : null, bitrateText = bitrate && bitrate > 0 ? `${(bitrate / 1e6).toFixed(2)} Mbps` : null, value = height ? `${height}p${bitrateText ? ` (${bitrateText})` : ""}` : autoText, isMin = sortedQualities[0] === $quality, isMax = sortedQualities.at(-1) === $quality;
934
+ return /* @__PURE__ */ React.createElement(DefaultMenuSection, { label, value }, /* @__PURE__ */ React.createElement(
935
+ DefaultMenuSliderItem,
936
+ {
937
+ UpIcon: Icons.Menu.QualityUp,
938
+ DownIcon: Icons.Menu.QualityDown,
939
+ isMin,
940
+ isMax
941
+ },
942
+ /* @__PURE__ */ React.createElement(DefaultQualitySlider, null)
943
+ ), /* @__PURE__ */ React.createElement(DefaultAutoQualityMenuCheckbox, null));
944
+ }
945
+ DefaultQualityMenuSection.displayName = "DefaultQualityMenuSection";
946
+ function DefaultQualitySlider() {
947
+ const label = useDefaultLayoutWord("Quality");
948
+ return /* @__PURE__ */ React.createElement(Root$7, { className: "vds-quality-slider vds-slider", "aria-label": label }, /* @__PURE__ */ React.createElement(DefaultSliderParts, null), /* @__PURE__ */ React.createElement(DefaultSliderSteps, null));
949
+ }
950
+ DefaultQualitySlider.displayName = "DefaultQualitySlider";
951
+ function DefaultSpeedMenuSection() {
952
+ const { icons: Icons } = useDefaultLayoutContext(), $playbackRate = useMediaState("playbackRate"), $canSetPlaybackRate = useMediaState("canSetPlaybackRate"), label = useDefaultLayoutWord("Speed"), normalText = useDefaultLayoutWord("Normal"), min = useSpeedMin(), max = useSpeedMax(), value = $playbackRate === 1 ? normalText : $playbackRate + "x";
953
+ if (!$canSetPlaybackRate) return null;
954
+ return /* @__PURE__ */ React.createElement(DefaultMenuSection, { label, value }, /* @__PURE__ */ React.createElement(
955
+ DefaultMenuSliderItem,
956
+ {
957
+ UpIcon: Icons.Menu.SpeedUp,
958
+ DownIcon: Icons.Menu.SpeedDown,
959
+ isMin: $playbackRate === min,
960
+ isMax: $playbackRate === max
961
+ },
962
+ /* @__PURE__ */ React.createElement(DefaultSpeedSlider, null)
963
+ ));
964
+ }
965
+ function useSpeedMin() {
966
+ const { playbackRates } = useDefaultLayoutContext(), rates = playbackRates;
967
+ return (isArray(rates) ? rates[0] : rates?.min) ?? 0;
968
+ }
969
+ function useSpeedMax() {
970
+ const { playbackRates } = useDefaultLayoutContext(), rates = playbackRates;
971
+ return (isArray(rates) ? rates[rates.length - 1] : rates?.max) ?? 2;
972
+ }
973
+ function useSpeedStep() {
974
+ const { playbackRates } = useDefaultLayoutContext(), rates = playbackRates;
975
+ return (isArray(rates) ? rates[1] - rates[0] : rates?.step) || 0.25;
976
+ }
977
+ function DefaultSpeedSlider() {
978
+ const label = useDefaultLayoutWord("Speed"), min = useSpeedMin(), max = useSpeedMax(), step = useSpeedStep();
979
+ return /* @__PURE__ */ React.createElement(
980
+ Root$6,
981
+ {
982
+ className: "vds-speed-slider vds-slider",
983
+ "aria-label": label,
984
+ min,
985
+ max,
986
+ step,
987
+ keyStep: step
988
+ },
989
+ /* @__PURE__ */ React.createElement(DefaultSliderParts, null),
990
+ /* @__PURE__ */ React.createElement(DefaultSliderSteps, null)
991
+ );
992
+ }
993
+ DefaultSpeedSlider.displayName = "DefaultSpeedSlider";
994
+
995
+ function DefaultSettingsMenu({
996
+ tooltip,
997
+ placement,
998
+ portalClass = "",
999
+ slots
1000
+ }) {
1001
+ const {
1002
+ showMenuDelay,
1003
+ icons: Icons,
1004
+ isSmallLayout,
1005
+ menuContainer,
1006
+ menuGroup,
1007
+ noModal,
1008
+ colorScheme
1009
+ } = useDefaultLayoutContext(), settingsText = useDefaultLayoutWord("Settings"), $viewType = useMediaState("viewType"), $offset = !isSmallLayout && menuGroup === "bottom" && $viewType === "video" ? 26 : 0, colorSchemeClass = useColorSchemeClass(colorScheme), [isOpen, setIsOpen] = React.useState(false), dialogEl = useParentDialogEl();
1010
+ useScoped(updateFontCssVars);
1011
+ function onOpen() {
1012
+ flushSync(() => {
1013
+ setIsOpen(true);
1014
+ });
1015
+ }
1016
+ function onClose() {
1017
+ setIsOpen(false);
1018
+ }
1019
+ const Content = /* @__PURE__ */ React.createElement(
1020
+ Items,
1021
+ {
1022
+ className: "vds-settings-menu-items vds-menu-items",
1023
+ placement,
1024
+ offset: $offset
1025
+ },
1026
+ isOpen ? /* @__PURE__ */ React.createElement(React.Fragment, null, slot(slots, "settingsMenuItemsStart", null), slot(slots, "settingsMenuStartItems", null), /* @__PURE__ */ React.createElement(DefaultPlaybackMenu, { slots }), /* @__PURE__ */ React.createElement(DefaultAccessibilityMenu, { slots }), /* @__PURE__ */ React.createElement(DefaultAudioMenu, { slots }), /* @__PURE__ */ React.createElement(DefaultCaptionMenu, { slots }), slot(slots, "settingsMenuEndItems", null), slot(slots, "settingsMenuItemsEnd", null)) : null
1027
+ );
1028
+ return /* @__PURE__ */ React.createElement(
1029
+ Root$3,
1030
+ {
1031
+ className: "vds-settings-menu vds-menu",
1032
+ showDelay: showMenuDelay,
1033
+ onOpen,
1034
+ onClose
1035
+ },
1036
+ /* @__PURE__ */ React.createElement(DefaultTooltip, { content: settingsText, placement: tooltip }, /* @__PURE__ */ React.createElement(Button, { className: "vds-menu-button vds-button", "aria-label": settingsText }, /* @__PURE__ */ React.createElement(Icons.Menu.Settings, { className: "vds-icon vds-rotate-icon" }))),
1037
+ noModal || !isSmallLayout ? Content : /* @__PURE__ */ React.createElement(
1038
+ Portal,
1039
+ {
1040
+ className: portalClass + (colorSchemeClass ? ` ${colorSchemeClass}` : ""),
1041
+ container: menuContainer ?? dialogEl,
1042
+ disabled: "fullscreen",
1043
+ "data-sm": isSmallLayout ? "" : null,
1044
+ "data-lg": !isSmallLayout ? "" : null,
1045
+ "data-size": isSmallLayout ? "sm" : "lg",
1046
+ "data-view-type": $viewType
1047
+ },
1048
+ Content
1049
+ )
1050
+ );
1051
+ }
1052
+ DefaultSettingsMenu.displayName = "DefaultSettingsMenu";
1053
+
1054
+ function DefaultVolumePopup({ tooltip, orientation, slots }) {
1055
+ const $pointer = useMediaState("pointer"), $muted = useMediaState("muted"), $canSetVolume = useMediaState("canSetVolume"), [rootEl, setRootEl] = React.useState(null), isRootActive = useActive(rootEl), muteButton = slot(slots, "muteButton", /* @__PURE__ */ React.createElement(DefaultMuteButton, { tooltip }));
1056
+ if (!$canSetVolume) {
1057
+ return muteButton;
1058
+ }
1059
+ return $pointer === "coarse" && !$muted ? null : /* @__PURE__ */ React.createElement("div", { className: "vds-volume", "data-active": isRootActive ? "" : null, ref: setRootEl }, muteButton, /* @__PURE__ */ React.createElement("div", { className: "vds-volume-popup" }, slot(slots, "volumeSlider", /* @__PURE__ */ React.createElement(DefaultVolumeSlider, { orientation }))));
1060
+ }
1061
+ DefaultVolumePopup.displayName = "DefaultVolumePopup";
1062
+ function DefaultVolumeSlider(props) {
1063
+ const label = useDefaultLayoutWord("Volume");
1064
+ return /* @__PURE__ */ React.createElement(Root$8, { className: "vds-volume-slider vds-slider", "aria-label": label, ...props }, /* @__PURE__ */ React.createElement(Track, { className: "vds-slider-track" }), /* @__PURE__ */ React.createElement(TrackFill, { className: "vds-slider-track-fill vds-slider-track" }), /* @__PURE__ */ React.createElement(Thumb, { className: "vds-slider-thumb" }), /* @__PURE__ */ React.createElement(Preview, { className: "vds-slider-preview", noClamp: true }, /* @__PURE__ */ React.createElement(Value, { className: "vds-slider-value" })));
1065
+ }
1066
+ DefaultVolumeSlider.displayName = "DefaultVolumeSlider";
1067
+ function DefaultTimeSlider() {
1068
+ const [instance, setInstance] = React.useState(null), [width, setWidth] = React.useState(0), $src = useMediaState("currentSrc"), { thumbnails, sliderChaptersMinWidth, disableTimeSlider, seekStep, noScrubGesture } = useDefaultLayoutContext(), label = useDefaultLayoutWord("Seek"), $RemotionSliderThumbnail = useSignal(RemotionSliderThumbnail);
1069
+ const onResize = React.useCallback(() => {
1070
+ const el = instance?.el;
1071
+ el && setWidth(el.clientWidth);
1072
+ }, [instance]);
1073
+ useResizeObserver(instance?.el, onResize);
1074
+ return /* @__PURE__ */ React.createElement(
1075
+ Root$9,
1076
+ {
1077
+ className: "vds-time-slider vds-slider",
1078
+ "aria-label": label,
1079
+ disabled: disableTimeSlider,
1080
+ noSwipeGesture: noScrubGesture,
1081
+ keyStep: seekStep,
1082
+ ref: setInstance
1083
+ },
1084
+ /* @__PURE__ */ React.createElement(
1085
+ Chapters,
1086
+ {
1087
+ className: "vds-slider-chapters",
1088
+ disabled: width < sliderChaptersMinWidth
1089
+ },
1090
+ (cues, forwardRef) => cues.map((cue) => /* @__PURE__ */ React.createElement("div", { className: "vds-slider-chapter", key: cue.startTime, ref: forwardRef }, /* @__PURE__ */ React.createElement(Track, { className: "vds-slider-track" }), /* @__PURE__ */ React.createElement(TrackFill, { className: "vds-slider-track-fill vds-slider-track" }), /* @__PURE__ */ React.createElement(Progress, { className: "vds-slider-progress vds-slider-track" })))
1091
+ ),
1092
+ /* @__PURE__ */ React.createElement(Thumb, { className: "vds-slider-thumb" }),
1093
+ /* @__PURE__ */ React.createElement(Preview, { className: "vds-slider-preview" }, thumbnails ? /* @__PURE__ */ React.createElement(
1094
+ Thumbnail.Root,
1095
+ {
1096
+ src: thumbnails,
1097
+ className: "vds-slider-thumbnail vds-thumbnail"
1098
+ },
1099
+ /* @__PURE__ */ React.createElement(Thumbnail.Img, null)
1100
+ ) : $RemotionSliderThumbnail && isRemotionSrc($src) ? /* @__PURE__ */ React.createElement($RemotionSliderThumbnail, { className: "vds-slider-thumbnail vds-thumbnail" }) : null, /* @__PURE__ */ React.createElement(ChapterTitle, { className: "vds-slider-chapter-title" }), /* @__PURE__ */ React.createElement(Value, { className: "vds-slider-value" }))
1101
+ );
1102
+ }
1103
+ DefaultTimeSlider.displayName = "DefaultTimeSlider";
1104
+
1105
+ function DefaultTimeGroup({ slots }) {
1106
+ const $duration = useMediaState("duration");
1107
+ if (!$duration) return null;
1108
+ return /* @__PURE__ */ React.createElement("div", { className: "vds-time-group" }, slot(slots, "currentTime", /* @__PURE__ */ React.createElement(Time, { className: "vds-time", type: "current" })), slot(slots, "timeDivider", /* @__PURE__ */ React.createElement("div", { className: "vds-time-divider" }, "/")), slot(slots, "endTime", /* @__PURE__ */ React.createElement(Time, { className: "vds-time", type: "duration" })));
1109
+ }
1110
+ DefaultTimeGroup.displayName = "DefaultTimeGroup";
1111
+ function DefaultTimeInfo({ slots }) {
1112
+ const $live = useMediaState("live");
1113
+ return $live ? slot(slots, "liveButton", /* @__PURE__ */ React.createElement(DefaultLiveButton, null)) : /* @__PURE__ */ React.createElement(DefaultTimeGroup, { slots });
1114
+ }
1115
+ DefaultTimeInfo.displayName = "DefaultTimeInfo";
1116
+ function DefaultTimeInvert({ slots }) {
1117
+ const $live = useMediaState("live"), $duration = useMediaState("duration");
1118
+ return $live ? slot(slots, "liveButton", /* @__PURE__ */ React.createElement(DefaultLiveButton, null)) : slot(
1119
+ slots,
1120
+ "endTime",
1121
+ $duration ? /* @__PURE__ */ React.createElement(Time, { className: "vds-time", type: "current", toggle: true, remainder: true }) : null
1122
+ );
1123
+ }
1124
+ DefaultTimeInvert.displayName = "DefaultTimeInvert";
1125
+
1126
+ const MediaLayout$1 = createDefaultMediaLayout({
1127
+ type: "audio",
1128
+ smLayoutWhen({ width }) {
1129
+ return width < 576;
1130
+ },
1131
+ renderLayout: () => /* @__PURE__ */ React.createElement(AudioLayout, null)
1132
+ });
1133
+ function DefaultAudioLayout(props) {
1134
+ const [scrubbing, setScrubbing] = React.useState(false), $pointer = useMediaState("pointer");
1135
+ const onStartScrubbing = React.useCallback((event) => {
1136
+ const { target } = event, hasTimeSlider = !!(target instanceof HTMLElement && target.closest(".vds-time-slider"));
1137
+ if (!hasTimeSlider) return;
1138
+ event.nativeEvent.stopImmediatePropagation();
1139
+ setScrubbing(true);
1140
+ }, []);
1141
+ const onStopScrubbing = React.useCallback(() => {
1142
+ setScrubbing(false);
1143
+ }, []);
1144
+ React.useEffect(() => {
1145
+ if (scrubbing) return listenEvent(window, "pointerdown", onStopScrubbing);
1146
+ }, [scrubbing, onStopScrubbing]);
1147
+ return /* @__PURE__ */ React.createElement(
1148
+ MediaLayout$1,
1149
+ {
1150
+ ...props,
1151
+ "data-scrubbing": scrubbing ? "" : null,
1152
+ onPointerDown: scrubbing ? (e) => e.stopPropagation() : void 0,
1153
+ onPointerDownCapture: $pointer === "coarse" && !scrubbing ? onStartScrubbing : void 0
1154
+ }
1155
+ );
1156
+ }
1157
+ DefaultAudioLayout.displayName = "DefaultAudioLayout";
1158
+ function AudioLayout() {
1159
+ const slots = useDefaultAudioLayoutSlots();
1160
+ useLayoutName("audio");
1161
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(DefaultAnnouncer, null), /* @__PURE__ */ React.createElement(DefaultCaptions, null), /* @__PURE__ */ React.createElement(Root$a, { className: "vds-controls" }, /* @__PURE__ */ React.createElement(Group, { className: "vds-controls-group" }, slot(slots, "seekBackwardButton", /* @__PURE__ */ React.createElement(DefaultSeekButton, { backward: true, tooltip: "top start" })), slot(slots, "playButton", /* @__PURE__ */ React.createElement(DefaultPlayButton, { tooltip: "top center" })), slot(slots, "seekForwardButton", /* @__PURE__ */ React.createElement(DefaultSeekButton, { tooltip: "top center" })), /* @__PURE__ */ React.createElement(DefaultAudioTitle, null), slot(slots, "timeSlider", /* @__PURE__ */ React.createElement(DefaultTimeSlider, null)), /* @__PURE__ */ React.createElement(DefaultTimeInvert, { slots }), /* @__PURE__ */ React.createElement(DefaultVolumePopup, { orientation: "vertical", tooltip: "top", slots }), slot(slots, "captionButton", /* @__PURE__ */ React.createElement(DefaultCaptionButton, { tooltip: "top center" })), slot(slots, "downloadButton", /* @__PURE__ */ React.createElement(DefaultDownloadButton, null)), /* @__PURE__ */ React.createElement(DefaultAudioMenus, { slots }))));
1162
+ }
1163
+ AudioLayout.displayName = "AudioLayout";
1164
+ function DefaultAudioMenus({ slots }) {
1165
+ const { isSmallLayout, noModal } = useDefaultLayoutContext(), placement = noModal ? "top end" : !isSmallLayout ? "top end" : null;
1166
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, slot(
1167
+ slots,
1168
+ "chaptersMenu",
1169
+ /* @__PURE__ */ React.createElement(DefaultChaptersMenu, { tooltip: "top", placement, portalClass: "vds-audio-layout" })
1170
+ ), slot(
1171
+ slots,
1172
+ "settingsMenu",
1173
+ /* @__PURE__ */ React.createElement(
1174
+ DefaultSettingsMenu,
1175
+ {
1176
+ tooltip: "top end",
1177
+ placement,
1178
+ portalClass: "vds-audio-layout",
1179
+ slots
1180
+ }
1181
+ )
1182
+ ));
1183
+ }
1184
+ DefaultAudioMenus.displayName = "DefaultAudioMenus";
1185
+ function DefaultAudioTitle() {
1186
+ const [rootEl, setRootEl] = React.useState(null), media = useMediaContext(), { translations } = useDefaultLayoutContext(), [isTextOverflowing, setIsTextOverflowing] = React.useState(false);
1187
+ const isContinued = createComputed(() => {
1188
+ const { started, currentTime } = media.$state;
1189
+ return started() || currentTime() > 0;
1190
+ });
1191
+ const $title = useSignal(
1192
+ createComputed(() => {
1193
+ const { title, ended } = media.$state;
1194
+ if (!title()) return "";
1195
+ const word = ended() ? "Replay" : isContinued() ? "Continue" : "Play";
1196
+ return `${i18n(translations, word)}: ${title()}`;
1197
+ })
1198
+ );
1199
+ const chapterTitle = useChapterTitle(), $isContinued = useSignal(isContinued), $chapterTitle = $isContinued ? chapterTitle : "", isTransitionActive = useTransitionActive(rootEl);
1200
+ React.useEffect(() => {
1201
+ if (isTransitionActive && document.activeElement === document.body) {
1202
+ media.player.el?.focus({ preventScroll: true });
1203
+ }
1204
+ }, []);
1205
+ const onResize = React.useCallback(() => {
1206
+ const el = rootEl, isOverflowing = !!el && !isTransitionActive && el.clientWidth < el.children[0].clientWidth;
1207
+ if (el) toggleClass(el, "vds-marquee", isOverflowing);
1208
+ setIsTextOverflowing(isOverflowing);
1209
+ }, [rootEl, isTransitionActive]);
1210
+ useResizeObserver(rootEl, onResize);
1211
+ return $title ? /* @__PURE__ */ React.createElement("span", { className: "vds-title", title: $title, ref: setRootEl }, /* @__PURE__ */ React.createElement(AudioTitle, { title: $title, chapterTitle: $chapterTitle }), isTextOverflowing && !isTransitionActive ? /* @__PURE__ */ React.createElement(AudioTitle, { title: $title, chapterTitle: $chapterTitle }) : null) : /* @__PURE__ */ React.createElement(DefaultControlsSpacer, null);
1212
+ }
1213
+ DefaultAudioTitle.displayName = "DefaultAudioTitle";
1214
+ function AudioTitle({ title, chapterTitle }) {
1215
+ const slots = useDefaultAudioLayoutSlots();
1216
+ return /* @__PURE__ */ React.createElement("span", { className: "vds-title-text" }, slot(slots, "title", title), slot(slots, "chapterTitle", /* @__PURE__ */ React.createElement("span", { className: "vds-chapter-title" }, chapterTitle)));
1217
+ }
1218
+ AudioTitle.displayName = "AudioTitle";
1219
+
1220
+ const DefaultKeyboardDisplay = React.forwardRef(
1221
+ ({ icons: Icons, ...props }, forwardRef) => {
1222
+ const [visible, setVisible] = React.useState(false), [Icon, setIcon] = React.useState(null), [count, setCount] = React.useState(0), $lastKeyboardAction = useMediaState("lastKeyboardAction");
1223
+ React.useEffect(() => {
1224
+ setCount((n) => n + 1);
1225
+ }, [$lastKeyboardAction]);
1226
+ const actionDataAttr = React.useMemo(() => {
1227
+ const action = $lastKeyboardAction?.action;
1228
+ return action && visible ? camelToKebabCase(action) : null;
1229
+ }, [visible, $lastKeyboardAction]);
1230
+ const className = React.useMemo(
1231
+ () => `vds-kb-action${!visible ? " hidden" : ""}${props.className ? ` ${props.className}` : ""}`,
1232
+ [visible]
1233
+ );
1234
+ const $$text = createComputed(getText), $text = useSignal($$text);
1235
+ createEffect(() => {
1236
+ const Icon2 = getIcon(Icons);
1237
+ setIcon(() => Icon2);
1238
+ }, [Icons]);
1239
+ React.useEffect(() => {
1240
+ setVisible(!!$lastKeyboardAction);
1241
+ const id = setTimeout(() => setVisible(false), 500);
1242
+ return () => {
1243
+ setVisible(false);
1244
+ window.clearTimeout(id);
1245
+ };
1246
+ }, [$lastKeyboardAction]);
1247
+ return Icon ? /* @__PURE__ */ React.createElement(
1248
+ Primitive.div,
1249
+ {
1250
+ ...props,
1251
+ className,
1252
+ "data-action": actionDataAttr,
1253
+ ref: forwardRef
1254
+ },
1255
+ /* @__PURE__ */ React.createElement("div", { className: "vds-kb-text-wrapper" }, /* @__PURE__ */ React.createElement("div", { className: "vds-kb-text" }, $text)),
1256
+ /* @__PURE__ */ React.createElement("div", { className: "vds-kb-bezel", key: count }, /* @__PURE__ */ React.createElement("div", { className: "vds-kb-icon" }, /* @__PURE__ */ React.createElement(Icon, null)))
1257
+ ) : null;
1258
+ }
1259
+ );
1260
+ DefaultKeyboardDisplay.displayName = "DefaultKeyboardDisplay";
1261
+ function getText() {
1262
+ const { $state } = useContext(mediaContext), action = $state.lastKeyboardAction()?.action, audioGain = $state.audioGain() ?? 1;
1263
+ switch (action) {
1264
+ case "toggleMuted":
1265
+ return $state.muted() ? "0%" : getVolumeText($state.volume(), audioGain);
1266
+ case "volumeUp":
1267
+ case "volumeDown":
1268
+ return getVolumeText($state.volume(), audioGain);
1269
+ default:
1270
+ return "";
1271
+ }
1272
+ }
1273
+ function getVolumeText(volume, gain) {
1274
+ return `${Math.round(volume * gain * 100)}%`;
1275
+ }
1276
+ function getIcon(Icons) {
1277
+ const { $state } = useContext(mediaContext), action = $state.lastKeyboardAction()?.action;
1278
+ switch (action) {
1279
+ case "togglePaused":
1280
+ return !$state.paused() ? Icons.Play : Icons.Pause;
1281
+ case "toggleMuted":
1282
+ return $state.muted() || $state.volume() === 0 ? Icons.Mute : $state.volume() >= 0.5 ? Icons.VolumeUp : Icons.VolumeDown;
1283
+ case "toggleFullscreen":
1284
+ return $state.fullscreen() ? Icons.EnterFullscreen : Icons.ExitFullscreen;
1285
+ case "togglePictureInPicture":
1286
+ return $state.pictureInPicture() ? Icons.EnterPiP : Icons.ExitPiP;
1287
+ case "toggleCaptions":
1288
+ return $state.hasCaptions() ? $state.textTrack() ? Icons.CaptionsOn : Icons.CaptionsOff : null;
1289
+ case "volumeUp":
1290
+ return Icons.VolumeUp;
1291
+ case "volumeDown":
1292
+ return Icons.VolumeDown;
1293
+ case "seekForward":
1294
+ return Icons.SeekForward;
1295
+ case "seekBackward":
1296
+ return Icons.SeekBackward;
1297
+ default:
1298
+ return null;
1299
+ }
1300
+ }
1301
+
1302
+ function DefaultTitle() {
1303
+ const $started = useMediaState("started"), $title = useMediaState("title"), $hasChapters = useActiveTextTrack("chapters");
1304
+ return $hasChapters && ($started || !$title) ? /* @__PURE__ */ React.createElement(ChapterTitle$1, { className: "vds-chapter-title" }) : /* @__PURE__ */ React.createElement(Title, { className: "vds-chapter-title" });
1305
+ }
1306
+ DefaultTitle.displayName = "DefaultTitle";
1307
+
1308
+ const MediaLayout = createDefaultMediaLayout({
1309
+ type: "video",
1310
+ smLayoutWhen({ width, height }) {
1311
+ return width < 576 || height < 380;
1312
+ },
1313
+ renderLayout(props) {
1314
+ return /* @__PURE__ */ React.createElement(VideoLayout, { ...props });
1315
+ }
1316
+ });
1317
+ function DefaultVideoLayout(props) {
1318
+ return /* @__PURE__ */ React.createElement(MediaLayout, { ...props });
1319
+ }
1320
+ DefaultVideoLayout.displayName = "DefaultVideoLayout";
1321
+ function VideoLayout({ streamType, isLoadLayout, isSmallLayout }) {
1322
+ useLayoutName("video");
1323
+ return isLoadLayout ? /* @__PURE__ */ React.createElement(DefaultVideoLoadLayout, null) : streamType === "unknown" ? /* @__PURE__ */ React.createElement(DefaultBufferingIndicator, null) : isSmallLayout ? /* @__PURE__ */ React.createElement(DefaultVideoSmallLayout, null) : /* @__PURE__ */ React.createElement(DefaultVideoLargeLayout, null);
1324
+ }
1325
+ VideoLayout.displayName = "VideoLayout";
1326
+ function DefaultVideoLargeLayout() {
1327
+ const { menuGroup, episodes, episodesTitle, isSmallLayout } = useDefaultLayoutContext(), baseSlots = useDefaultVideoLayoutSlots(), slots = { ...baseSlots, ...baseSlots?.largeLayout }, $fullscreen = useMediaState("fullscreen"), [episodesOpen, setEpisodesOpen] = React.useState(false), list = episodes ?? [];
1328
+ React.useEffect(() => {
1329
+ if (!$fullscreen) setEpisodesOpen(false);
1330
+ }, [$fullscreen]);
1331
+ const canOpenEpisodes = $fullscreen && list.length > 0;
1332
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(DefaultAnnouncer, null), /* @__PURE__ */ React.createElement(DefaultVideoGestures, null), /* @__PURE__ */ React.createElement(DefaultVideoKeyboardDisplay, null), slot(slots, "bufferingIndicator", /* @__PURE__ */ React.createElement(DefaultBufferingIndicator, null)), slot(slots, "captions", /* @__PURE__ */ React.createElement(DefaultCaptions, null)), /* @__PURE__ */ React.createElement(Root$a, { className: "vds-controls" }, /* @__PURE__ */ React.createElement(Group, { className: "vds-controls-group" }, slot(slots, "topControlsGroupStart", null), /* @__PURE__ */ React.createElement(DefaultControlsSpacer, null), slot(slots, "topControlsGroupCenter", null), /* @__PURE__ */ React.createElement(DefaultControlsSpacer, null), slot(slots, "topControlsGroupEnd", null), menuGroup === "top" && /* @__PURE__ */ React.createElement(DefaultVideoMenus, { slots })), /* @__PURE__ */ React.createElement(DefaultControlsSpacer, null), /* @__PURE__ */ React.createElement(Group, { className: "vds-controls-group" }, slot(slots, "centerControlsGroupStart", null), /* @__PURE__ */ React.createElement(DefaultControlsSpacer, null), slot(slots, "centerControlsGroupCenter", null), /* @__PURE__ */ React.createElement(DefaultControlsSpacer, null), slot(slots, "centerControlsGroupEnd", null)), /* @__PURE__ */ React.createElement(DefaultControlsSpacer, null), /* @__PURE__ */ React.createElement(Group, { className: "vds-controls-group" }, slot(slots, "timeSlider", /* @__PURE__ */ React.createElement(DefaultTimeSlider, null))), /* @__PURE__ */ React.createElement(Group, { className: "vds-controls-group" }, slot(slots, "playButton", /* @__PURE__ */ React.createElement(DefaultPlayButton, { tooltip: "top start" })), /* @__PURE__ */ React.createElement(DefaultSeekButton, { backward: true, tooltip: "top" }), /* @__PURE__ */ React.createElement(DefaultSeekButton, { tooltip: "top" }), /* @__PURE__ */ React.createElement(DefaultVolumePopup, { orientation: "horizontal", tooltip: "top", slots }), /* @__PURE__ */ React.createElement(DefaultTimeInfo, { slots }), slot(slots, "chapterTitle", /* @__PURE__ */ React.createElement(DefaultTitle, null)), canOpenEpisodes ? /* @__PURE__ */ React.createElement(DefaultEpisodeButton, { tooltip: "top", onPress: () => setEpisodesOpen((open) => !open) }) : null, slot(slots, "captionButton", /* @__PURE__ */ React.createElement(DefaultCaptionButton, { tooltip: "top" })), menuGroup === "bottom" && /* @__PURE__ */ React.createElement(DefaultVideoMenus, { slots }), slot(slots, "airPlayButton", /* @__PURE__ */ React.createElement(DefaultAirPlayButton, { tooltip: "top" })), slot(slots, "googleCastButton", /* @__PURE__ */ React.createElement(DefaultGoogleCastButton, { tooltip: "top" })), slot(slots, "downloadButton", /* @__PURE__ */ React.createElement(DefaultDownloadButton, null)), slot(slots, "pipButton", /* @__PURE__ */ React.createElement(DefaultPIPButton, { tooltip: "top" })), slot(slots, "fullscreenButton", /* @__PURE__ */ React.createElement(DefaultFullscreenButton, { tooltip: "top end" })))), /* @__PURE__ */ React.createElement(
1333
+ DefaultEpisodesSidebar,
1334
+ {
1335
+ open: episodesOpen,
1336
+ onClose: () => setEpisodesOpen(false),
1337
+ episodes: list,
1338
+ episodesTitle: episodesTitle ?? "Episodes"
1339
+ }
1340
+ ));
1341
+ }
1342
+ DefaultVideoLargeLayout.displayName = "DefaultVideoLargeLayout";
1343
+ function DefaultVideoSmallLayout() {
1344
+ const { episodes, episodesTitle, isSmallLayout } = useDefaultLayoutContext(), baseSlots = useDefaultVideoLayoutSlots(), slots = { ...baseSlots, ...baseSlots?.smallLayout }, $fullscreen = useMediaState("fullscreen"), [episodesOpen, setEpisodesOpen] = React.useState(false), list = episodes ?? [];
1345
+ React.useEffect(() => {
1346
+ if (!$fullscreen) setEpisodesOpen(false);
1347
+ }, [$fullscreen]);
1348
+ const canOpenEpisodes = $fullscreen && list.length > 0;
1349
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(DefaultAnnouncer, null), /* @__PURE__ */ React.createElement(DefaultVideoGestures, null), /* @__PURE__ */ React.createElement(DefaultVideoKeyboardDisplay, null), slot(slots, "bufferingIndicator", /* @__PURE__ */ React.createElement(DefaultBufferingIndicator, null)), slot(slots, "captions", /* @__PURE__ */ React.createElement(DefaultCaptions, null)), /* @__PURE__ */ React.createElement(Root$a, { className: "vds-controls" }, /* @__PURE__ */ React.createElement(Group, { className: "vds-controls-group" }, slot(slots, "topControlsGroupStart", null), slot(slots, "airPlayButton", /* @__PURE__ */ React.createElement(DefaultAirPlayButton, { tooltip: "top start" })), slot(slots, "googleCastButton", /* @__PURE__ */ React.createElement(DefaultGoogleCastButton, { tooltip: "top start" })), /* @__PURE__ */ React.createElement(DefaultControlsSpacer, null), slot(slots, "topControlsGroupCenter", null), /* @__PURE__ */ React.createElement(DefaultControlsSpacer, null), slot(slots, "captionButton", /* @__PURE__ */ React.createElement(DefaultCaptionButton, { tooltip: "bottom" })), slot(slots, "downloadButton", /* @__PURE__ */ React.createElement(DefaultDownloadButton, null)), /* @__PURE__ */ React.createElement(DefaultVideoMenus, { slots }), /* @__PURE__ */ React.createElement(DefaultVolumePopup, { orientation: "vertical", tooltip: "bottom end", slots }), slot(slots, "topControlsGroupEnd", null)), /* @__PURE__ */ React.createElement(DefaultControlsSpacer, null), /* @__PURE__ */ React.createElement(Group, { className: "vds-controls-group" }, slot(slots, "centerControlsGroupStart", null), /* @__PURE__ */ React.createElement(DefaultControlsSpacer, null), /* @__PURE__ */ React.createElement(DefaultSeekButton, { backward: true, tooltip: "top" }), slot(slots, "centerControlsGroupCenter", null), slot(slots, "playButton", /* @__PURE__ */ React.createElement(DefaultPlayButton, { tooltip: "top" })), /* @__PURE__ */ React.createElement(DefaultSeekButton, { tooltip: "top" }), canOpenEpisodes ? /* @__PURE__ */ React.createElement(DefaultEpisodeButton, { tooltip: "top", onPress: () => setEpisodesOpen((open) => !open) }) : null, /* @__PURE__ */ React.createElement(DefaultControlsSpacer, null), slot(slots, "centerControlsGroupEnd", null)), /* @__PURE__ */ React.createElement(DefaultControlsSpacer, null), /* @__PURE__ */ React.createElement(Group, { className: "vds-controls-group" }, /* @__PURE__ */ React.createElement(DefaultTimeInfo, { slots }), slot(slots, "chapterTitle", /* @__PURE__ */ React.createElement(DefaultTitle, null)), slot(slots, "fullscreenButton", /* @__PURE__ */ React.createElement(DefaultFullscreenButton, { tooltip: "top end" }))), /* @__PURE__ */ React.createElement(Group, { className: "vds-controls-group" }, slot(slots, "timeSlider", /* @__PURE__ */ React.createElement(DefaultTimeSlider, null)))), slot(slots, "startDuration", /* @__PURE__ */ React.createElement(DefaultVideoStartDuration, null)), /* @__PURE__ */ React.createElement(
1350
+ DefaultEpisodesSidebar,
1351
+ {
1352
+ open: episodesOpen,
1353
+ onClose: () => setEpisodesOpen(false),
1354
+ episodes: list,
1355
+ episodesTitle: episodesTitle ?? "Episodes"
1356
+ }
1357
+ ));
1358
+ }
1359
+ DefaultVideoSmallLayout.displayName = "DefaultVideoSmallLayout";
1360
+ function DefaultVideoStartDuration() {
1361
+ const $duration = useMediaState("duration");
1362
+ if ($duration === 0) return null;
1363
+ return /* @__PURE__ */ React.createElement("div", { className: "vds-start-duration" }, /* @__PURE__ */ React.createElement(Time, { className: "vds-time", type: "duration" }));
1364
+ }
1365
+ DefaultVideoStartDuration.displayName = "DefaultVideoStartDuration";
1366
+ function DefaultVideoGestures() {
1367
+ const { noGestures } = useDefaultLayoutContext();
1368
+ if (noGestures) return null;
1369
+ return /* @__PURE__ */ React.createElement("div", { className: "vds-gestures" }, /* @__PURE__ */ React.createElement(Gesture, { className: "vds-gesture", event: "pointerup", action: "toggle:paused" }), /* @__PURE__ */ React.createElement(Gesture, { className: "vds-gesture", event: "pointerup", action: "toggle:controls" }), /* @__PURE__ */ React.createElement(Gesture, { className: "vds-gesture", event: "dblpointerup", action: "toggle:fullscreen" }), /* @__PURE__ */ React.createElement(Gesture, { className: "vds-gesture", event: "dblpointerup", action: "seek:-10" }), /* @__PURE__ */ React.createElement(Gesture, { className: "vds-gesture", event: "dblpointerup", action: "seek:10" }));
1370
+ }
1371
+ DefaultVideoGestures.displayName = "DefaultVideoGestures";
1372
+ function DefaultBufferingIndicator() {
1373
+ return /* @__PURE__ */ React.createElement("div", { className: "vds-buffering-indicator" }, /* @__PURE__ */ React.createElement(Root$b, { className: "vds-buffering-spinner" }, /* @__PURE__ */ React.createElement(Track$1, { className: "vds-buffering-track" }), /* @__PURE__ */ React.createElement(TrackFill$1, { className: "vds-buffering-track-fill" })));
1374
+ }
1375
+ DefaultBufferingIndicator.displayName = "DefaultBufferingIndicator";
1376
+ function DefaultVideoMenus({ slots }) {
1377
+ const { isSmallLayout, noModal, menuGroup } = useDefaultLayoutContext(), side = menuGroup === "top" || isSmallLayout ? "bottom" : "top", tooltip = `${side} end`, placement = noModal ? `${side} end` : !isSmallLayout ? `${side} end` : null;
1378
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, slot(
1379
+ slots,
1380
+ "chaptersMenu",
1381
+ /* @__PURE__ */ React.createElement(
1382
+ DefaultChaptersMenu,
1383
+ {
1384
+ tooltip,
1385
+ placement,
1386
+ portalClass: "vds-video-layout"
1387
+ }
1388
+ )
1389
+ ), slot(
1390
+ slots,
1391
+ "settingsMenu",
1392
+ /* @__PURE__ */ React.createElement(
1393
+ DefaultSettingsMenu,
1394
+ {
1395
+ tooltip,
1396
+ placement,
1397
+ portalClass: "vds-video-layout",
1398
+ slots
1399
+ }
1400
+ )
1401
+ ));
1402
+ }
1403
+ DefaultVideoMenus.displayName = "DefaultVideoMenus";
1404
+ function formatMinutesLeftLabel(secondsLeft) {
1405
+ if (secondsLeft <= 0) return null;
1406
+ if (secondsLeft < 60) return "<1m left";
1407
+ const m = Math.floor(secondsLeft / 60);
1408
+ return `${m}m left`;
1409
+ }
1410
+ function DefaultEpisodesSidebar({
1411
+ open,
1412
+ onClose,
1413
+ episodes,
1414
+ episodesTitle
1415
+ }) {
1416
+ const $fullscreen = useMediaState("fullscreen"), currentTime = useMediaState("currentTime"), duration = useMediaState("duration"), listRef = React.useRef(null), wasEpisodesOpenRef = React.useRef(false);
1417
+ React.useLayoutEffect(() => {
1418
+ const justOpened = open && !wasEpisodesOpenRef.current;
1419
+ wasEpisodesOpenRef.current = open;
1420
+ if (!justOpened || !$fullscreen) return;
1421
+ const root = listRef.current;
1422
+ if (!root) return;
1423
+ requestAnimationFrame(() => {
1424
+ root.querySelector('[data-active="true"]')?.scrollIntoView({
1425
+ block: "nearest",
1426
+ behavior: "smooth"
1427
+ });
1428
+ });
1429
+ }, [open, $fullscreen]);
1430
+ if (!$fullscreen || episodes.length === 0) return null;
1431
+ return /* @__PURE__ */ React.createElement(
1432
+ "div",
1433
+ {
1434
+ className: "vds-episodes-backdrop",
1435
+ "data-open": open ? "true" : "false",
1436
+ onPointerUp: (event) => {
1437
+ if (event.target === event.currentTarget) onClose();
1438
+ }
1439
+ },
1440
+ /* @__PURE__ */ React.createElement(
1441
+ "aside",
1442
+ {
1443
+ className: "vds-episodes-panel",
1444
+ "data-open": open ? "true" : "false",
1445
+ onKeyDown: (event) => {
1446
+ if (event.key === "Escape") onClose();
1447
+ }
1448
+ },
1449
+ /* @__PURE__ */ React.createElement("header", { className: "vds-episodes-panel-header" }, /* @__PURE__ */ React.createElement("h3", { className: "vds-episodes-panel-title" }, episodesTitle), /* @__PURE__ */ React.createElement(
1450
+ "button",
1451
+ {
1452
+ type: "button",
1453
+ className: "vds-episodes-close-btn",
1454
+ "aria-label": "Close episodes",
1455
+ onPointerUp: (event) => {
1456
+ event.stopPropagation();
1457
+ onClose();
1458
+ }
1459
+ },
1460
+ /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, "\u2715")
1461
+ )),
1462
+ /* @__PURE__ */ React.createElement("div", { ref: listRef, className: "vds-episodes-list", role: "list" }, episodes.map((episode, index) => {
1463
+ const episodeName = episode.episodeTitle || `Episode ${episode.episodeNumber ?? index + 1}`;
1464
+ const seasonEpLabel = episode.seasonNumber != null && episode.episodeNumber != null ? `S${String(episode.seasonNumber).padStart(2, "0")} \xB7 E${String(episode.episodeNumber).padStart(2, "0")}` : episodeName;
1465
+ const isActive = !!episode.isActive;
1466
+ const onEpisodeSelect = (event) => {
1467
+ event.stopPropagation();
1468
+ if (isActive) return;
1469
+ event.currentTarget.dispatchEvent(
1470
+ new CustomEvent("vds-episode-select", {
1471
+ bubbles: true,
1472
+ composed: true,
1473
+ detail: { episode, index }
1474
+ })
1475
+ );
1476
+ onClose();
1477
+ };
1478
+ let runtimeText = null;
1479
+ if (isActive && duration > 0) {
1480
+ const secsLeft = Math.max(0, duration - currentTime);
1481
+ runtimeText = formatMinutesLeftLabel(secsLeft);
1482
+ } else if (episode.timeLeft != null && episode.timeLeft > 0) {
1483
+ runtimeText = `${episode.timeLeft}m left`;
1484
+ } else if (Number.isFinite(episode.runtime) && (episode.runtime ?? 0) > 0) {
1485
+ runtimeText = `${episode.runtime}m`;
1486
+ }
1487
+ let progressPct = 0;
1488
+ if (isActive && duration > 0) {
1489
+ progressPct = Math.min(100, currentTime / duration * 100);
1490
+ } else if (episode.progressPercent != null && episode.progressPercent > 0) {
1491
+ progressPct = Math.min(100, episode.progressPercent);
1492
+ }
1493
+ const showSubtitle = !!episode.episodeTitle && episode.episodeTitle.trim() !== "" && episode.episodeTitle !== episode.title;
1494
+ return /* @__PURE__ */ React.createElement(
1495
+ "article",
1496
+ {
1497
+ key: `${episode.episodeNumber ?? index}-${episode.title ?? ""}`,
1498
+ className: "vds-episode-item",
1499
+ "data-active": isActive ? "true" : "false",
1500
+ "aria-current": isActive ? "true" : void 0,
1501
+ role: "button",
1502
+ tabIndex: 0,
1503
+ "aria-label": episode.title || episodeName,
1504
+ onPointerUp: onEpisodeSelect,
1505
+ onKeyDown: (event) => {
1506
+ if (event.key === "Enter" || event.key === " ") onEpisodeSelect(event);
1507
+ }
1508
+ },
1509
+ /* @__PURE__ */ React.createElement("div", { className: "vds-episode-thumb-wrap" }, episode.thumbnail ? /* @__PURE__ */ React.createElement(
1510
+ "img",
1511
+ {
1512
+ className: "vds-episode-thumb",
1513
+ src: episode.thumbnail,
1514
+ alt: episode.title || episodeName,
1515
+ loading: "lazy",
1516
+ decoding: "async"
1517
+ }
1518
+ ) : /* @__PURE__ */ React.createElement("div", { className: "vds-episode-thumb vds-episode-thumb-placeholder" }), progressPct > 0 ? /* @__PURE__ */ React.createElement("div", { className: "vds-episode-progress-track", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("div", { className: "vds-episode-progress-fill", style: { width: `${progressPct}%` } })) : null),
1519
+ /* @__PURE__ */ React.createElement("div", { className: "vds-episode-body" }, /* @__PURE__ */ React.createElement("div", { className: "vds-episode-meta-row" }, /* @__PURE__ */ React.createElement("div", { className: "vds-episode-meta-primary" }, isActive ? /* @__PURE__ */ React.createElement("span", { className: "vds-episode-now-chip" }, "Playing") : null, /* @__PURE__ */ React.createElement("span", { className: "vds-episode-label" }, seasonEpLabel)), runtimeText ? /* @__PURE__ */ React.createElement("span", { className: "vds-episode-runtime" }, runtimeText) : null), /* @__PURE__ */ React.createElement("h4", { className: "vds-episode-title", title: episode.title || "" }, episode.title || "-"), showSubtitle ? /* @__PURE__ */ React.createElement("p", { className: "vds-episode-subtitle", title: episode.episodeTitle }, episode.episodeTitle) : null, episode.overview ? /* @__PURE__ */ React.createElement("p", { className: "vds-episode-desc", title: episode.overview }, episode.overview) : null)
1520
+ );
1521
+ }))
1522
+ )
1523
+ );
1524
+ }
1525
+ function DefaultVideoLoadLayout() {
1526
+ const { isSmallLayout } = useDefaultLayoutContext(), baseSlots = useDefaultVideoLayoutSlots(), slots = { ...baseSlots, ...baseSlots?.[isSmallLayout ? "smallLayout" : "largeLayout"] };
1527
+ return /* @__PURE__ */ React.createElement("div", { className: "vds-load-container" }, slot(slots, "bufferingIndicator", /* @__PURE__ */ React.createElement(DefaultBufferingIndicator, null)), slot(slots, "loadButton", /* @__PURE__ */ React.createElement(DefaultPlayButton, { tooltip: "top" })));
1528
+ }
1529
+ DefaultVideoLoadLayout.displayName = "DefaultVideoLoadLayout";
1530
+ function DefaultVideoKeyboardDisplay() {
1531
+ const { noKeyboardAnimations, icons, userPrefersKeyboardAnimations } = useDefaultLayoutContext(), $userPrefersKeyboardAnimations = useSignal(userPrefersKeyboardAnimations), disabled = noKeyboardAnimations || !$userPrefersKeyboardAnimations;
1532
+ if (disabled || !icons.KeyboardDisplay) return null;
1533
+ return /* @__PURE__ */ React.createElement(DefaultKeyboardDisplay, { icons: icons.KeyboardDisplay });
1534
+ }
1535
+ DefaultVideoKeyboardDisplay.displayName = "DefaultVideoKeyboardDisplay";
1536
+
1537
+ export { DefaultAudioLayout, DefaultBufferingIndicator, DefaultKeyboardDisplay, DefaultLayoutContext, DefaultMenuButton, DefaultMenuCheckbox, DefaultMenuItem, DefaultMenuRadioGroup, DefaultMenuSection, DefaultMenuSliderItem, DefaultSliderParts, DefaultSliderSteps, DefaultTooltip, DefaultVideoGestures, DefaultVideoLargeLayout, DefaultVideoLayout, DefaultVideoSmallLayout, createRadioOptions, i18n, useDefaultLayoutContext, useDefaultLayoutWord };