@hanifhan1f/vidstack-react 1.12.33 → 1.12.34

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.
@@ -0,0 +1,1677 @@
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-t48GJbb5.js';
6
+ import { useColorSchemePreference, useActive, useResizeObserver, useLayoutName, useTransitionActive } from './vidstack-DpWioszb.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, Root$4 as Root$8, Preview, Value, Root$1 as Root$9, Chapters, Progress, Thumbnail, ChapterTitle, Time, Gesture } from './vidstack-CjLj3q5w.js';
8
+ import { useMediaState, isTrackCaptionKind, getDownloadFile, isRemotionSrc, IS_SERVER, useMediaContext as useMediaContext$1, sortVideoQualities, Primitive, mediaContext } from './vidstack-C5tZAkKo.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
+ const LANG_FLAGS = {
866
+ en: "gb",
867
+ eng: "gb",
868
+ english: "gb",
869
+ id: "id",
870
+ ind: "id",
871
+ indonesian: "id",
872
+ ja: "jp",
873
+ jpn: "jp",
874
+ japanese: "jp",
875
+ ko: "kr",
876
+ kor: "kr",
877
+ korean: "kr",
878
+ zh: "cn",
879
+ zho: "cn",
880
+ chi: "cn",
881
+ chinese: "cn",
882
+ es: "es",
883
+ spa: "es",
884
+ spanish: "es",
885
+ fr: "fr",
886
+ fra: "fr",
887
+ french: "fr",
888
+ de: "de",
889
+ deu: "de",
890
+ ger: "de",
891
+ german: "de",
892
+ pt: "br",
893
+ por: "br",
894
+ portuguese: "br",
895
+ ru: "ru",
896
+ rus: "ru",
897
+ russian: "ru",
898
+ ar: "sa",
899
+ ara: "sa",
900
+ arabic: "sa",
901
+ hi: "in",
902
+ hin: "in",
903
+ hindi: "in",
904
+ th: "th",
905
+ tha: "th",
906
+ thai: "th",
907
+ vi: "vn",
908
+ vie: "vn",
909
+ vietnamese: "vn",
910
+ it: "it",
911
+ ita: "it",
912
+ italian: "it",
913
+ tr: "tr",
914
+ tur: "tr",
915
+ turkish: "tr",
916
+ pl: "pl",
917
+ pol: "pl",
918
+ polish: "pl",
919
+ nl: "nl",
920
+ nld: "nl",
921
+ dutch: "nl",
922
+ ms: "my",
923
+ msa: "my",
924
+ malay: "my"
925
+ };
926
+ function getFlagCode(label) {
927
+ const lower = label.toLowerCase().replace(/\s\d+$/, "").trim();
928
+ if (LANG_FLAGS[lower]) return LANG_FLAGS[lower];
929
+ for (const [key, code] of Object.entries(LANG_FLAGS)) {
930
+ if (lower.startsWith(key) || lower.includes(key)) return code;
931
+ }
932
+ return null;
933
+ }
934
+ function DefaultCaptionMenu({ slots }) {
935
+ const { icons: Icons } = useDefaultLayoutContext(), label = useDefaultLayoutWord("Captions"), offText = useDefaultLayoutWord("Off"), media = useMediaContext(), hasCaptions = useMediaState("hasCaptions"), textTracks = useMediaState("textTracks"), activeTrack = useMediaState("textTrack");
936
+ if (!hasCaptions) return null;
937
+ const tracks = textTracks.filter(isTrackCaptionKind);
938
+ const grouped = /* @__PURE__ */ new Map();
939
+ for (const track of tracks) {
940
+ const normalizedLabel = track.label.replace(/\s\d+$/, "").trim();
941
+ if (!grouped.has(normalizedLabel)) {
942
+ grouped.set(normalizedLabel, []);
943
+ }
944
+ grouped.get(normalizedLabel).push(track);
945
+ }
946
+ const priorities = ["indonesian", "english", "japanese", "korean", "chinese"];
947
+ const getPriority = (lbl) => {
948
+ const lower = lbl.toLowerCase();
949
+ const pIndex = priorities.findIndex((p) => lower.includes(p));
950
+ return pIndex !== -1 ? pIndex : 999;
951
+ };
952
+ const sortedGroups = Array.from(grouped.entries()).sort((a, b) => {
953
+ const pa = getPriority(a[0]);
954
+ const pb = getPriority(b[0]);
955
+ if (pa !== pb) return pa - pb;
956
+ return a[0].localeCompare(b[0]);
957
+ });
958
+ const isOff = tracks.every((t) => t.mode !== "showing");
959
+ const onOffSelect = (e) => {
960
+ const selected = media.textTracks.selected;
961
+ if (selected) {
962
+ const idx = media.textTracks.indexOf(selected);
963
+ media.remote.changeTextTrackMode(idx, "disabled", e);
964
+ }
965
+ };
966
+ return /* @__PURE__ */ React.createElement(Root$3, { className: "vds-captions-menu vds-menu" }, /* @__PURE__ */ React.createElement(
967
+ DefaultMenuButton,
968
+ {
969
+ label,
970
+ hint: activeTrack?.label ?? offText,
971
+ disabled: false,
972
+ Icon: Icons.Menu.Captions
973
+ }
974
+ ), /* @__PURE__ */ React.createElement(Items, { className: "vds-menu-items" }, slot(slots, "captionsMenuItemsStart", null), /* @__PURE__ */ React.createElement(
975
+ Root$1,
976
+ {
977
+ className: "vds-captions-radio-group vds-radio-group",
978
+ value: isOff ? "off" : "none"
979
+ },
980
+ /* @__PURE__ */ React.createElement(
981
+ Item,
982
+ {
983
+ className: "vds-caption-radio vds-radio",
984
+ value: "off",
985
+ onSelect: onOffSelect
986
+ },
987
+ /* @__PURE__ */ React.createElement(Icons.Menu.RadioCheck, { className: "vds-icon" }),
988
+ /* @__PURE__ */ React.createElement("span", { className: "vds-radio-label" }, offText)
989
+ )
990
+ ), sortedGroups.map(([groupLabel, groupTracks]) => {
991
+ const flagCode = getFlagCode(groupLabel);
992
+ const activeIndex = groupTracks.findIndex((t) => t === activeTrack);
993
+ return /* @__PURE__ */ React.createElement(Root$3, { key: groupLabel, className: "vds-captions-group-menu vds-menu" }, /* @__PURE__ */ React.createElement(Button, { className: "vds-menu-item" }, /* @__PURE__ */ React.createElement(Icons.Menu.ArrowLeft, { className: "vds-menu-close-icon vds-icon" }), /* @__PURE__ */ React.createElement("span", { className: "vds-menu-item-label", style: { display: "flex", alignItems: "center", gap: "8px" } }, flagCode ? /* @__PURE__ */ React.createElement(
994
+ "span",
995
+ {
996
+ className: `fi fi-${flagCode}`,
997
+ style: {
998
+ fontSize: "1.25em",
999
+ borderRadius: "2px",
1000
+ width: "1.33em",
1001
+ height: "1em",
1002
+ display: "inline-block",
1003
+ backgroundImage: `url('https://cdnjs.cloudflare.com/ajax/libs/flag-icons/7.1.0/flags/4x3/${flagCode}.svg')`,
1004
+ backgroundSize: "cover",
1005
+ backgroundPosition: "center"
1006
+ }
1007
+ }
1008
+ ) : null, /* @__PURE__ */ React.createElement("span", null, groupLabel)), /* @__PURE__ */ React.createElement("span", { className: "vds-menu-item-hint" }, activeIndex !== -1 ? `${activeIndex + 1}/${groupTracks.length}` : ""), /* @__PURE__ */ React.createElement(Icons.Menu.ArrowRight, { className: "vds-menu-open-icon vds-icon" })), /* @__PURE__ */ React.createElement(Items, { className: "vds-menu-items" }, /* @__PURE__ */ React.createElement(
1009
+ Root$1,
1010
+ {
1011
+ className: "vds-captions-radio-group vds-radio-group",
1012
+ value: String(activeIndex)
1013
+ },
1014
+ groupTracks.map((track, idx) => {
1015
+ const displayLabel = groupTracks.length > 1 ? `${groupLabel} ${idx + 1}` : groupLabel;
1016
+ const onSelect = (e) => {
1017
+ const globalIdx = media.textTracks.indexOf(track);
1018
+ media.remote.changeTextTrackMode(globalIdx, "showing", e);
1019
+ };
1020
+ return /* @__PURE__ */ React.createElement(
1021
+ Item,
1022
+ {
1023
+ key: idx,
1024
+ className: "vds-caption-radio vds-radio",
1025
+ value: String(idx),
1026
+ onSelect
1027
+ },
1028
+ /* @__PURE__ */ React.createElement(Icons.Menu.RadioCheck, { className: "vds-icon" }),
1029
+ /* @__PURE__ */ React.createElement("span", { className: "vds-radio-label" }, displayLabel)
1030
+ );
1031
+ })
1032
+ )));
1033
+ }), slot(slots, "captionsMenuItemsEnd", null)));
1034
+ }
1035
+ DefaultCaptionMenu.displayName = "DefaultCaptionMenu";
1036
+
1037
+ function DefaultPlaybackMenu({ slots }) {
1038
+ const label = useDefaultLayoutWord("Playback"), { icons: Icons } = useDefaultLayoutContext();
1039
+ 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)));
1040
+ }
1041
+ DefaultPlaybackMenu.displayName = "DefaultPlaybackMenu";
1042
+ function DefaultLoopMenuCheckbox() {
1043
+ const { remote } = useMediaContext(), label = useDefaultLayoutWord("Loop");
1044
+ function onChange(checked, trigger) {
1045
+ remote.userPrefersLoopChange(checked, trigger);
1046
+ }
1047
+ return /* @__PURE__ */ React.createElement(DefaultMenuItem, { label }, /* @__PURE__ */ React.createElement(DefaultMenuCheckbox, { label, storageKey: "vds-player::user-loop", onChange }));
1048
+ }
1049
+ DefaultLoopMenuCheckbox.displayName = "DefaultLoopMenuCheckbox";
1050
+ function DefaultAutoQualityMenuCheckbox() {
1051
+ const { remote, qualities } = useMediaContext(), $autoQuality = useMediaState("autoQuality"), label = useDefaultLayoutWord("Auto");
1052
+ function onChange(checked, trigger) {
1053
+ if (checked) {
1054
+ remote.requestAutoQuality(trigger);
1055
+ } else {
1056
+ remote.changeQuality(qualities.selectedIndex, trigger);
1057
+ }
1058
+ }
1059
+ return /* @__PURE__ */ React.createElement(DefaultMenuItem, { label }, /* @__PURE__ */ React.createElement(
1060
+ DefaultMenuCheckbox,
1061
+ {
1062
+ label,
1063
+ checked: $autoQuality,
1064
+ onChange,
1065
+ defaultChecked: $autoQuality
1066
+ }
1067
+ ));
1068
+ }
1069
+ DefaultAutoQualityMenuCheckbox.displayName = "DefaultAutoQualityMenuCheckbox";
1070
+ function DefaultQualityMenuSection() {
1071
+ 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]);
1072
+ if (!$canSetQuality || $qualities.length <= 1) return null;
1073
+ 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;
1074
+ return /* @__PURE__ */ React.createElement(DefaultMenuSection, { label, value }, /* @__PURE__ */ React.createElement(
1075
+ DefaultMenuSliderItem,
1076
+ {
1077
+ UpIcon: Icons.Menu.QualityUp,
1078
+ DownIcon: Icons.Menu.QualityDown,
1079
+ isMin,
1080
+ isMax
1081
+ },
1082
+ /* @__PURE__ */ React.createElement(DefaultQualitySlider, null)
1083
+ ), /* @__PURE__ */ React.createElement(DefaultAutoQualityMenuCheckbox, null));
1084
+ }
1085
+ DefaultQualityMenuSection.displayName = "DefaultQualityMenuSection";
1086
+ function DefaultQualitySlider() {
1087
+ const label = useDefaultLayoutWord("Quality");
1088
+ 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));
1089
+ }
1090
+ DefaultQualitySlider.displayName = "DefaultQualitySlider";
1091
+ function DefaultSpeedMenuSection() {
1092
+ 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";
1093
+ if (!$canSetPlaybackRate) return null;
1094
+ return /* @__PURE__ */ React.createElement(DefaultMenuSection, { label, value }, /* @__PURE__ */ React.createElement(
1095
+ DefaultMenuSliderItem,
1096
+ {
1097
+ UpIcon: Icons.Menu.SpeedUp,
1098
+ DownIcon: Icons.Menu.SpeedDown,
1099
+ isMin: $playbackRate === min,
1100
+ isMax: $playbackRate === max
1101
+ },
1102
+ /* @__PURE__ */ React.createElement(DefaultSpeedSlider, null)
1103
+ ));
1104
+ }
1105
+ function useSpeedMin() {
1106
+ const { playbackRates } = useDefaultLayoutContext(), rates = playbackRates;
1107
+ return (isArray(rates) ? rates[0] : rates?.min) ?? 0;
1108
+ }
1109
+ function useSpeedMax() {
1110
+ const { playbackRates } = useDefaultLayoutContext(), rates = playbackRates;
1111
+ return (isArray(rates) ? rates[rates.length - 1] : rates?.max) ?? 2;
1112
+ }
1113
+ function useSpeedStep() {
1114
+ const { playbackRates } = useDefaultLayoutContext(), rates = playbackRates;
1115
+ return (isArray(rates) ? rates[1] - rates[0] : rates?.step) || 0.25;
1116
+ }
1117
+ function DefaultSpeedSlider() {
1118
+ const label = useDefaultLayoutWord("Speed"), min = useSpeedMin(), max = useSpeedMax(), step = useSpeedStep();
1119
+ return /* @__PURE__ */ React.createElement(
1120
+ Root$6,
1121
+ {
1122
+ className: "vds-speed-slider vds-slider",
1123
+ "aria-label": label,
1124
+ min,
1125
+ max,
1126
+ step,
1127
+ keyStep: step
1128
+ },
1129
+ /* @__PURE__ */ React.createElement(DefaultSliderParts, null),
1130
+ /* @__PURE__ */ React.createElement(DefaultSliderSteps, null)
1131
+ );
1132
+ }
1133
+ DefaultSpeedSlider.displayName = "DefaultSpeedSlider";
1134
+
1135
+ function DefaultSettingsMenu({
1136
+ tooltip,
1137
+ placement,
1138
+ portalClass = "",
1139
+ slots
1140
+ }) {
1141
+ const {
1142
+ showMenuDelay,
1143
+ icons: Icons,
1144
+ isSmallLayout,
1145
+ menuContainer,
1146
+ menuGroup,
1147
+ noModal,
1148
+ colorScheme
1149
+ } = 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();
1150
+ useScoped(updateFontCssVars);
1151
+ function onOpen() {
1152
+ flushSync(() => {
1153
+ setIsOpen(true);
1154
+ });
1155
+ }
1156
+ function onClose() {
1157
+ setIsOpen(false);
1158
+ }
1159
+ const Content = /* @__PURE__ */ React.createElement(
1160
+ Items,
1161
+ {
1162
+ className: "vds-settings-menu-items vds-menu-items",
1163
+ placement,
1164
+ offset: $offset
1165
+ },
1166
+ 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
1167
+ );
1168
+ return /* @__PURE__ */ React.createElement(
1169
+ Root$3,
1170
+ {
1171
+ className: "vds-settings-menu vds-menu",
1172
+ showDelay: showMenuDelay,
1173
+ onOpen,
1174
+ onClose
1175
+ },
1176
+ /* @__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" }))),
1177
+ noModal || !isSmallLayout ? Content : /* @__PURE__ */ React.createElement(
1178
+ Portal,
1179
+ {
1180
+ className: portalClass + (colorSchemeClass ? ` ${colorSchemeClass}` : ""),
1181
+ container: menuContainer ?? dialogEl,
1182
+ disabled: "fullscreen",
1183
+ "data-sm": isSmallLayout ? "" : null,
1184
+ "data-lg": !isSmallLayout ? "" : null,
1185
+ "data-size": isSmallLayout ? "sm" : "lg",
1186
+ "data-view-type": $viewType
1187
+ },
1188
+ Content
1189
+ )
1190
+ );
1191
+ }
1192
+ DefaultSettingsMenu.displayName = "DefaultSettingsMenu";
1193
+
1194
+ function DefaultVolumePopup({ tooltip, orientation, slots }) {
1195
+ 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 }));
1196
+ if (!$canSetVolume) {
1197
+ return muteButton;
1198
+ }
1199
+ 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 }))));
1200
+ }
1201
+ DefaultVolumePopup.displayName = "DefaultVolumePopup";
1202
+ function DefaultVolumeSlider(props) {
1203
+ const label = useDefaultLayoutWord("Volume");
1204
+ 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" })));
1205
+ }
1206
+ DefaultVolumeSlider.displayName = "DefaultVolumeSlider";
1207
+ function DefaultTimeSlider() {
1208
+ 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);
1209
+ const onResize = React.useCallback(() => {
1210
+ const el = instance?.el;
1211
+ el && setWidth(el.clientWidth);
1212
+ }, [instance]);
1213
+ useResizeObserver(instance?.el, onResize);
1214
+ return /* @__PURE__ */ React.createElement(
1215
+ Root$9,
1216
+ {
1217
+ className: "vds-time-slider vds-slider",
1218
+ "aria-label": label,
1219
+ disabled: disableTimeSlider,
1220
+ noSwipeGesture: noScrubGesture,
1221
+ keyStep: seekStep,
1222
+ ref: setInstance
1223
+ },
1224
+ /* @__PURE__ */ React.createElement(
1225
+ Chapters,
1226
+ {
1227
+ className: "vds-slider-chapters",
1228
+ disabled: width < sliderChaptersMinWidth
1229
+ },
1230
+ (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" })))
1231
+ ),
1232
+ /* @__PURE__ */ React.createElement(Thumb, { className: "vds-slider-thumb" }),
1233
+ /* @__PURE__ */ React.createElement(Preview, { className: "vds-slider-preview" }, thumbnails ? /* @__PURE__ */ React.createElement(
1234
+ Thumbnail.Root,
1235
+ {
1236
+ src: thumbnails,
1237
+ className: "vds-slider-thumbnail vds-thumbnail"
1238
+ },
1239
+ /* @__PURE__ */ React.createElement(Thumbnail.Img, null)
1240
+ ) : $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" }))
1241
+ );
1242
+ }
1243
+ DefaultTimeSlider.displayName = "DefaultTimeSlider";
1244
+
1245
+ function DefaultTimeGroup({ slots }) {
1246
+ const $duration = useMediaState("duration");
1247
+ if (!$duration) return null;
1248
+ 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" })));
1249
+ }
1250
+ DefaultTimeGroup.displayName = "DefaultTimeGroup";
1251
+ function DefaultTimeInfo({ slots }) {
1252
+ const $live = useMediaState("live");
1253
+ return $live ? slot(slots, "liveButton", /* @__PURE__ */ React.createElement(DefaultLiveButton, null)) : /* @__PURE__ */ React.createElement(DefaultTimeGroup, { slots });
1254
+ }
1255
+ DefaultTimeInfo.displayName = "DefaultTimeInfo";
1256
+ function DefaultTimeInvert({ slots }) {
1257
+ const $live = useMediaState("live"), $duration = useMediaState("duration");
1258
+ return $live ? slot(slots, "liveButton", /* @__PURE__ */ React.createElement(DefaultLiveButton, null)) : slot(
1259
+ slots,
1260
+ "endTime",
1261
+ $duration ? /* @__PURE__ */ React.createElement(Time, { className: "vds-time", type: "current", toggle: true, remainder: true }) : null
1262
+ );
1263
+ }
1264
+ DefaultTimeInvert.displayName = "DefaultTimeInvert";
1265
+
1266
+ const MediaLayout$1 = createDefaultMediaLayout({
1267
+ type: "audio",
1268
+ smLayoutWhen({ width }) {
1269
+ return width < 576;
1270
+ },
1271
+ renderLayout: () => /* @__PURE__ */ React.createElement(AudioLayout, null)
1272
+ });
1273
+ function DefaultAudioLayout(props) {
1274
+ const [scrubbing, setScrubbing] = React.useState(false), $pointer = useMediaState("pointer");
1275
+ const onStartScrubbing = React.useCallback((event) => {
1276
+ const { target } = event, hasTimeSlider = !!(target instanceof HTMLElement && target.closest(".vds-time-slider"));
1277
+ if (!hasTimeSlider) return;
1278
+ event.nativeEvent.stopImmediatePropagation();
1279
+ setScrubbing(true);
1280
+ }, []);
1281
+ const onStopScrubbing = React.useCallback(() => {
1282
+ setScrubbing(false);
1283
+ }, []);
1284
+ React.useEffect(() => {
1285
+ if (scrubbing) return listenEvent(window, "pointerdown", onStopScrubbing);
1286
+ }, [scrubbing, onStopScrubbing]);
1287
+ return /* @__PURE__ */ React.createElement(
1288
+ MediaLayout$1,
1289
+ {
1290
+ ...props,
1291
+ "data-scrubbing": scrubbing ? "" : null,
1292
+ onPointerDown: scrubbing ? (e) => e.stopPropagation() : void 0,
1293
+ onPointerDownCapture: $pointer === "coarse" && !scrubbing ? onStartScrubbing : void 0
1294
+ }
1295
+ );
1296
+ }
1297
+ DefaultAudioLayout.displayName = "DefaultAudioLayout";
1298
+ function AudioLayout() {
1299
+ const slots = useDefaultAudioLayoutSlots();
1300
+ useLayoutName("audio");
1301
+ 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 }))));
1302
+ }
1303
+ AudioLayout.displayName = "AudioLayout";
1304
+ function DefaultAudioMenus({ slots }) {
1305
+ const { isSmallLayout, noModal } = useDefaultLayoutContext(), placement = noModal ? "top end" : !isSmallLayout ? "top end" : null;
1306
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, slot(
1307
+ slots,
1308
+ "chaptersMenu",
1309
+ /* @__PURE__ */ React.createElement(DefaultChaptersMenu, { tooltip: "top", placement, portalClass: "vds-audio-layout" })
1310
+ ), slot(
1311
+ slots,
1312
+ "settingsMenu",
1313
+ /* @__PURE__ */ React.createElement(
1314
+ DefaultSettingsMenu,
1315
+ {
1316
+ tooltip: "top end",
1317
+ placement,
1318
+ portalClass: "vds-audio-layout",
1319
+ slots
1320
+ }
1321
+ )
1322
+ ));
1323
+ }
1324
+ DefaultAudioMenus.displayName = "DefaultAudioMenus";
1325
+ function DefaultAudioTitle() {
1326
+ const [rootEl, setRootEl] = React.useState(null), media = useMediaContext(), { translations } = useDefaultLayoutContext(), [isTextOverflowing, setIsTextOverflowing] = React.useState(false);
1327
+ const isContinued = createComputed(() => {
1328
+ const { started, currentTime } = media.$state;
1329
+ return started() || currentTime() > 0;
1330
+ });
1331
+ const $title = useSignal(
1332
+ createComputed(() => {
1333
+ const { title, ended } = media.$state;
1334
+ if (!title()) return "";
1335
+ const word = ended() ? "Replay" : isContinued() ? "Continue" : "Play";
1336
+ return `${i18n(translations, word)}: ${title()}`;
1337
+ })
1338
+ );
1339
+ const chapterTitle = useChapterTitle(), $isContinued = useSignal(isContinued), $chapterTitle = $isContinued ? chapterTitle : "", isTransitionActive = useTransitionActive(rootEl);
1340
+ React.useEffect(() => {
1341
+ if (isTransitionActive && document.activeElement === document.body) {
1342
+ media.player.el?.focus({ preventScroll: true });
1343
+ }
1344
+ }, []);
1345
+ const onResize = React.useCallback(() => {
1346
+ const el = rootEl, isOverflowing = !!el && !isTransitionActive && el.clientWidth < el.children[0].clientWidth;
1347
+ if (el) toggleClass(el, "vds-marquee", isOverflowing);
1348
+ setIsTextOverflowing(isOverflowing);
1349
+ }, [rootEl, isTransitionActive]);
1350
+ useResizeObserver(rootEl, onResize);
1351
+ 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);
1352
+ }
1353
+ DefaultAudioTitle.displayName = "DefaultAudioTitle";
1354
+ function AudioTitle({ title, chapterTitle }) {
1355
+ const slots = useDefaultAudioLayoutSlots();
1356
+ 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)));
1357
+ }
1358
+ AudioTitle.displayName = "AudioTitle";
1359
+
1360
+ const DefaultKeyboardDisplay = React.forwardRef(
1361
+ ({ icons: Icons, ...props }, forwardRef) => {
1362
+ const [visible, setVisible] = React.useState(false), [Icon, setIcon] = React.useState(null), [count, setCount] = React.useState(0), $lastKeyboardAction = useMediaState("lastKeyboardAction");
1363
+ React.useEffect(() => {
1364
+ setCount((n) => n + 1);
1365
+ }, [$lastKeyboardAction]);
1366
+ const actionDataAttr = React.useMemo(() => {
1367
+ const action = $lastKeyboardAction?.action;
1368
+ return action && visible ? camelToKebabCase(action) : null;
1369
+ }, [visible, $lastKeyboardAction]);
1370
+ const className = React.useMemo(
1371
+ () => `vds-kb-action${!visible ? " hidden" : ""}${props.className ? ` ${props.className}` : ""}`,
1372
+ [visible]
1373
+ );
1374
+ const $$text = createComputed(getText), $text = useSignal($$text);
1375
+ createEffect(() => {
1376
+ const Icon2 = getIcon(Icons);
1377
+ setIcon(() => Icon2);
1378
+ }, [Icons]);
1379
+ React.useEffect(() => {
1380
+ setVisible(!!$lastKeyboardAction);
1381
+ const id = setTimeout(() => setVisible(false), 500);
1382
+ return () => {
1383
+ setVisible(false);
1384
+ window.clearTimeout(id);
1385
+ };
1386
+ }, [$lastKeyboardAction]);
1387
+ return Icon ? /* @__PURE__ */ React.createElement(
1388
+ Primitive.div,
1389
+ {
1390
+ ...props,
1391
+ className,
1392
+ "data-action": actionDataAttr,
1393
+ ref: forwardRef
1394
+ },
1395
+ /* @__PURE__ */ React.createElement("div", { className: "vds-kb-text-wrapper" }, /* @__PURE__ */ React.createElement("div", { className: "vds-kb-text" }, $text)),
1396
+ /* @__PURE__ */ React.createElement("div", { className: "vds-kb-bezel", key: count }, /* @__PURE__ */ React.createElement("div", { className: "vds-kb-icon" }, /* @__PURE__ */ React.createElement(Icon, null)))
1397
+ ) : null;
1398
+ }
1399
+ );
1400
+ DefaultKeyboardDisplay.displayName = "DefaultKeyboardDisplay";
1401
+ function getText() {
1402
+ const { $state } = useContext(mediaContext), action = $state.lastKeyboardAction()?.action, audioGain = $state.audioGain() ?? 1;
1403
+ switch (action) {
1404
+ case "toggleMuted":
1405
+ return $state.muted() ? "0%" : getVolumeText($state.volume(), audioGain);
1406
+ case "volumeUp":
1407
+ case "volumeDown":
1408
+ return getVolumeText($state.volume(), audioGain);
1409
+ default:
1410
+ return "";
1411
+ }
1412
+ }
1413
+ function getVolumeText(volume, gain) {
1414
+ return `${Math.round(volume * gain * 100)}%`;
1415
+ }
1416
+ function getIcon(Icons) {
1417
+ const { $state } = useContext(mediaContext), action = $state.lastKeyboardAction()?.action;
1418
+ switch (action) {
1419
+ case "togglePaused":
1420
+ return !$state.paused() ? Icons.Play : Icons.Pause;
1421
+ case "toggleMuted":
1422
+ return $state.muted() || $state.volume() === 0 ? Icons.Mute : $state.volume() >= 0.5 ? Icons.VolumeUp : Icons.VolumeDown;
1423
+ case "toggleFullscreen":
1424
+ return $state.fullscreen() ? Icons.EnterFullscreen : Icons.ExitFullscreen;
1425
+ case "togglePictureInPicture":
1426
+ return $state.pictureInPicture() ? Icons.EnterPiP : Icons.ExitPiP;
1427
+ case "toggleCaptions":
1428
+ return $state.hasCaptions() ? $state.textTrack() ? Icons.CaptionsOn : Icons.CaptionsOff : null;
1429
+ case "volumeUp":
1430
+ return Icons.VolumeUp;
1431
+ case "volumeDown":
1432
+ return Icons.VolumeDown;
1433
+ case "seekForward":
1434
+ return Icons.SeekForward;
1435
+ case "seekBackward":
1436
+ return Icons.SeekBackward;
1437
+ default:
1438
+ return null;
1439
+ }
1440
+ }
1441
+
1442
+ function DefaultTitle() {
1443
+ const $started = useMediaState("started"), $title = useMediaState("title"), $hasChapters = useActiveTextTrack("chapters");
1444
+ return $hasChapters && ($started || !$title) ? /* @__PURE__ */ React.createElement(ChapterTitle$1, { className: "vds-chapter-title" }) : /* @__PURE__ */ React.createElement(Title, { className: "vds-chapter-title" });
1445
+ }
1446
+ DefaultTitle.displayName = "DefaultTitle";
1447
+
1448
+ const MediaLayout = createDefaultMediaLayout({
1449
+ type: "video",
1450
+ smLayoutWhen({ width, height }) {
1451
+ return width < 576 || height < 380;
1452
+ },
1453
+ renderLayout(props) {
1454
+ return /* @__PURE__ */ React.createElement(VideoLayout, { ...props });
1455
+ }
1456
+ });
1457
+ function DefaultVideoLayout(props) {
1458
+ return /* @__PURE__ */ React.createElement(MediaLayout, { ...props });
1459
+ }
1460
+ DefaultVideoLayout.displayName = "DefaultVideoLayout";
1461
+ function VideoLayout({ streamType, isLoadLayout, isSmallLayout }) {
1462
+ useLayoutName("video");
1463
+ 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);
1464
+ }
1465
+ VideoLayout.displayName = "VideoLayout";
1466
+ function DefaultVideoLargeLayout() {
1467
+ const { menuGroup, episodes, episodesTitle, isSmallLayout } = useDefaultLayoutContext(), baseSlots = useDefaultVideoLayoutSlots(), slots = { ...baseSlots, ...baseSlots?.largeLayout }, $fullscreen = useMediaState("fullscreen"), [episodesOpen, setEpisodesOpen] = React.useState(false), list = episodes ?? [];
1468
+ React.useEffect(() => {
1469
+ if (!$fullscreen) setEpisodesOpen(false);
1470
+ }, [$fullscreen]);
1471
+ const canOpenEpisodes = $fullscreen && list.length > 0;
1472
+ 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(
1473
+ DefaultEpisodesSidebar,
1474
+ {
1475
+ open: episodesOpen,
1476
+ onClose: () => setEpisodesOpen(false),
1477
+ episodes: list,
1478
+ episodesTitle: episodesTitle ?? "Episodes"
1479
+ }
1480
+ ));
1481
+ }
1482
+ DefaultVideoLargeLayout.displayName = "DefaultVideoLargeLayout";
1483
+ function DefaultVideoSmallLayout() {
1484
+ const { episodes, episodesTitle, isSmallLayout } = useDefaultLayoutContext(), baseSlots = useDefaultVideoLayoutSlots(), slots = { ...baseSlots, ...baseSlots?.smallLayout }, $fullscreen = useMediaState("fullscreen"), [episodesOpen, setEpisodesOpen] = React.useState(false), list = episodes ?? [];
1485
+ React.useEffect(() => {
1486
+ if (!$fullscreen) setEpisodesOpen(false);
1487
+ }, [$fullscreen]);
1488
+ const canOpenEpisodes = $fullscreen && list.length > 0;
1489
+ 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(
1490
+ DefaultEpisodesSidebar,
1491
+ {
1492
+ open: episodesOpen,
1493
+ onClose: () => setEpisodesOpen(false),
1494
+ episodes: list,
1495
+ episodesTitle: episodesTitle ?? "Episodes"
1496
+ }
1497
+ ));
1498
+ }
1499
+ DefaultVideoSmallLayout.displayName = "DefaultVideoSmallLayout";
1500
+ function DefaultVideoStartDuration() {
1501
+ const $duration = useMediaState("duration");
1502
+ if ($duration === 0) return null;
1503
+ return /* @__PURE__ */ React.createElement("div", { className: "vds-start-duration" }, /* @__PURE__ */ React.createElement(Time, { className: "vds-time", type: "duration" }));
1504
+ }
1505
+ DefaultVideoStartDuration.displayName = "DefaultVideoStartDuration";
1506
+ function DefaultVideoGestures() {
1507
+ const { noGestures } = useDefaultLayoutContext();
1508
+ if (noGestures) return null;
1509
+ 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" }));
1510
+ }
1511
+ DefaultVideoGestures.displayName = "DefaultVideoGestures";
1512
+ function DefaultBufferingIndicator() {
1513
+ 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" })));
1514
+ }
1515
+ DefaultBufferingIndicator.displayName = "DefaultBufferingIndicator";
1516
+ function DefaultVideoMenus({ slots }) {
1517
+ const { isSmallLayout, noModal, menuGroup } = useDefaultLayoutContext(), side = menuGroup === "top" || isSmallLayout ? "bottom" : "top", tooltip = `${side} end`, placement = noModal ? `${side} end` : !isSmallLayout ? `${side} end` : null;
1518
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, slot(
1519
+ slots,
1520
+ "chaptersMenu",
1521
+ /* @__PURE__ */ React.createElement(
1522
+ DefaultChaptersMenu,
1523
+ {
1524
+ tooltip,
1525
+ placement,
1526
+ portalClass: "vds-video-layout"
1527
+ }
1528
+ )
1529
+ ), slot(
1530
+ slots,
1531
+ "settingsMenu",
1532
+ /* @__PURE__ */ React.createElement(
1533
+ DefaultSettingsMenu,
1534
+ {
1535
+ tooltip,
1536
+ placement,
1537
+ portalClass: "vds-video-layout",
1538
+ slots
1539
+ }
1540
+ )
1541
+ ));
1542
+ }
1543
+ DefaultVideoMenus.displayName = "DefaultVideoMenus";
1544
+ function formatMinutesLeftLabel(secondsLeft) {
1545
+ if (secondsLeft <= 0) return null;
1546
+ if (secondsLeft < 60) return "<1m left";
1547
+ const m = Math.floor(secondsLeft / 60);
1548
+ return `${m}m left`;
1549
+ }
1550
+ function DefaultEpisodesSidebar({
1551
+ open,
1552
+ onClose,
1553
+ episodes,
1554
+ episodesTitle
1555
+ }) {
1556
+ const $fullscreen = useMediaState("fullscreen"), currentTime = useMediaState("currentTime"), duration = useMediaState("duration"), listRef = React.useRef(null), wasEpisodesOpenRef = React.useRef(false);
1557
+ React.useLayoutEffect(() => {
1558
+ const justOpened = open && !wasEpisodesOpenRef.current;
1559
+ wasEpisodesOpenRef.current = open;
1560
+ if (!justOpened || !$fullscreen) return;
1561
+ const root = listRef.current;
1562
+ if (!root) return;
1563
+ requestAnimationFrame(() => {
1564
+ root.querySelector('[data-active="true"]')?.scrollIntoView({
1565
+ block: "nearest",
1566
+ behavior: "smooth"
1567
+ });
1568
+ });
1569
+ }, [open, $fullscreen]);
1570
+ if (!$fullscreen || episodes.length === 0) return null;
1571
+ return /* @__PURE__ */ React.createElement(
1572
+ "div",
1573
+ {
1574
+ className: "vds-episodes-backdrop",
1575
+ "data-open": open ? "true" : "false",
1576
+ onPointerUp: (event) => {
1577
+ if (event.target === event.currentTarget) onClose();
1578
+ }
1579
+ },
1580
+ /* @__PURE__ */ React.createElement(
1581
+ "aside",
1582
+ {
1583
+ className: "vds-episodes-panel",
1584
+ "data-open": open ? "true" : "false",
1585
+ onKeyDown: (event) => {
1586
+ if (event.key === "Escape") onClose();
1587
+ }
1588
+ },
1589
+ /* @__PURE__ */ React.createElement("header", { className: "vds-episodes-panel-header" }, /* @__PURE__ */ React.createElement("h3", { className: "vds-episodes-panel-title" }, episodesTitle), /* @__PURE__ */ React.createElement(
1590
+ "button",
1591
+ {
1592
+ type: "button",
1593
+ className: "vds-episodes-close-btn",
1594
+ "aria-label": "Close episodes",
1595
+ onPointerUp: (event) => {
1596
+ event.stopPropagation();
1597
+ onClose();
1598
+ }
1599
+ },
1600
+ /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, "\u2715")
1601
+ )),
1602
+ /* @__PURE__ */ React.createElement("div", { ref: listRef, className: "vds-episodes-list", role: "list" }, episodes.map((episode, index) => {
1603
+ const episodeName = episode.episodeTitle || `Episode ${episode.episodeNumber ?? index + 1}`;
1604
+ const seasonEpLabel = episode.seasonNumber != null && episode.episodeNumber != null ? `S${String(episode.seasonNumber).padStart(2, "0")} \xB7 E${String(episode.episodeNumber).padStart(2, "0")}` : episodeName;
1605
+ const isActive = !!episode.isActive;
1606
+ const onEpisodeSelect = (event) => {
1607
+ event.stopPropagation();
1608
+ if (isActive) return;
1609
+ event.currentTarget.dispatchEvent(
1610
+ new CustomEvent("vds-episode-select", {
1611
+ bubbles: true,
1612
+ composed: true,
1613
+ detail: { episode, index }
1614
+ })
1615
+ );
1616
+ onClose();
1617
+ };
1618
+ let runtimeText = null;
1619
+ if (isActive && duration > 0) {
1620
+ const secsLeft = Math.max(0, duration - currentTime);
1621
+ runtimeText = formatMinutesLeftLabel(secsLeft);
1622
+ } else if (episode.timeLeft != null && episode.timeLeft > 0) {
1623
+ runtimeText = `${episode.timeLeft}m left`;
1624
+ } else if (Number.isFinite(episode.runtime) && (episode.runtime ?? 0) > 0) {
1625
+ runtimeText = `${episode.runtime}m`;
1626
+ }
1627
+ let progressPct = 0;
1628
+ if (isActive && duration > 0) {
1629
+ progressPct = Math.min(100, currentTime / duration * 100);
1630
+ } else if (episode.progressPercent != null && episode.progressPercent > 0) {
1631
+ progressPct = Math.min(100, episode.progressPercent);
1632
+ }
1633
+ const showSubtitle = !!episode.episodeTitle && episode.episodeTitle.trim() !== "" && episode.episodeTitle !== episode.title;
1634
+ return /* @__PURE__ */ React.createElement(
1635
+ "article",
1636
+ {
1637
+ key: `${episode.episodeNumber ?? index}-${episode.title ?? ""}`,
1638
+ className: "vds-episode-item",
1639
+ "data-active": isActive ? "true" : "false",
1640
+ "aria-current": isActive ? "true" : void 0,
1641
+ role: "button",
1642
+ tabIndex: 0,
1643
+ "aria-label": episode.title || episodeName,
1644
+ onPointerUp: onEpisodeSelect,
1645
+ onKeyDown: (event) => {
1646
+ if (event.key === "Enter" || event.key === " ") onEpisodeSelect(event);
1647
+ }
1648
+ },
1649
+ /* @__PURE__ */ React.createElement("div", { className: "vds-episode-thumb-wrap" }, episode.thumbnail ? /* @__PURE__ */ React.createElement(
1650
+ "img",
1651
+ {
1652
+ className: "vds-episode-thumb",
1653
+ src: episode.thumbnail,
1654
+ alt: episode.title || episodeName,
1655
+ loading: "lazy",
1656
+ decoding: "async"
1657
+ }
1658
+ ) : /* @__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),
1659
+ /* @__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) : /* @__PURE__ */ React.createElement("p", { className: "vds-episode-desc", style: { fontStyle: "italic", opacity: 0.7 } }, "Deskripsi tidak tersedia."))
1660
+ );
1661
+ }))
1662
+ )
1663
+ );
1664
+ }
1665
+ function DefaultVideoLoadLayout() {
1666
+ const { isSmallLayout } = useDefaultLayoutContext(), baseSlots = useDefaultVideoLayoutSlots(), slots = { ...baseSlots, ...baseSlots?.[isSmallLayout ? "smallLayout" : "largeLayout"] };
1667
+ 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" })));
1668
+ }
1669
+ DefaultVideoLoadLayout.displayName = "DefaultVideoLoadLayout";
1670
+ function DefaultVideoKeyboardDisplay() {
1671
+ const { noKeyboardAnimations, icons, userPrefersKeyboardAnimations } = useDefaultLayoutContext(), $userPrefersKeyboardAnimations = useSignal(userPrefersKeyboardAnimations), disabled = noKeyboardAnimations || !$userPrefersKeyboardAnimations;
1672
+ if (disabled || !icons.KeyboardDisplay) return null;
1673
+ return /* @__PURE__ */ React.createElement(DefaultKeyboardDisplay, { icons: icons.KeyboardDisplay });
1674
+ }
1675
+ DefaultVideoKeyboardDisplay.displayName = "DefaultVideoKeyboardDisplay";
1676
+
1677
+ export { DefaultAudioLayout, DefaultBufferingIndicator, DefaultKeyboardDisplay, DefaultLayoutContext, DefaultMenuButton, DefaultMenuCheckbox, DefaultMenuItem, DefaultMenuRadioGroup, DefaultMenuSection, DefaultMenuSliderItem, DefaultSliderParts, DefaultSliderSteps, DefaultTooltip, DefaultVideoGestures, DefaultVideoLargeLayout, DefaultVideoLayout, DefaultVideoSmallLayout, createRadioOptions, i18n, useDefaultLayoutContext, useDefaultLayoutWord };