@kuraykaraaslan/kui-react 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +17 -0
  2. package/README.md +168 -0
  3. package/dist/AdvancedDataTable-F3DNXDKX.mjs +11 -0
  4. package/dist/DataTable-2G27T4E6.mjs +11 -0
  5. package/dist/DateRangePicker-AL32QB6L.mjs +11 -0
  6. package/dist/DropdownMenu-f5yV9dzM.d.mts +22 -0
  7. package/dist/DropdownMenu-f5yV9dzM.d.ts +22 -0
  8. package/dist/MapView-FERKPCDB.mjs +10 -0
  9. package/dist/ServerDataTable-RZV3K6KQ.mjs +11 -0
  10. package/dist/Tooltip-Bof5GvOc.d.mts +248 -0
  11. package/dist/Tooltip-Bof5GvOc.d.ts +248 -0
  12. package/dist/VideoPlayer-P3I6ESXJ.mjs +9 -0
  13. package/dist/app.d.mts +620 -0
  14. package/dist/app.d.ts +620 -0
  15. package/dist/app.js +7061 -0
  16. package/dist/app.mjs +100 -0
  17. package/dist/chunk-24BCQSLI.mjs +1 -0
  18. package/dist/chunk-45I3EDB2.mjs +90 -0
  19. package/dist/chunk-4IWCD7ID.mjs +1450 -0
  20. package/dist/chunk-5E2HXWFI.mjs +105 -0
  21. package/dist/chunk-C7AYI4XM.mjs +402 -0
  22. package/dist/chunk-J4D44TUA.mjs +1267 -0
  23. package/dist/chunk-KTEWZKNE.mjs +1020 -0
  24. package/dist/chunk-LMUQHL4Z.mjs +3829 -0
  25. package/dist/chunk-MD5OQ4J2.mjs +527 -0
  26. package/dist/chunk-MPJRPYIZ.mjs +1 -0
  27. package/dist/chunk-MPWUEQ7J.mjs +2422 -0
  28. package/dist/chunk-MTT5TKAJ.mjs +93 -0
  29. package/dist/chunk-RBDK7MWQ.mjs +46 -0
  30. package/dist/chunk-SVFQZPNZ.mjs +3648 -0
  31. package/dist/chunk-TZWBBMSG.mjs +1 -0
  32. package/dist/chunk-XA7J6PVJ.mjs +1488 -0
  33. package/dist/chunk-ZLYBRYWQ.mjs +726 -0
  34. package/dist/common.d.mts +921 -0
  35. package/dist/common.d.ts +921 -0
  36. package/dist/common.js +4991 -0
  37. package/dist/common.mjs +172 -0
  38. package/dist/index.d.mts +10 -0
  39. package/dist/index.d.ts +10 -0
  40. package/dist/index.js +17563 -0
  41. package/dist/index.mjs +349 -0
  42. package/dist/ui.d.mts +937 -0
  43. package/dist/ui.d.ts +937 -0
  44. package/dist/ui.js +10095 -0
  45. package/dist/ui.mjs +163 -0
  46. package/package.json +114 -0
  47. package/styles/index.css +129 -0
@@ -0,0 +1,1267 @@
1
+ "use client";
2
+ import {
3
+ __objRest,
4
+ __spreadProps,
5
+ __spreadValues,
6
+ cn
7
+ } from "./chunk-RBDK7MWQ.mjs";
8
+
9
+ // modules/ui/VideoPlayer/index.tsx
10
+ import { useCallback as useCallback5, useEffect as useEffect6, useRef as useRef3, useState as useState5 } from "react";
11
+
12
+ // modules/ui/VideoPlayer/format.ts
13
+ function formatTime(seconds) {
14
+ if (!isFinite(seconds) || isNaN(seconds)) return "0:00";
15
+ const h = Math.floor(seconds / 3600);
16
+ const m = Math.floor(seconds % 3600 / 60);
17
+ const s = Math.floor(seconds % 60);
18
+ if (h > 0) return `${h}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
19
+ return `${m}:${String(s).padStart(2, "0")}`;
20
+ }
21
+
22
+ // modules/ui/VideoPlayer/parts/ControlRow.tsx
23
+ import { useState } from "react";
24
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
25
+ import {
26
+ faPlay,
27
+ faPause,
28
+ faVolumeHigh,
29
+ faVolumeLow,
30
+ faVolumeOff,
31
+ faExpand,
32
+ faCompress,
33
+ faRotateLeft,
34
+ faRotateRight,
35
+ faGear
36
+ } from "@fortawesome/free-solid-svg-icons";
37
+ import { faChromecast } from "@fortawesome/free-brands-svg-icons";
38
+
39
+ // modules/ui/VideoPlayer/parts/CtrlBtn.tsx
40
+ import { jsx } from "react/jsx-runtime";
41
+ function CtrlBtn(_a) {
42
+ var _b = _a, {
43
+ onClick,
44
+ children,
45
+ primary,
46
+ active,
47
+ className
48
+ } = _b, rest = __objRest(_b, [
49
+ "onClick",
50
+ "children",
51
+ "primary",
52
+ "active",
53
+ "className"
54
+ ]);
55
+ return /* @__PURE__ */ jsx(
56
+ "button",
57
+ __spreadProps(__spreadValues({
58
+ type: "button",
59
+ onClick,
60
+ className: cn(
61
+ "flex items-center justify-center rounded transition-colors",
62
+ "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-white",
63
+ primary ? "w-9 h-9 text-white hover:text-primary" : "w-8 h-8",
64
+ !primary && active && "text-primary",
65
+ !primary && !active && "text-white/80 hover:text-white",
66
+ className
67
+ )
68
+ }, rest), {
69
+ children
70
+ })
71
+ );
72
+ }
73
+
74
+ // modules/ui/VideoPlayer/parts/ControlRow.tsx
75
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
76
+ function ControlRow({
77
+ playing,
78
+ muted,
79
+ volume,
80
+ currentTime,
81
+ duration,
82
+ isFullscreen,
83
+ showSettings,
84
+ enableCast,
85
+ castState,
86
+ onPlay,
87
+ onSeekBy,
88
+ onToggleMute,
89
+ onVolumeChange,
90
+ onToggleSettings,
91
+ onToggleCast,
92
+ onToggleFullscreen
93
+ }) {
94
+ const [showVolumeSlider, setShowVolumeSlider] = useState(false);
95
+ const volumeIcon = muted || volume === 0 ? faVolumeOff : volume < 0.5 ? faVolumeLow : faVolumeHigh;
96
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
97
+ /* @__PURE__ */ jsx2(CtrlBtn, { onClick: () => onSeekBy(-10), "aria-label": "Rewind 10 seconds", children: /* @__PURE__ */ jsx2(FontAwesomeIcon, { icon: faRotateLeft, className: "text-sm", "aria-hidden": "true" }) }),
98
+ /* @__PURE__ */ jsx2(CtrlBtn, { onClick: onPlay, "aria-label": playing ? "Pause" : "Play", primary: true, children: /* @__PURE__ */ jsx2(FontAwesomeIcon, { icon: playing ? faPause : faPlay, className: "text-base", "aria-hidden": "true" }) }),
99
+ /* @__PURE__ */ jsx2(CtrlBtn, { onClick: () => onSeekBy(10), "aria-label": "Forward 10 seconds", children: /* @__PURE__ */ jsx2(FontAwesomeIcon, { icon: faRotateRight, className: "text-sm", "aria-hidden": "true" }) }),
100
+ /* @__PURE__ */ jsxs(
101
+ "div",
102
+ {
103
+ className: "flex items-center gap-1.5",
104
+ onMouseEnter: () => setShowVolumeSlider(true),
105
+ onMouseLeave: () => setShowVolumeSlider(false),
106
+ children: [
107
+ /* @__PURE__ */ jsx2(CtrlBtn, { onClick: onToggleMute, "aria-label": muted ? "Unmute" : "Mute", children: /* @__PURE__ */ jsx2(FontAwesomeIcon, { icon: volumeIcon, className: "text-sm", "aria-hidden": "true" }) }),
108
+ /* @__PURE__ */ jsx2(
109
+ "div",
110
+ {
111
+ className: cn(
112
+ "overflow-hidden transition-all duration-200 ease-out",
113
+ showVolumeSlider ? "w-20 opacity-100" : "w-0 opacity-0"
114
+ ),
115
+ children: /* @__PURE__ */ jsx2(
116
+ "input",
117
+ {
118
+ type: "range",
119
+ min: 0,
120
+ max: 1,
121
+ step: 0.05,
122
+ value: muted ? 0 : volume,
123
+ onChange: (e) => onVolumeChange(parseFloat(e.target.value)),
124
+ "aria-label": "Volume",
125
+ className: "w-full h-1 cursor-pointer accent-primary"
126
+ }
127
+ )
128
+ }
129
+ )
130
+ ]
131
+ }
132
+ ),
133
+ /* @__PURE__ */ jsxs("span", { className: "text-white/70 text-xs tabular-nums flex-1 pl-1 select-none", children: [
134
+ formatTime(currentTime),
135
+ /* @__PURE__ */ jsx2("span", { className: "text-white/30 mx-0.5", children: "/" }),
136
+ formatTime(duration)
137
+ ] }),
138
+ /* @__PURE__ */ jsx2(
139
+ CtrlBtn,
140
+ {
141
+ onClick: onToggleSettings,
142
+ "aria-label": "Settings",
143
+ "aria-expanded": showSettings,
144
+ active: showSettings,
145
+ children: /* @__PURE__ */ jsx2(
146
+ FontAwesomeIcon,
147
+ {
148
+ icon: faGear,
149
+ className: cn("text-sm transition-transform duration-300", showSettings && "rotate-[30deg]"),
150
+ "aria-hidden": "true"
151
+ }
152
+ )
153
+ }
154
+ ),
155
+ enableCast && castState !== "unavailable" && /* @__PURE__ */ jsx2(
156
+ CtrlBtn,
157
+ {
158
+ onClick: onToggleCast,
159
+ "aria-label": castState === "connected" ? "Stop casting" : "Cast to device",
160
+ "aria-pressed": castState === "connected",
161
+ active: castState === "connected" || castState === "connecting",
162
+ children: /* @__PURE__ */ jsx2(
163
+ FontAwesomeIcon,
164
+ {
165
+ icon: faChromecast,
166
+ className: cn("text-sm", castState === "connecting" && "animate-pulse"),
167
+ "aria-hidden": "true"
168
+ }
169
+ )
170
+ }
171
+ ),
172
+ /* @__PURE__ */ jsx2(
173
+ CtrlBtn,
174
+ {
175
+ onClick: onToggleFullscreen,
176
+ "aria-label": isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
177
+ children: /* @__PURE__ */ jsx2(FontAwesomeIcon, { icon: isFullscreen ? faCompress : faExpand, className: "text-sm", "aria-hidden": "true" })
178
+ }
179
+ )
180
+ ] });
181
+ }
182
+
183
+ // modules/ui/VideoPlayer/parts/ProgressBar.tsx
184
+ import { forwardRef } from "react";
185
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
186
+ var ProgressBar = forwardRef(function ProgressBar2({ progress, buffered, seekHoverX, seekHoverPct, hoverTime, onSeek, onSeekMouseMove, onSeekLeave }, ref) {
187
+ return /* @__PURE__ */ jsxs2(
188
+ "div",
189
+ {
190
+ ref,
191
+ role: "slider",
192
+ "aria-label": "Seek",
193
+ "aria-valuemin": 0,
194
+ "aria-valuemax": 100,
195
+ "aria-valuenow": Math.round(progress),
196
+ tabIndex: 0,
197
+ className: "relative h-1.5 rounded-full bg-white/20 cursor-pointer group/seek hover:h-2 transition-all",
198
+ onClick: onSeek,
199
+ onMouseMove: onSeekMouseMove,
200
+ onMouseLeave: onSeekLeave,
201
+ children: [
202
+ /* @__PURE__ */ jsx3(
203
+ "div",
204
+ {
205
+ className: "absolute inset-y-0 left-0 rounded-full bg-white/25",
206
+ style: { width: `${buffered}%` }
207
+ }
208
+ ),
209
+ /* @__PURE__ */ jsx3(
210
+ "div",
211
+ {
212
+ className: "absolute inset-y-0 left-0 rounded-full bg-primary transition-all",
213
+ style: { width: `${progress}%` }
214
+ }
215
+ ),
216
+ seekHoverPct !== null && /* @__PURE__ */ jsx3(
217
+ "div",
218
+ {
219
+ className: "absolute inset-y-0 left-0 rounded-full bg-white/15",
220
+ style: { width: `${seekHoverPct}%` }
221
+ }
222
+ ),
223
+ /* @__PURE__ */ jsx3(
224
+ "div",
225
+ {
226
+ className: "absolute top-1/2 -translate-y-1/2 w-3.5 h-3.5 rounded-full bg-white shadow-md opacity-0 group-hover/seek:opacity-100 transition-opacity",
227
+ style: { left: `calc(${progress}% - 7px)` }
228
+ }
229
+ ),
230
+ hoverTime && seekHoverX !== null && /* @__PURE__ */ jsx3(
231
+ "div",
232
+ {
233
+ className: "absolute -top-8 -translate-x-1/2 bg-black/80 text-white text-xs px-1.5 py-0.5 rounded pointer-events-none whitespace-nowrap",
234
+ style: { left: seekHoverX },
235
+ children: hoverTime
236
+ }
237
+ )
238
+ ]
239
+ }
240
+ );
241
+ });
242
+
243
+ // modules/ui/VideoPlayer/parts/SettingsPanel.tsx
244
+ import { forwardRef as forwardRef2 } from "react";
245
+ import { FontAwesomeIcon as FontAwesomeIcon5 } from "@fortawesome/react-fontawesome";
246
+ import { faGear as faGear2 } from "@fortawesome/free-solid-svg-icons";
247
+
248
+ // modules/ui/VideoPlayer/parts/SettingsRow.tsx
249
+ import { FontAwesomeIcon as FontAwesomeIcon2 } from "@fortawesome/react-fontawesome";
250
+ import { faChevronRight } from "@fortawesome/free-solid-svg-icons";
251
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
252
+ function SettingsRow({ label, value, onClick }) {
253
+ return /* @__PURE__ */ jsxs3(
254
+ "button",
255
+ {
256
+ type: "button",
257
+ onClick,
258
+ className: "w-full flex items-center justify-between px-4 py-2.5 hover:bg-white/10 transition-colors group",
259
+ children: [
260
+ /* @__PURE__ */ jsx4("span", { className: "text-white/85 text-sm", children: label }),
261
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1.5 text-white/45 text-xs group-hover:text-white/65 transition-colors", children: [
262
+ /* @__PURE__ */ jsx4("span", { children: value }),
263
+ /* @__PURE__ */ jsx4(FontAwesomeIcon2, { icon: faChevronRight, className: "text-[10px]", "aria-hidden": "true" })
264
+ ] })
265
+ ]
266
+ }
267
+ );
268
+ }
269
+
270
+ // modules/ui/VideoPlayer/parts/SettingsSubMenu.tsx
271
+ import { FontAwesomeIcon as FontAwesomeIcon3 } from "@fortawesome/react-fontawesome";
272
+ import { faChevronLeft } from "@fortawesome/free-solid-svg-icons";
273
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
274
+ function SettingsSubMenu({ title, onBack, children }) {
275
+ return /* @__PURE__ */ jsxs4("div", { children: [
276
+ /* @__PURE__ */ jsxs4(
277
+ "button",
278
+ {
279
+ type: "button",
280
+ onClick: onBack,
281
+ className: "w-full flex items-center gap-2.5 px-3 py-2.5 border-b border-white/10 hover:bg-white/5 transition-colors",
282
+ children: [
283
+ /* @__PURE__ */ jsx5(FontAwesomeIcon3, { icon: faChevronLeft, className: "text-white/50 text-xs", "aria-hidden": "true" }),
284
+ /* @__PURE__ */ jsx5("span", { className: "text-white text-sm font-semibold", children: title })
285
+ ]
286
+ }
287
+ ),
288
+ /* @__PURE__ */ jsx5("div", { className: "py-1", children })
289
+ ] });
290
+ }
291
+
292
+ // modules/ui/VideoPlayer/parts/SettingsOption.tsx
293
+ import { FontAwesomeIcon as FontAwesomeIcon4 } from "@fortawesome/react-fontawesome";
294
+ import { faCheck } from "@fortawesome/free-solid-svg-icons";
295
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
296
+ function SettingsOption({ label, sublabel, selected, onClick }) {
297
+ return /* @__PURE__ */ jsxs5(
298
+ "button",
299
+ {
300
+ type: "button",
301
+ onClick,
302
+ className: cn(
303
+ "w-full flex items-center justify-between px-4 py-2 text-sm transition-colors hover:bg-white/10",
304
+ selected ? "text-primary font-semibold" : "text-white/80"
305
+ ),
306
+ children: [
307
+ /* @__PURE__ */ jsxs5("span", { className: "flex flex-col items-start gap-0.5", children: [
308
+ /* @__PURE__ */ jsx6("span", { children: label }),
309
+ sublabel && /* @__PURE__ */ jsx6("span", { className: "text-xs text-white/35 font-normal", children: sublabel })
310
+ ] }),
311
+ selected && /* @__PURE__ */ jsx6(FontAwesomeIcon4, { icon: faCheck, className: "text-primary text-xs shrink-0", "aria-hidden": "true" })
312
+ ]
313
+ }
314
+ );
315
+ }
316
+
317
+ // modules/ui/VideoPlayer/constants.ts
318
+ var SPEEDS = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];
319
+ var SUBTITLE_SIZES = {
320
+ sm: "0.8rem",
321
+ md: "1rem",
322
+ lg: "1.3rem",
323
+ xl: "1.65rem"
324
+ };
325
+ var SUBTITLE_SIZE_LABELS = {
326
+ sm: "K\xFC\xE7\xFCk",
327
+ md: "Orta",
328
+ lg: "B\xFCy\xFCk",
329
+ xl: "\xC7ok B\xFCy\xFCk"
330
+ };
331
+
332
+ // modules/ui/VideoPlayer/parts/SettingsPanel.tsx
333
+ import { Fragment, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
334
+ var SettingsPanel = forwardRef2(function SettingsPanel2({
335
+ view,
336
+ onChangeView,
337
+ qualities,
338
+ subtitles,
339
+ audioTracks,
340
+ selectedQuality,
341
+ selectedSubtitle,
342
+ selectedAudioTrack,
343
+ speed,
344
+ subtitleFontSize,
345
+ applyQuality,
346
+ applySpeed,
347
+ applySubtitle,
348
+ applySubtitleSize,
349
+ applyAudioTrack
350
+ }, ref) {
351
+ var _a, _b, _c, _d, _e, _f;
352
+ const currentQualityLabel = (_b = (_a = qualities == null ? void 0 : qualities.find((q) => q.value === selectedQuality)) == null ? void 0 : _a.label) != null ? _b : "Auto";
353
+ const currentSubtitleLabel = selectedSubtitle !== null ? (_d = (_c = subtitles == null ? void 0 : subtitles[selectedSubtitle]) == null ? void 0 : _c.label) != null ? _d : "Kapal\u0131" : "Kapal\u0131";
354
+ const currentAudioLabel = (_f = (_e = audioTracks == null ? void 0 : audioTracks[selectedAudioTrack]) == null ? void 0 : _e.label) != null ? _f : "";
355
+ return /* @__PURE__ */ jsxs6(
356
+ "div",
357
+ {
358
+ ref,
359
+ className: "absolute bottom-14 right-4 w-60 bg-black/90 backdrop-blur-md rounded-xl border border-white/10 shadow-2xl overflow-hidden z-20",
360
+ children: [
361
+ view === "main" && /* @__PURE__ */ jsxs6(Fragment, { children: [
362
+ /* @__PURE__ */ jsxs6("div", { className: "px-4 py-2.5 border-b border-white/10 flex items-center gap-2", children: [
363
+ /* @__PURE__ */ jsx7(FontAwesomeIcon5, { icon: faGear2, className: "text-white/50 text-xs", "aria-hidden": "true" }),
364
+ /* @__PURE__ */ jsx7("p", { className: "text-white/70 text-xs font-semibold uppercase tracking-wider", children: "Ayarlar" })
365
+ ] }),
366
+ /* @__PURE__ */ jsxs6("div", { className: "py-1", children: [
367
+ qualities && qualities.length > 0 && /* @__PURE__ */ jsx7(
368
+ SettingsRow,
369
+ {
370
+ label: "Kalite",
371
+ value: currentQualityLabel,
372
+ onClick: () => onChangeView("quality")
373
+ }
374
+ ),
375
+ /* @__PURE__ */ jsx7(
376
+ SettingsRow,
377
+ {
378
+ label: "Oynatma H\u0131z\u0131",
379
+ value: speed === 1 ? "Normal" : `${speed}\xD7`,
380
+ onClick: () => onChangeView("speed")
381
+ }
382
+ ),
383
+ subtitles && subtitles.length > 0 && /* @__PURE__ */ jsxs6(Fragment, { children: [
384
+ /* @__PURE__ */ jsx7(
385
+ SettingsRow,
386
+ {
387
+ label: "Altyaz\u0131",
388
+ value: currentSubtitleLabel,
389
+ onClick: () => onChangeView("subtitles")
390
+ }
391
+ ),
392
+ /* @__PURE__ */ jsx7(
393
+ SettingsRow,
394
+ {
395
+ label: "Altyaz\u0131 Boyutu",
396
+ value: SUBTITLE_SIZE_LABELS[subtitleFontSize],
397
+ onClick: () => onChangeView("subtitle-size")
398
+ }
399
+ )
400
+ ] }),
401
+ audioTracks && audioTracks.length > 1 && /* @__PURE__ */ jsx7(
402
+ SettingsRow,
403
+ {
404
+ label: "Ses Dili",
405
+ value: currentAudioLabel,
406
+ onClick: () => onChangeView("language")
407
+ }
408
+ )
409
+ ] })
410
+ ] }),
411
+ view === "quality" && qualities && /* @__PURE__ */ jsx7(SettingsSubMenu, { title: "Kalite", onBack: () => onChangeView("main"), children: qualities.map((q) => /* @__PURE__ */ jsx7(
412
+ SettingsOption,
413
+ {
414
+ label: q.label,
415
+ selected: selectedQuality === q.value,
416
+ onClick: () => applyQuality(q.value)
417
+ },
418
+ q.value
419
+ )) }),
420
+ view === "speed" && /* @__PURE__ */ jsx7(SettingsSubMenu, { title: "Oynatma H\u0131z\u0131", onBack: () => onChangeView("main"), children: SPEEDS.map((s) => /* @__PURE__ */ jsx7(
421
+ SettingsOption,
422
+ {
423
+ label: s === 1 ? "1\xD7 (Normal)" : `${s}\xD7`,
424
+ selected: speed === s,
425
+ onClick: () => applySpeed(s)
426
+ },
427
+ s
428
+ )) }),
429
+ view === "subtitles" && subtitles && /* @__PURE__ */ jsxs6(SettingsSubMenu, { title: "Altyaz\u0131", onBack: () => onChangeView("main"), children: [
430
+ /* @__PURE__ */ jsx7(
431
+ SettingsOption,
432
+ {
433
+ label: "Kapal\u0131",
434
+ selected: selectedSubtitle === null,
435
+ onClick: () => applySubtitle(null)
436
+ }
437
+ ),
438
+ subtitles.map((sub, i) => /* @__PURE__ */ jsx7(
439
+ SettingsOption,
440
+ {
441
+ label: sub.label,
442
+ selected: selectedSubtitle === i,
443
+ onClick: () => applySubtitle(i)
444
+ },
445
+ i
446
+ ))
447
+ ] }),
448
+ view === "subtitle-size" && /* @__PURE__ */ jsx7(SettingsSubMenu, { title: "Altyaz\u0131 Boyutu", onBack: () => onChangeView("main"), children: Object.entries(SUBTITLE_SIZE_LABELS).map(
449
+ ([key, label]) => /* @__PURE__ */ jsx7(
450
+ SettingsOption,
451
+ {
452
+ label,
453
+ sublabel: SUBTITLE_SIZES[key],
454
+ selected: subtitleFontSize === key,
455
+ onClick: () => applySubtitleSize(key)
456
+ },
457
+ key
458
+ )
459
+ ) }),
460
+ view === "language" && audioTracks && /* @__PURE__ */ jsx7(SettingsSubMenu, { title: "Ses Dili", onBack: () => onChangeView("main"), children: audioTracks.map((track, i) => /* @__PURE__ */ jsx7(
461
+ SettingsOption,
462
+ {
463
+ label: track.label,
464
+ sublabel: track.language,
465
+ selected: selectedAudioTrack === i,
466
+ onClick: () => applyAudioTrack(i)
467
+ },
468
+ i
469
+ )) })
470
+ ]
471
+ }
472
+ );
473
+ });
474
+
475
+ // modules/ui/VideoPlayer/parts/Overlays.tsx
476
+ import { FontAwesomeIcon as FontAwesomeIcon6 } from "@fortawesome/react-fontawesome";
477
+ import { faPlay as faPlay2, faSpinner } from "@fortawesome/free-solid-svg-icons";
478
+ import { faChromecast as faChromecast2 } from "@fortawesome/free-brands-svg-icons";
479
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
480
+ function CastOverlay({
481
+ castDeviceName,
482
+ title
483
+ }) {
484
+ return /* @__PURE__ */ jsxs7(
485
+ "div",
486
+ {
487
+ className: "absolute inset-0 flex flex-col items-center justify-center pointer-events-none z-10 gap-3 text-center px-6",
488
+ style: {
489
+ background: "linear-gradient(to bottom, rgba(0,0,0,0.75) 0%, rgba(0,0,0,0.75) 55%, rgba(0,0,0,0) 100%)"
490
+ },
491
+ children: [
492
+ /* @__PURE__ */ jsx8(
493
+ FontAwesomeIcon6,
494
+ {
495
+ icon: faChromecast2,
496
+ className: "text-white text-5xl drop-shadow-lg",
497
+ "aria-hidden": "true"
498
+ }
499
+ ),
500
+ /* @__PURE__ */ jsx8("p", { className: "text-white/90 text-sm font-medium", children: castDeviceName ? `${castDeviceName} cihaz\u0131na yay\u0131nlan\u0131yor` : "Cihaza yay\u0131nlan\u0131yor" }),
501
+ title && /* @__PURE__ */ jsx8("p", { className: "text-white/60 text-xs max-w-[90%] truncate", children: title })
502
+ ]
503
+ }
504
+ );
505
+ }
506
+ function LoadingOverlay() {
507
+ return /* @__PURE__ */ jsx8("div", { className: "absolute inset-0 flex items-center justify-center bg-black/20 pointer-events-none", children: /* @__PURE__ */ jsx8(
508
+ FontAwesomeIcon6,
509
+ {
510
+ icon: faSpinner,
511
+ className: "text-white text-4xl animate-spin drop-shadow-lg",
512
+ "aria-hidden": "true"
513
+ }
514
+ ) });
515
+ }
516
+ function CenterPlayOverlay({ playing }) {
517
+ return /* @__PURE__ */ jsx8(
518
+ "div",
519
+ {
520
+ className: cn(
521
+ "absolute inset-0 flex items-center justify-center pointer-events-none",
522
+ "transition-opacity duration-300 ease-out",
523
+ playing ? "opacity-0" : "opacity-100"
524
+ ),
525
+ "aria-hidden": "true",
526
+ children: /* @__PURE__ */ jsx8(
527
+ "div",
528
+ {
529
+ className: cn(
530
+ "w-20 h-20 rounded-full bg-black/50 backdrop-blur-sm",
531
+ "flex items-center justify-center shadow-2xl ring-2 ring-white/20",
532
+ "transition-transform duration-300 ease-out",
533
+ playing ? "scale-125" : "scale-100"
534
+ ),
535
+ children: /* @__PURE__ */ jsx8(FontAwesomeIcon6, { icon: faPlay2, className: "text-white text-2xl ml-1" })
536
+ }
537
+ )
538
+ }
539
+ );
540
+ }
541
+ function SubtitleOverlay({
542
+ cueText,
543
+ effectiveControls,
544
+ subtitleFontSize
545
+ }) {
546
+ return /* @__PURE__ */ jsx8(
547
+ "div",
548
+ {
549
+ className: cn(
550
+ "absolute left-0 right-0 flex justify-center px-6 pointer-events-none z-10 transition-all duration-300",
551
+ effectiveControls ? "bottom-[4.5rem]" : "bottom-4"
552
+ ),
553
+ children: /* @__PURE__ */ jsx8(
554
+ "span",
555
+ {
556
+ className: "bg-black/80 text-white px-3 py-1 rounded-md text-center max-w-[85%] whitespace-pre-line leading-snug font-medium",
557
+ style: { fontSize: SUBTITLE_SIZES[subtitleFontSize] },
558
+ children: cueText
559
+ }
560
+ )
561
+ }
562
+ );
563
+ }
564
+
565
+ // modules/ui/VideoPlayer/hooks/useControlsVisibility.ts
566
+ import { useCallback, useEffect, useRef, useState as useState2 } from "react";
567
+ function useControlsVisibility({
568
+ controlsVisible,
569
+ autoHideControls,
570
+ isCasting,
571
+ playing,
572
+ onChange
573
+ }) {
574
+ const [showControls, setShowControls] = useState2(true);
575
+ const hideTimerRef = useRef(null);
576
+ const isControlled = controlsVisible !== void 0;
577
+ const effectiveControls = isCasting ? true : isControlled ? controlsVisible : showControls;
578
+ useEffect(() => {
579
+ onChange == null ? void 0 : onChange(effectiveControls);
580
+ }, [effectiveControls, onChange]);
581
+ const scheduleHide = useCallback(
582
+ (isPlaying) => {
583
+ if (isControlled) return;
584
+ if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
585
+ setShowControls(true);
586
+ if (isCasting) return;
587
+ if (isPlaying && autoHideControls) {
588
+ hideTimerRef.current = setTimeout(() => setShowControls(false), 3e3);
589
+ }
590
+ },
591
+ [isControlled, autoHideControls, isCasting]
592
+ );
593
+ const resetHideTimer = useCallback(() => {
594
+ scheduleHide(playing);
595
+ }, [playing, scheduleHide]);
596
+ const forceShow = useCallback(() => {
597
+ if (!isControlled) setShowControls(true);
598
+ }, [isControlled]);
599
+ const hideIfPlaying = useCallback(() => {
600
+ if (!isControlled && autoHideControls && playing) setShowControls(false);
601
+ }, [isControlled, autoHideControls, playing]);
602
+ return {
603
+ showControls,
604
+ setShowControls,
605
+ effectiveControls,
606
+ isControlled,
607
+ scheduleHide,
608
+ resetHideTimer,
609
+ forceShow,
610
+ hideIfPlaying
611
+ };
612
+ }
613
+
614
+ // modules/ui/VideoPlayer/hooks/useVideoEvents.ts
615
+ import { useEffect as useEffect2 } from "react";
616
+ function useVideoEvents({
617
+ videoRef,
618
+ setCurrentTime,
619
+ setDuration,
620
+ setBuffered,
621
+ setLoading,
622
+ setPlaying,
623
+ setIsFullscreen,
624
+ scheduleHide,
625
+ forceShow
626
+ }) {
627
+ useEffect2(() => {
628
+ const video = videoRef.current;
629
+ if (!video) return;
630
+ const onTimeUpdate = () => setCurrentTime(video.currentTime);
631
+ const onDurationChange = () => setDuration(video.duration || 0);
632
+ const onProgress = () => {
633
+ if (video.buffered.length > 0 && video.duration) {
634
+ setBuffered(video.buffered.end(video.buffered.length - 1) / video.duration * 100);
635
+ }
636
+ };
637
+ const onWaiting = () => setLoading(true);
638
+ const onCanPlay = () => setLoading(false);
639
+ const onPlay = () => {
640
+ setPlaying(true);
641
+ scheduleHide(true);
642
+ };
643
+ const onPause = () => {
644
+ setPlaying(false);
645
+ forceShow();
646
+ };
647
+ const onEnded = () => {
648
+ setPlaying(false);
649
+ forceShow();
650
+ };
651
+ const onFSChange = () => setIsFullscreen(!!document.fullscreenElement);
652
+ video.addEventListener("timeupdate", onTimeUpdate);
653
+ video.addEventListener("durationchange", onDurationChange);
654
+ video.addEventListener("progress", onProgress);
655
+ video.addEventListener("waiting", onWaiting);
656
+ video.addEventListener("canplay", onCanPlay);
657
+ video.addEventListener("play", onPlay);
658
+ video.addEventListener("pause", onPause);
659
+ video.addEventListener("ended", onEnded);
660
+ document.addEventListener("fullscreenchange", onFSChange);
661
+ return () => {
662
+ video.removeEventListener("timeupdate", onTimeUpdate);
663
+ video.removeEventListener("durationchange", onDurationChange);
664
+ video.removeEventListener("progress", onProgress);
665
+ video.removeEventListener("waiting", onWaiting);
666
+ video.removeEventListener("canplay", onCanPlay);
667
+ video.removeEventListener("play", onPlay);
668
+ video.removeEventListener("pause", onPause);
669
+ video.removeEventListener("ended", onEnded);
670
+ document.removeEventListener("fullscreenchange", onFSChange);
671
+ };
672
+ }, [
673
+ videoRef,
674
+ setCurrentTime,
675
+ setDuration,
676
+ setBuffered,
677
+ setLoading,
678
+ setPlaying,
679
+ setIsFullscreen,
680
+ scheduleHide,
681
+ forceShow
682
+ ]);
683
+ }
684
+
685
+ // modules/ui/VideoPlayer/hooks/useSubtitleCues.ts
686
+ import { useEffect as useEffect3, useState as useState3 } from "react";
687
+ function useSubtitleCues({ videoRef, selectedSubtitle, subtitles }) {
688
+ const [cueText, setCueText] = useState3(null);
689
+ useEffect3(() => {
690
+ const video = videoRef.current;
691
+ if (!video) return;
692
+ Array.from(video.textTracks).forEach((t) => {
693
+ t.mode = "disabled";
694
+ });
695
+ setCueText(null);
696
+ if (selectedSubtitle === null || !(subtitles == null ? void 0 : subtitles[selectedSubtitle])) return;
697
+ const track = video.textTracks[selectedSubtitle];
698
+ if (!track) return;
699
+ track.mode = "hidden";
700
+ const onCueChange = () => {
701
+ const active = track.activeCues;
702
+ if (!active || active.length === 0) {
703
+ setCueText(null);
704
+ return;
705
+ }
706
+ const text = Array.from(active).map((c) => c.text.replace(/<[^>]+>/g, "")).join("\n");
707
+ setCueText(text || null);
708
+ };
709
+ track.addEventListener("cuechange", onCueChange);
710
+ return () => track.removeEventListener("cuechange", onCueChange);
711
+ }, [videoRef, selectedSubtitle, subtitles]);
712
+ return cueText;
713
+ }
714
+
715
+ // modules/ui/VideoPlayer/hooks/useGoogleCast.ts
716
+ import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef2, useState as useState4 } from "react";
717
+ function mapState(s) {
718
+ if (s === "CONNECTED") return "connected";
719
+ if (s === "CONNECTING") return "connecting";
720
+ if (s === "NO_DEVICES_AVAILABLE") return "unavailable";
721
+ return "available";
722
+ }
723
+ function useGoogleCast({
724
+ enableCast,
725
+ videoRef,
726
+ src,
727
+ title,
728
+ poster,
729
+ setPlaying,
730
+ setCurrentTime,
731
+ setDuration,
732
+ setVolume,
733
+ setMuted,
734
+ onCastStateChange
735
+ }) {
736
+ const [castState, setCastState] = useState4("unavailable");
737
+ const [castDeviceName, setCastDeviceName] = useState4(null);
738
+ const remotePlayerRef = useRef2(null);
739
+ const remoteControllerRef = useRef2(null);
740
+ useEffect4(() => {
741
+ var _a;
742
+ if (!enableCast || typeof window === "undefined") return;
743
+ const w = window;
744
+ let cleanupListener;
745
+ const init = () => {
746
+ var _a2, _b;
747
+ const framework = (_a2 = w.cast) == null ? void 0 : _a2.framework;
748
+ const chromeCast = (_b = w.chrome) == null ? void 0 : _b.cast;
749
+ if (!framework || !chromeCast) return;
750
+ const context = framework.CastContext.getInstance();
751
+ context.setOptions({
752
+ receiverApplicationId: chromeCast.media.DEFAULT_MEDIA_RECEIVER_APP_ID,
753
+ autoJoinPolicy: chromeCast.AutoJoinPolicy.ORIGIN_SCOPED
754
+ });
755
+ const sync = () => {
756
+ var _a3, _b2;
757
+ const next = mapState(context.getCastState());
758
+ setCastState(next);
759
+ const session = context.getCurrentSession();
760
+ setCastDeviceName(
761
+ next === "connected" ? (_b2 = (_a3 = session == null ? void 0 : session.getCastDevice()) == null ? void 0 : _a3.friendlyName) != null ? _b2 : null : null
762
+ );
763
+ };
764
+ const handler = () => sync();
765
+ context.addEventListener(framework.CastContextEventType.CAST_STATE_CHANGED, handler);
766
+ sync();
767
+ const remotePlayer = new framework.RemotePlayer();
768
+ const remoteController = new framework.RemotePlayerController(remotePlayer);
769
+ remotePlayerRef.current = remotePlayer;
770
+ remoteControllerRef.current = remoteController;
771
+ const syncRemote = () => {
772
+ if (!remotePlayer.isConnected) return;
773
+ setPlaying(!remotePlayer.isPaused);
774
+ if (isFinite(remotePlayer.currentTime)) setCurrentTime(remotePlayer.currentTime);
775
+ if (remotePlayer.duration > 0) setDuration(remotePlayer.duration);
776
+ setVolume(remotePlayer.volumeLevel);
777
+ setMuted(remotePlayer.isMuted);
778
+ };
779
+ remoteController.addEventListener(framework.RemotePlayerEventType.ANY_CHANGE, syncRemote);
780
+ cleanupListener = () => {
781
+ context.removeEventListener(framework.CastContextEventType.CAST_STATE_CHANGED, handler);
782
+ remoteController.removeEventListener(
783
+ framework.RemotePlayerEventType.ANY_CHANGE,
784
+ syncRemote
785
+ );
786
+ };
787
+ };
788
+ if ((_a = w.cast) == null ? void 0 : _a.framework) {
789
+ init();
790
+ } else {
791
+ const SCRIPT_ID = "google-cast-sdk";
792
+ w.__onGCastApiAvailable = (available) => {
793
+ if (available) init();
794
+ };
795
+ if (!document.getElementById(SCRIPT_ID)) {
796
+ const script = document.createElement("script");
797
+ script.id = SCRIPT_ID;
798
+ script.src = "https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1";
799
+ script.async = true;
800
+ document.head.appendChild(script);
801
+ }
802
+ }
803
+ return () => {
804
+ cleanupListener == null ? void 0 : cleanupListener();
805
+ };
806
+ }, [enableCast, setPlaying, setCurrentTime, setDuration, setVolume, setMuted]);
807
+ useEffect4(() => {
808
+ onCastStateChange == null ? void 0 : onCastStateChange(castState);
809
+ }, [castState, onCastStateChange]);
810
+ const toggleCast = useCallback2(async () => {
811
+ var _a, _b, _c;
812
+ if (typeof window === "undefined") return;
813
+ const w = window;
814
+ const framework = (_a = w.cast) == null ? void 0 : _a.framework;
815
+ const chromeCast = (_b = w.chrome) == null ? void 0 : _b.cast;
816
+ if (!framework || !chromeCast) return;
817
+ const context = framework.CastContext.getInstance();
818
+ if (castState === "connected") {
819
+ context.endCurrentSession(true);
820
+ return;
821
+ }
822
+ try {
823
+ await context.requestSession();
824
+ const session = context.getCurrentSession();
825
+ const v = videoRef.current;
826
+ if (!session || !v) return;
827
+ const first = Array.isArray(src) ? src[0] : src;
828
+ const videoSrc = v.currentSrc || (typeof first === "string" ? first : first.src);
829
+ const contentType = typeof first === "string" ? "video/mp4" : (_c = first.type) != null ? _c : "video/mp4";
830
+ const mediaInfo = new chromeCast.media.MediaInfo(videoSrc, contentType);
831
+ const metadata = new chromeCast.media.GenericMediaMetadata();
832
+ if (title) metadata.title = title;
833
+ if (poster) metadata.images = [new chromeCast.Image(poster)];
834
+ mediaInfo.metadata = metadata;
835
+ const request = new chromeCast.media.LoadRequest(mediaInfo);
836
+ request.currentTime = v.currentTime;
837
+ await session.loadMedia(request);
838
+ v.pause();
839
+ } catch (e) {
840
+ }
841
+ }, [castState, src, title, poster, videoRef]);
842
+ return {
843
+ castState,
844
+ castDeviceName,
845
+ remotePlayerRef,
846
+ remoteControllerRef,
847
+ toggleCast
848
+ };
849
+ }
850
+
851
+ // modules/ui/VideoPlayer/hooks/useFullscreen.ts
852
+ import { useCallback as useCallback3 } from "react";
853
+ function useFullscreen(containerRef) {
854
+ const toggleFullscreen = useCallback3(() => {
855
+ const c = containerRef.current;
856
+ if (!c) return;
857
+ if (!document.fullscreenElement) c.requestFullscreen();
858
+ else document.exitFullscreen();
859
+ }, [containerRef]);
860
+ return { toggleFullscreen };
861
+ }
862
+
863
+ // modules/ui/VideoPlayer/hooks/useKeyboardShortcuts.ts
864
+ import { useEffect as useEffect5 } from "react";
865
+ function useKeyboardShortcuts({
866
+ containerRef,
867
+ videoRef,
868
+ togglePlay,
869
+ seekBy,
870
+ toggleMute,
871
+ handleVolumeChange,
872
+ toggleFullscreen,
873
+ volume,
874
+ showSettings,
875
+ closeSettings
876
+ }) {
877
+ useEffect5(() => {
878
+ const handler = (e) => {
879
+ const c = containerRef.current;
880
+ if (!c) return;
881
+ const focused = document.activeElement;
882
+ if (!c.contains(focused) && focused !== c) return;
883
+ const v = videoRef.current;
884
+ if (!v) return;
885
+ switch (e.key) {
886
+ case " ":
887
+ case "k":
888
+ e.preventDefault();
889
+ togglePlay();
890
+ break;
891
+ case "ArrowLeft":
892
+ e.preventDefault();
893
+ seekBy(-10);
894
+ break;
895
+ case "ArrowRight":
896
+ e.preventDefault();
897
+ seekBy(10);
898
+ break;
899
+ case "ArrowUp":
900
+ e.preventDefault();
901
+ handleVolumeChange(volume + 0.1);
902
+ break;
903
+ case "ArrowDown":
904
+ e.preventDefault();
905
+ handleVolumeChange(volume - 0.1);
906
+ break;
907
+ case "m":
908
+ e.preventDefault();
909
+ toggleMute();
910
+ break;
911
+ case "f":
912
+ e.preventDefault();
913
+ toggleFullscreen();
914
+ break;
915
+ case "Escape":
916
+ if (showSettings) {
917
+ e.preventDefault();
918
+ closeSettings();
919
+ }
920
+ break;
921
+ }
922
+ };
923
+ document.addEventListener("keydown", handler);
924
+ return () => document.removeEventListener("keydown", handler);
925
+ }, [
926
+ containerRef,
927
+ videoRef,
928
+ togglePlay,
929
+ seekBy,
930
+ toggleMute,
931
+ handleVolumeChange,
932
+ toggleFullscreen,
933
+ volume,
934
+ showSettings,
935
+ closeSettings
936
+ ]);
937
+ }
938
+
939
+ // modules/ui/VideoPlayer/hooks/usePlayerActions.ts
940
+ import { useCallback as useCallback4 } from "react";
941
+ function usePlayerActions({
942
+ isCasting,
943
+ videoRef,
944
+ progressRef,
945
+ remotePlayerRef,
946
+ remoteControllerRef,
947
+ setVolume,
948
+ setMuted
949
+ }) {
950
+ const togglePlay = useCallback4(() => {
951
+ if (isCasting && remoteControllerRef.current) {
952
+ remoteControllerRef.current.playOrPause();
953
+ return;
954
+ }
955
+ const v = videoRef.current;
956
+ if (!v) return;
957
+ if (v.paused) v.play();
958
+ else v.pause();
959
+ }, [isCasting, remoteControllerRef, videoRef]);
960
+ const seekBy = useCallback4(
961
+ (delta) => {
962
+ if (isCasting && remotePlayerRef.current && remoteControllerRef.current) {
963
+ const rp = remotePlayerRef.current;
964
+ rp.currentTime = Math.max(0, Math.min(rp.duration || 0, rp.currentTime + delta));
965
+ remoteControllerRef.current.seek();
966
+ return;
967
+ }
968
+ const v = videoRef.current;
969
+ if (!v) return;
970
+ v.currentTime = Math.max(0, Math.min(v.duration, v.currentTime + delta));
971
+ },
972
+ [isCasting, remotePlayerRef, remoteControllerRef, videoRef]
973
+ );
974
+ const toggleMute = useCallback4(() => {
975
+ if (isCasting && remoteControllerRef.current) {
976
+ remoteControllerRef.current.muteOrUnmute();
977
+ return;
978
+ }
979
+ const v = videoRef.current;
980
+ if (!v) return;
981
+ v.muted = !v.muted;
982
+ setMuted(v.muted);
983
+ }, [isCasting, remoteControllerRef, videoRef, setMuted]);
984
+ const handleVolumeChange = useCallback4(
985
+ (val) => {
986
+ const c = Math.max(0, Math.min(1, val));
987
+ if (isCasting && remotePlayerRef.current && remoteControllerRef.current) {
988
+ remotePlayerRef.current.volumeLevel = c;
989
+ remoteControllerRef.current.setVolumeLevel();
990
+ setVolume(c);
991
+ setMuted(c === 0);
992
+ return;
993
+ }
994
+ const v = videoRef.current;
995
+ if (!v) return;
996
+ v.volume = c;
997
+ v.muted = c === 0;
998
+ setVolume(c);
999
+ setMuted(c === 0);
1000
+ },
1001
+ [isCasting, remotePlayerRef, remoteControllerRef, videoRef, setVolume, setMuted]
1002
+ );
1003
+ const handleSeek = useCallback4(
1004
+ (e) => {
1005
+ const bar = progressRef.current;
1006
+ if (!bar) return;
1007
+ const rect = bar.getBoundingClientRect();
1008
+ const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
1009
+ if (isCasting && remotePlayerRef.current && remoteControllerRef.current) {
1010
+ const rp = remotePlayerRef.current;
1011
+ if (!rp.duration) return;
1012
+ rp.currentTime = ratio * rp.duration;
1013
+ remoteControllerRef.current.seek();
1014
+ return;
1015
+ }
1016
+ const v = videoRef.current;
1017
+ if (!v || !v.duration) return;
1018
+ v.currentTime = ratio * v.duration;
1019
+ },
1020
+ [isCasting, remotePlayerRef, remoteControllerRef, videoRef, progressRef]
1021
+ );
1022
+ return { togglePlay, seekBy, toggleMute, handleVolumeChange, handleSeek };
1023
+ }
1024
+
1025
+ // modules/ui/VideoPlayer/index.tsx
1026
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1027
+ function VideoPlayer({
1028
+ src,
1029
+ poster,
1030
+ title,
1031
+ autoPlay = false,
1032
+ loop = false,
1033
+ startMuted = false,
1034
+ qualities,
1035
+ defaultQuality,
1036
+ subtitles,
1037
+ audioTracks,
1038
+ onQualityChange,
1039
+ onAudioTrackChange,
1040
+ controlsVisible,
1041
+ autoHideControls = true,
1042
+ onControlsVisibilityChange,
1043
+ enableCast = true,
1044
+ onCastStateChange,
1045
+ className
1046
+ }) {
1047
+ var _a, _b;
1048
+ const videoRef = useRef3(null);
1049
+ const containerRef = useRef3(null);
1050
+ const progressRef = useRef3(null);
1051
+ const settingsPanelRef = useRef3(null);
1052
+ const [playing, setPlaying] = useState5(false);
1053
+ const [currentTime, setCurrentTime] = useState5(0);
1054
+ const [duration, setDuration] = useState5(0);
1055
+ const [buffered, setBuffered] = useState5(0);
1056
+ const [volume, setVolume] = useState5(1);
1057
+ const [muted, setMuted] = useState5(startMuted);
1058
+ const [speed, setSpeed] = useState5(1);
1059
+ const [isFullscreen, setIsFullscreen] = useState5(false);
1060
+ const [loading, setLoading] = useState5(true);
1061
+ const [seekHoverX, setSeekHoverX] = useState5(null);
1062
+ const [selectedQuality, setSelectedQuality] = useState5((_b = defaultQuality != null ? defaultQuality : (_a = qualities == null ? void 0 : qualities[0]) == null ? void 0 : _a.value) != null ? _b : "");
1063
+ const [selectedSubtitle, setSelectedSubtitle] = useState5(null);
1064
+ const [selectedAudioTrack, setSelectedAudioTrack] = useState5(0);
1065
+ const [subtitleFontSize, setSubtitleFontSize] = useState5("md");
1066
+ const [showSettings, setShowSettings] = useState5(false);
1067
+ const [settingsView, setSettingsView] = useState5("main");
1068
+ const sources = Array.isArray(src) ? src : [src];
1069
+ const { castState, castDeviceName, remotePlayerRef, remoteControllerRef, toggleCast } = useGoogleCast({
1070
+ enableCast,
1071
+ videoRef,
1072
+ src,
1073
+ title,
1074
+ poster,
1075
+ setPlaying,
1076
+ setCurrentTime,
1077
+ setDuration,
1078
+ setVolume,
1079
+ setMuted,
1080
+ onCastStateChange
1081
+ });
1082
+ const isCasting = castState === "connected";
1083
+ const { effectiveControls, scheduleHide, resetHideTimer, forceShow, hideIfPlaying } = useControlsVisibility({
1084
+ controlsVisible,
1085
+ autoHideControls,
1086
+ isCasting,
1087
+ playing,
1088
+ onChange: onControlsVisibilityChange
1089
+ });
1090
+ useVideoEvents({ videoRef, setCurrentTime, setDuration, setBuffered, setLoading, setPlaying, setIsFullscreen, scheduleHide, forceShow });
1091
+ const cueText = useSubtitleCues({ videoRef, selectedSubtitle, subtitles });
1092
+ const { toggleFullscreen } = useFullscreen(containerRef);
1093
+ const { togglePlay, seekBy, toggleMute, handleVolumeChange, handleSeek } = usePlayerActions({
1094
+ isCasting,
1095
+ videoRef,
1096
+ progressRef,
1097
+ remotePlayerRef,
1098
+ remoteControllerRef,
1099
+ setVolume,
1100
+ setMuted
1101
+ });
1102
+ const closeSettings = useCallback5(() => {
1103
+ setShowSettings(false);
1104
+ setSettingsView("main");
1105
+ }, []);
1106
+ const applySpeed = useCallback5((s) => {
1107
+ const v = videoRef.current;
1108
+ if (v) v.playbackRate = s;
1109
+ setSpeed(s);
1110
+ closeSettings();
1111
+ }, [closeSettings]);
1112
+ const applyQuality = useCallback5((value) => {
1113
+ setSelectedQuality(value);
1114
+ onQualityChange == null ? void 0 : onQualityChange(value);
1115
+ closeSettings();
1116
+ }, [onQualityChange, closeSettings]);
1117
+ const applySubtitle = useCallback5((index) => {
1118
+ setSelectedSubtitle(index);
1119
+ closeSettings();
1120
+ }, [closeSettings]);
1121
+ const applyAudioTrack = useCallback5((index) => {
1122
+ setSelectedAudioTrack(index);
1123
+ onAudioTrackChange == null ? void 0 : onAudioTrackChange(index);
1124
+ closeSettings();
1125
+ }, [onAudioTrackChange, closeSettings]);
1126
+ const applySubtitleSize = useCallback5((size) => {
1127
+ setSubtitleFontSize(size);
1128
+ setSettingsView("main");
1129
+ }, []);
1130
+ useKeyboardShortcuts({ containerRef, videoRef, togglePlay, seekBy, toggleMute, handleVolumeChange, toggleFullscreen, volume, showSettings, closeSettings });
1131
+ useEffect6(() => {
1132
+ if (!showSettings) return;
1133
+ const handler = (e) => {
1134
+ var _a2;
1135
+ if (!((_a2 = settingsPanelRef.current) == null ? void 0 : _a2.contains(e.target))) closeSettings();
1136
+ };
1137
+ document.addEventListener("mousedown", handler);
1138
+ return () => document.removeEventListener("mousedown", handler);
1139
+ }, [showSettings, closeSettings]);
1140
+ const handleSeekMouseMove = useCallback5((e) => {
1141
+ const bar = progressRef.current;
1142
+ if (!bar) return;
1143
+ const rect = bar.getBoundingClientRect();
1144
+ setSeekHoverX(Math.max(0, Math.min(rect.width, e.clientX - rect.left)));
1145
+ }, []);
1146
+ const progress = duration > 0 ? currentTime / duration * 100 : 0;
1147
+ const seekHoverPct = seekHoverX !== null && progressRef.current ? seekHoverX / progressRef.current.getBoundingClientRect().width * 100 : null;
1148
+ const hoverTime = seekHoverPct !== null ? formatTime(seekHoverPct / 100 * duration) : null;
1149
+ return /* @__PURE__ */ jsxs8(
1150
+ "div",
1151
+ {
1152
+ ref: containerRef,
1153
+ tabIndex: 0,
1154
+ "aria-label": title ? `Video: ${title}` : "Video player",
1155
+ className: cn(
1156
+ "relative bg-black rounded-xl overflow-hidden select-none outline-none",
1157
+ "aspect-video min-h-[10rem]",
1158
+ "focus-visible:ring-2 focus-visible:ring-border-focus",
1159
+ className
1160
+ ),
1161
+ onMouseMove: resetHideTimer,
1162
+ onMouseLeave: hideIfPlaying,
1163
+ children: [
1164
+ /* @__PURE__ */ jsxs8(
1165
+ "video",
1166
+ {
1167
+ ref: videoRef,
1168
+ poster,
1169
+ autoPlay,
1170
+ loop,
1171
+ muted: startMuted,
1172
+ crossOrigin: "anonymous",
1173
+ className: "w-full h-full object-contain block",
1174
+ onClick: togglePlay,
1175
+ style: { cursor: "pointer" },
1176
+ children: [
1177
+ sources.map((s, i) => typeof s === "string" ? /* @__PURE__ */ jsx9("source", { src: s }, i) : /* @__PURE__ */ jsx9("source", { src: s.src, type: s.type }, i)),
1178
+ subtitles == null ? void 0 : subtitles.map((sub, i) => /* @__PURE__ */ jsx9("track", { kind: "subtitles", label: sub.label, srcLang: sub.srclang, src: sub.src }, i))
1179
+ ]
1180
+ }
1181
+ ),
1182
+ isCasting && /* @__PURE__ */ jsx9(CastOverlay, { castDeviceName, title }),
1183
+ loading && /* @__PURE__ */ jsx9(LoadingOverlay, {}),
1184
+ !loading && /* @__PURE__ */ jsx9(CenterPlayOverlay, { playing }),
1185
+ cueText && /* @__PURE__ */ jsx9(SubtitleOverlay, { cueText, effectiveControls, subtitleFontSize }),
1186
+ /* @__PURE__ */ jsxs8(
1187
+ "div",
1188
+ {
1189
+ className: cn("absolute inset-0 flex flex-col justify-end transition-opacity duration-300 z-20", effectiveControls ? "opacity-100" : "opacity-0 pointer-events-none"),
1190
+ onClick: (e) => {
1191
+ if (e.target === e.currentTarget && !isCasting) togglePlay();
1192
+ },
1193
+ children: [
1194
+ /* @__PURE__ */ jsx9("div", { className: "absolute inset-0 pointer-events-none", style: { background: "linear-gradient(to top, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.3) 30%, transparent 60%)" } }),
1195
+ showSettings && /* @__PURE__ */ jsx9(
1196
+ SettingsPanel,
1197
+ {
1198
+ ref: settingsPanelRef,
1199
+ view: settingsView,
1200
+ onChangeView: setSettingsView,
1201
+ qualities,
1202
+ subtitles,
1203
+ audioTracks,
1204
+ selectedQuality,
1205
+ selectedSubtitle,
1206
+ selectedAudioTrack,
1207
+ speed,
1208
+ subtitleFontSize,
1209
+ applyQuality,
1210
+ applySpeed,
1211
+ applySubtitle,
1212
+ applySubtitleSize,
1213
+ applyAudioTrack
1214
+ }
1215
+ ),
1216
+ /* @__PURE__ */ jsxs8("div", { className: "relative px-4 pb-3 pt-6 space-y-2.5", children: [
1217
+ title && /* @__PURE__ */ jsx9("p", { className: "text-white/90 text-sm font-medium truncate leading-tight", children: title }),
1218
+ /* @__PURE__ */ jsx9(
1219
+ ProgressBar,
1220
+ {
1221
+ ref: progressRef,
1222
+ progress,
1223
+ buffered,
1224
+ seekHoverX,
1225
+ seekHoverPct,
1226
+ hoverTime,
1227
+ onSeek: handleSeek,
1228
+ onSeekMouseMove: handleSeekMouseMove,
1229
+ onSeekLeave: () => setSeekHoverX(null)
1230
+ }
1231
+ ),
1232
+ /* @__PURE__ */ jsx9(
1233
+ ControlRow,
1234
+ {
1235
+ playing,
1236
+ muted,
1237
+ volume,
1238
+ currentTime,
1239
+ duration,
1240
+ isFullscreen,
1241
+ showSettings,
1242
+ enableCast,
1243
+ castState,
1244
+ onPlay: togglePlay,
1245
+ onSeekBy: seekBy,
1246
+ onToggleMute: toggleMute,
1247
+ onVolumeChange: handleVolumeChange,
1248
+ onToggleSettings: () => {
1249
+ setShowSettings((v) => !v);
1250
+ setSettingsView("main");
1251
+ },
1252
+ onToggleCast: toggleCast,
1253
+ onToggleFullscreen: toggleFullscreen
1254
+ }
1255
+ )
1256
+ ] })
1257
+ ]
1258
+ }
1259
+ )
1260
+ ]
1261
+ }
1262
+ );
1263
+ }
1264
+
1265
+ export {
1266
+ VideoPlayer
1267
+ };