@therealtinhtute/baroiplayer 1.2.1 → 2.0.0-beta.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.
- package/README.md +90 -71
- package/dist/components/icons/index.d.ts +36 -0
- package/dist/components/icons/index.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/ArrowLeftIcon.d.ts +4 -0
- package/dist/components/icons/svg-icons/ArrowLeftIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/ArrowRightIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/ArrowRightIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/AudioIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/AudioIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/BackwardIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/BackwardIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/CameraIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/CameraIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/CheckIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/CheckIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/EnablePiPIcon.d.ts +4 -0
- package/dist/components/icons/svg-icons/EnablePiPIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/ForwardIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/ForwardIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/FullscreenEnterIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/FullscreenEnterIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/FullscreenExitIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/FullscreenExitIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/LoadingIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/LoadingIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/PauseIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/PauseIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/PlayIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/PlayIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/PlaybackSpeedIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/PlaybackSpeedIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/QualityIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/QualityIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/SettingsIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/SettingsIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/SliderIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/SliderIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/SubtitleIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/SubtitleIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/SubtitleOffIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/SubtitleOffIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/UnPiPIcon.d.ts +4 -0
- package/dist/components/icons/svg-icons/UnPiPIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/VolumeMutedIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/VolumeMutedIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/VolumeOneIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/VolumeOneIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/VolumeThreeIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/VolumeThreeIcon.d.ts.map +1 -0
- package/dist/components/icons/svg-icons/VolumeTwoIcon.d.ts +5 -0
- package/dist/components/icons/svg-icons/VolumeTwoIcon.d.ts.map +1 -0
- package/dist/components/variants/gradient/gradient-audio.d.ts +13 -0
- package/dist/components/variants/gradient/gradient-audio.d.ts.map +1 -0
- package/dist/components/variants/gradient/gradient-controls.d.ts +4 -0
- package/dist/components/variants/gradient/gradient-controls.d.ts.map +1 -0
- package/dist/components/variants/gradient/gradient-glyphs.d.ts +30 -0
- package/dist/components/variants/gradient/gradient-glyphs.d.ts.map +1 -0
- package/dist/components/variants/gradient/gradient-video.d.ts +16 -0
- package/dist/components/variants/gradient/gradient-video.d.ts.map +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +750 -5640
- package/dist/index.mjs.map +1 -1
- package/dist/package.json +3 -0
- package/package.json +110 -114
- package/src/components/variants/gradient/gradient-audio.tsx +47 -0
- package/src/components/variants/gradient/gradient-controls.tsx +254 -0
- package/src/components/variants/gradient/gradient-glyphs.test.tsx +69 -0
- package/src/components/variants/gradient/gradient-glyphs.tsx +129 -0
- package/src/components/variants/gradient/gradient-skin.css +211 -0
- package/src/components/variants/gradient/gradient-video.tsx +70 -0
- package/src/index.ts +5 -198
- package/src/test/accessibility-utils.ts +0 -59
- package/dist/context/media.js +0 -1
- package/dist/context/media.mjs +0 -3
- package/dist/context/store.js +0 -1
- package/dist/context/store.mjs +0 -3
- package/dist/context-Bzd5QCKM.cjs +0 -7
- package/dist/context-Bzd5QCKM.cjs.map +0 -1
- package/dist/context-DD_SH4tT.js +0 -1053
- package/dist/context-DD_SH4tT.js.map +0 -1
- package/dist/core-events-BRnLMB7s.cjs +0 -2
- package/dist/core-events-BRnLMB7s.cjs.map +0 -1
- package/dist/core-events-v_mKdj5p.js +0 -417
- package/dist/core-events-v_mKdj5p.js.map +0 -1
- package/dist/core-plugins-CMBv-yVh.cjs +0 -2
- package/dist/core-plugins-CMBv-yVh.cjs.map +0 -1
- package/dist/core-plugins-CqCxLkbd.js +0 -754
- package/dist/core-plugins-CqCxLkbd.js.map +0 -1
- package/dist/core.js +0 -15
- package/dist/core.js.map +0 -1
- package/dist/core.mjs +0 -960
- package/dist/core.mjs.map +0 -1
- package/dist/enhanced-components-BwjBqSA1.js +0 -2858
- package/dist/enhanced-components-BwjBqSA1.js.map +0 -1
- package/dist/enhanced-components-C-ka-XLp.cjs +0 -2
- package/dist/enhanced-components-C-ka-XLp.cjs.map +0 -1
- package/dist/hooks-B-IIWPJr.js +0 -1842
- package/dist/hooks-B-IIWPJr.js.map +0 -1
- package/dist/hooks-DcHzmBwA.cjs +0 -4
- package/dist/hooks-DcHzmBwA.cjs.map +0 -1
- package/dist/minimal-components-BYsMQde5.js +0 -1610
- package/dist/minimal-components-BYsMQde5.js.map +0 -1
- package/dist/minimal-components-CsxQbDp7.cjs +0 -2
- package/dist/minimal-components-CsxQbDp7.cjs.map +0 -1
- package/dist/minimal-components.css +0 -1
- package/dist/mona-loading.gif +0 -0
- package/dist/performance-monitor-DCSijLJg.js +0 -312
- package/dist/performance-monitor-DCSijLJg.js.map +0 -1
- package/dist/performance-monitor-wNFKmqMr.cjs +0 -2
- package/dist/performance-monitor-wNFKmqMr.cjs.map +0 -1
- package/dist/rolldown-runtime-BmowowmI.js +0 -14
- package/dist/rolldown-runtime-mLOUI7ql.cjs +0 -1
- package/dist/utils-D2gQ7c2R.js +0 -64
- package/dist/utils-D2gQ7c2R.js.map +0 -1
- package/dist/utils-DJaAwiZ9.cjs +0 -2
- package/dist/utils-DJaAwiZ9.cjs.map +0 -1
- package/dist/utils.js +0 -1
- package/dist/utils.mjs +0 -3
- package/src/adapters/videojs-adapter.tsx +0 -470
- package/src/assets/mona-loading.gif +0 -0
- package/src/components/AnimateLoading.tsx +0 -111
- package/src/components/LoadingState.tsx +0 -239
- package/src/components/__tests__/media-player-accessibility.test.tsx +0 -510
- package/src/components/__tests__/media-player-react19.test.tsx +0 -457
- package/src/components/accessibility/keyboard-help-overlay.tsx +0 -207
- package/src/components/audio-player.tsx +0 -85
- package/src/components/basic/index.ts +0 -37
- package/src/components/enhanced/advanced-features.tsx +0 -311
- package/src/components/enhanced/debug-panel.tsx +0 -448
- package/src/components/enhanced/enhanced-controls.tsx +0 -597
- package/src/components/enhanced/enhanced-media-player.tsx +0 -566
- package/src/components/enhanced/error-handling.tsx +0 -338
- package/src/components/enhanced/index.ts +0 -39
- package/src/components/enhanced/media-player-enhanced.tsx +0 -383
- package/src/components/examples/basic-video-player.tsx +0 -50
- package/src/components/examples/custom-controls-example.tsx +0 -63
- package/src/components/examples/enhanced-controls-example.tsx +0 -100
- package/src/components/examples/enhanced-media-player-example.tsx +0 -302
- package/src/components/examples/loading-states-demo.tsx +0 -141
- package/src/components/examples/react-19-demo.tsx +0 -349
- package/src/components/examples/volume-orientation-demo.tsx +0 -105
- package/src/components/icon-demo.tsx +0 -196
- package/src/components/legacy.tsx +0 -87
- package/src/components/media-metadata-display.tsx +0 -99
- package/src/components/media-player-audio.tsx +0 -39
- package/src/components/media-player-controls.tsx +0 -223
- package/src/components/media-player-error.tsx +0 -163
- package/src/components/media-player-fullscreen.tsx +0 -109
- package/src/components/media-player-hls.tsx +0 -111
- package/src/components/media-player-loading.tsx +0 -76
- package/src/components/media-player-play.tsx +0 -75
- package/src/components/media-player-root.tsx +0 -338
- package/src/components/media-player-seek.tsx +0 -168
- package/src/components/media-player-time.tsx +0 -99
- package/src/components/media-player-tooltip.tsx +0 -58
- package/src/components/media-player-video.tsx +0 -54
- package/src/components/media-player-volume.tsx +0 -155
- package/src/components/media-player.tsx +0 -292
- package/src/components/media-suspense.tsx +0 -344
- package/src/components/player-settings-form.tsx +0 -158
- package/src/components/react-player-wrapper.tsx +0 -651
- package/src/components/simple-video-player.tsx +0 -62
- package/src/components/ui/blur-poster.tsx +0 -96
- package/src/components/ui/empty-state.tsx +0 -91
- package/src/components/ui/enhanced-seek-bar.tsx +0 -254
- package/src/components/ui/error-state.tsx +0 -124
- package/src/components/ui/fullscreen-button.tsx +0 -81
- package/src/components/ui/loading-skeleton.tsx +0 -107
- package/src/components/ui/media-resource-preloader.tsx +0 -458
- package/src/components/ui/offline-message.tsx +0 -98
- package/src/components/ui/play-button.tsx +0 -99
- package/src/components/ui/seek-bar.tsx +0 -191
- package/src/components/ui/time-display.tsx +0 -61
- package/src/components/ui/volume-control.tsx +0 -190
- package/src/components/unified/base-media-player.tsx +0 -252
- package/src/components/unified/media-player.tsx +0 -75
- package/src/components/variants/minimal/AccessibleMinimalPlayer.tsx +0 -587
- package/src/components/variants/minimal/EnhancedMinimalMediaPlayer.tsx +0 -573
- package/src/components/variants/minimal/MinimalControls.tsx +0 -124
- package/src/components/variants/minimal/MinimalLoading.tsx +0 -28
- package/src/components/variants/minimal/MinimalMediaPlayer.tsx +0 -318
- package/src/components/variants/minimal/MinimalOverlay.tsx +0 -58
- package/src/components/variants/minimal/MinimalPlayButton.tsx +0 -56
- package/src/components/variants/minimal/MinimalSeekBar.tsx +0 -140
- package/src/components/variants/minimal/MinimalVolumeControl.tsx +0 -75
- package/src/components/variants/minimal/SimpleMinimalPlayer.tsx +0 -206
- package/src/components/variants/minimal/components/CenterPlayButton.tsx +0 -22
- package/src/components/variants/minimal/components/ControlBar.tsx +0 -142
- package/src/components/variants/minimal/components/EnhancedSettingsMenu.tsx +0 -412
- package/src/components/variants/minimal/components/PlaybackControls.tsx +0 -46
- package/src/components/variants/minimal/components/RightControls.tsx +0 -164
- package/src/components/variants/minimal/components/SeekBar.tsx +0 -127
- package/src/components/variants/minimal/components/SettingsDropdown.tsx +0 -68
- package/src/components/variants/minimal/components/SubtitleDisplay.tsx +0 -21
- package/src/components/variants/minimal/components/TimeDisplay.tsx +0 -15
- package/src/components/variants/minimal/components/VideoElement.tsx +0 -35
- package/src/components/variants/minimal/components/VolumeControl.tsx +0 -220
- package/src/components/variants/minimal/index.ts +0 -23
- package/src/components/video-player.tsx +0 -100
- package/src/context/__tests__/media-store.test.ts +0 -509
- package/src/context/error-handling.ts +0 -398
- package/src/context/media-context.tsx +0 -254
- package/src/context/media-provider.tsx +0 -264
- package/src/context/media-store.ts +0 -387
- package/src/context/persistence.ts +0 -462
- package/src/core/events/event-system.ts +0 -448
- package/src/core/events/hooks.ts +0 -325
- package/src/core/events/index.ts +0 -22
- package/src/core/events/middleware.ts +0 -286
- package/src/core/index.ts +0 -22
- package/src/core/optimization/bundle-optimizer.tsx +0 -312
- package/src/core/performance/cicd.ts +0 -98
- package/src/core/performance/index.ts +0 -38
- package/src/core/performance/media-loading-optimizer.ts +0 -545
- package/src/core/performance/performance-budgets.ts +0 -437
- package/src/core/performance/performance-monitor.tsx +0 -488
- package/src/core/performance/performance-testing.ts +0 -461
- package/src/core/plugins/__tests__/registry.test.ts +0 -595
- package/src/core/plugins/base-plugin.ts +0 -225
- package/src/core/plugins/enhanced-plugin-system.ts +0 -435
- package/src/core/plugins/examples/analytics-plugin.ts +0 -241
- package/src/core/plugins/index.ts +0 -39
- package/src/core/plugins/registry.ts +0 -546
- package/src/core/plugins/types.ts +0 -169
- package/src/core/plugins/utils.ts +0 -317
- package/src/core/progressive-loading.tsx +0 -331
- package/src/core/providers/enhanced-hls-provider.ts +0 -307
- package/src/core/providers/lazy-provider.tsx +0 -14
- package/src/core/providers/service-provider.tsx +0 -164
- package/src/core/utils.tsx +0 -34
- package/src/features/media-session/media-session-manager.ts +0 -411
- package/src/features/picture-in-picture/pip-manager.ts +0 -428
- package/src/features/playlist/playlist-manager.ts +0 -626
- package/src/features/subtitles/subtitle-manager.ts +0 -435
- package/src/features/subtitles/subtitle-parser.ts +0 -284
- package/src/features/thumbnails/thumbnail-generator.ts +0 -528
- package/src/hooks/__tests__/currentTime-fix.test.ts +0 -149
- package/src/hooks/__tests__/use-media-player.test.ts +0 -712
- package/src/hooks/__tests__/use-media-state.test.tsx +0 -316
- package/src/hooks/accessibility/index.ts +0 -27
- package/src/hooks/accessibility/use-accessibility-preferences.ts +0 -190
- package/src/hooks/accessibility/use-focus-management.ts +0 -183
- package/src/hooks/accessibility/use-keyboard-shortcuts.ts +0 -152
- package/src/hooks/accessibility/use-screen-reader.ts +0 -212
- package/src/hooks/core/index.ts +0 -47
- package/src/hooks/legacy.ts +0 -21
- package/src/hooks/use-buffer-health.ts +0 -218
- package/src/hooks/use-enhanced-media-player.ts +0 -575
- package/src/hooks/use-focus-trap.tsx +0 -162
- package/src/hooks/use-intelligent-preloading.ts +0 -387
- package/src/hooks/use-keyboard-controls.ts +0 -115
- package/src/hooks/use-keyboard-shortcuts.ts +0 -255
- package/src/hooks/use-media-actions.ts +0 -184
- package/src/hooks/use-media-metadata.ts +0 -91
- package/src/hooks/use-media-optimistic.ts +0 -264
- package/src/hooks/use-media-player.ts +0 -343
- package/src/hooks/use-media-state.ts +0 -194
- package/src/hooks/use-memory-optimization.ts +0 -360
- package/src/hooks/use-network-status.ts +0 -124
- package/src/hooks/use-optimistic-controls.ts +0 -95
- package/src/hooks/use-optimized-media.ts +0 -283
- package/src/hooks/use-smart-preload.ts +0 -229
- package/src/hooks/use-touch-gestures.ts +0 -461
- package/src/hooks/useEnhancedSubtitles.ts +0 -258
- package/src/hooks/useKeyboardShortcuts.ts +0 -106
- package/src/hooks/useMediaControls.ts +0 -52
- package/src/hooks/useScreenshot.ts +0 -77
- package/src/hooks/useSubtitles.ts +0 -91
- package/src/hooks/useVideoPlayer.ts +0 -278
- package/src/icons/components/FullscreenIcon.tsx +0 -36
- package/src/icons/components/SettingsIcon.tsx +0 -37
- package/src/icons/components/animated/LoadingIcon.tsx +0 -27
- package/src/icons/components/animated/PlayPauseIcon.tsx +0 -42
- package/src/icons/components/media/PauseIcon.tsx +0 -38
- package/src/icons/components/media/PlayIcon.tsx +0 -37
- package/src/icons/components/volume/VolumeIcon.tsx +0 -38
- package/src/icons/components/volume/VolumeOffIcon.tsx +0 -38
- package/src/icons/index.ts +0 -56
- package/src/icons/provider.tsx +0 -58
- package/src/icons/types.ts +0 -60
- package/src/index-enhanced.ts +0 -35
- package/src/index-legacy.ts +0 -324
- package/src/index.css +0 -102
- package/src/lib/__tests__/async-utils.test.ts +0 -291
- package/src/lib/async-utils.ts +0 -194
- package/src/lib/deprecation.ts +0 -51
- package/src/lib/format-time.ts +0 -37
- package/src/lib/index.ts +0 -4
- package/src/lib/utils.ts +0 -6
- package/src/styles/globals.css +0 -462
- package/src/styles/minimal-player.css +0 -621
- package/src/styles/pixel-art.css +0 -41
- package/src/styles/videojs.css +0 -100
- package/src/types/assets.d.ts +0 -29
- package/src/types/events.ts +0 -47
- package/src/types/formats.ts +0 -79
- package/src/types/index.ts +0 -203
- package/src/types/videojs-plugins.d.ts +0 -14
|
@@ -1,597 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import React from "react"
|
|
4
|
-
import { MediaActionTypes, useMediaDispatch, useMediaSelector } from "../../context/media-context"
|
|
5
|
-
import { formatTime } from "../../lib/format-time"
|
|
6
|
-
import { cn } from "../../lib/utils"
|
|
7
|
-
|
|
8
|
-
// Enhanced play button with better animations
|
|
9
|
-
export const EnhancedPlayButton = function EnhancedPlayButton({
|
|
10
|
-
className,
|
|
11
|
-
size = "md"
|
|
12
|
-
}: {
|
|
13
|
-
className?: string
|
|
14
|
-
size?: "sm" | "md" | "lg" | "xl"
|
|
15
|
-
}) {
|
|
16
|
-
const dispatch = useMediaDispatch()
|
|
17
|
-
const isPlaying = useMediaSelector((state) => state.isPlaying)
|
|
18
|
-
const isLoading = useMediaSelector((state) => state.isLoading)
|
|
19
|
-
|
|
20
|
-
const sizeClasses = {
|
|
21
|
-
sm: "h-8 w-8",
|
|
22
|
-
md: "h-10 w-10",
|
|
23
|
-
lg: "h-12 w-12",
|
|
24
|
-
xl: "h-16 w-16"
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const iconSizes = {
|
|
28
|
-
sm: "h-4 w-4",
|
|
29
|
-
md: "h-5 w-5",
|
|
30
|
-
lg: "h-6 w-6",
|
|
31
|
-
xl: "h-8 w-8"
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const handleClick = React.useCallback(() => {
|
|
35
|
-
dispatch({
|
|
36
|
-
type: isPlaying ? MediaActionTypes.PAUSE : MediaActionTypes.PLAY
|
|
37
|
-
})
|
|
38
|
-
}, [dispatch, isPlaying])
|
|
39
|
-
|
|
40
|
-
return (
|
|
41
|
-
<button
|
|
42
|
-
type="button"
|
|
43
|
-
onClick={handleClick}
|
|
44
|
-
disabled={isLoading}
|
|
45
|
-
className={cn(
|
|
46
|
-
"group relative flex items-center justify-center rounded-full bg-white/10 backdrop-blur-sm",
|
|
47
|
-
"border border-white/20 text-white transition-all duration-200",
|
|
48
|
-
"hover:scale-105 hover:bg-white/20 active:scale-95",
|
|
49
|
-
"focus:outline-none focus:ring-2 focus:ring-white/50",
|
|
50
|
-
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
51
|
-
sizeClasses[size],
|
|
52
|
-
className
|
|
53
|
-
)}
|
|
54
|
-
aria-label={isPlaying ? "Pause" : "Play"}>
|
|
55
|
-
{isLoading ? (
|
|
56
|
-
<div
|
|
57
|
-
className={cn(
|
|
58
|
-
"animate-spin rounded-full border-2 border-white/30 border-t-white",
|
|
59
|
-
iconSizes[size]
|
|
60
|
-
)}
|
|
61
|
-
/>
|
|
62
|
-
) : (
|
|
63
|
-
<div className={cn("transition-transform duration-200", iconSizes[size])}>
|
|
64
|
-
{isPlaying ? (
|
|
65
|
-
<svg viewBox="0 0 24 24" fill="currentColor" className="h-full w-full">
|
|
66
|
-
<path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z" />
|
|
67
|
-
</svg>
|
|
68
|
-
) : (
|
|
69
|
-
<svg viewBox="0 0 24 24" fill="currentColor" className="ml-0.5 h-full w-full">
|
|
70
|
-
<path d="M8 5v14l11-7z" />
|
|
71
|
-
</svg>
|
|
72
|
-
)}
|
|
73
|
-
</div>
|
|
74
|
-
)}
|
|
75
|
-
</button>
|
|
76
|
-
)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Enhanced seek bar with preview and better touch support
|
|
80
|
-
export const EnhancedSeekBar = function EnhancedSeekBar({
|
|
81
|
-
className
|
|
82
|
-
}: {
|
|
83
|
-
className?: string
|
|
84
|
-
}) {
|
|
85
|
-
const dispatch = useMediaDispatch()
|
|
86
|
-
const currentTime = useMediaSelector((state) => state.currentTime)
|
|
87
|
-
const duration = useMediaSelector((state) => state.duration)
|
|
88
|
-
const buffered = useMediaSelector((state) => state.buffered)
|
|
89
|
-
const previewTime = useMediaSelector((state) => state.previewTime)
|
|
90
|
-
|
|
91
|
-
const [isDragging, setIsDragging] = React.useState(false)
|
|
92
|
-
const [hoverTime, setHoverTime] = React.useState<number | null>(null)
|
|
93
|
-
const progressRef = React.useRef<HTMLDivElement>(null)
|
|
94
|
-
|
|
95
|
-
const progress = duration > 0 ? (currentTime / duration) * 100 : 0
|
|
96
|
-
const previewProgress =
|
|
97
|
-
duration > 0 && previewTime !== null ? (previewTime / duration) * 100 : null
|
|
98
|
-
|
|
99
|
-
// Calculate buffered ranges
|
|
100
|
-
const bufferedRanges = React.useMemo(() => {
|
|
101
|
-
if (!buffered || duration === 0) return []
|
|
102
|
-
const ranges = []
|
|
103
|
-
for (let i = 0; i < buffered.length; i++) {
|
|
104
|
-
ranges.push({
|
|
105
|
-
start: (buffered.start(i) / duration) * 100,
|
|
106
|
-
end: (buffered.end(i) / duration) * 100
|
|
107
|
-
})
|
|
108
|
-
}
|
|
109
|
-
return ranges
|
|
110
|
-
}, [buffered, duration])
|
|
111
|
-
|
|
112
|
-
const getTimeFromPosition = React.useCallback(
|
|
113
|
-
(clientX: number) => {
|
|
114
|
-
if (!progressRef.current) return 0
|
|
115
|
-
const rect = progressRef.current.getBoundingClientRect()
|
|
116
|
-
const position = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width))
|
|
117
|
-
return position * duration
|
|
118
|
-
},
|
|
119
|
-
[duration]
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
const handleMouseMove = React.useCallback(
|
|
123
|
-
(e: React.MouseEvent) => {
|
|
124
|
-
if (!isDragging) {
|
|
125
|
-
const time = getTimeFromPosition(e.clientX)
|
|
126
|
-
setHoverTime(time)
|
|
127
|
-
dispatch({ type: MediaActionTypes.PREVIEW_TIME, detail: time })
|
|
128
|
-
}
|
|
129
|
-
},
|
|
130
|
-
[isDragging, getTimeFromPosition, dispatch]
|
|
131
|
-
)
|
|
132
|
-
|
|
133
|
-
const handleMouseLeave = React.useCallback(() => {
|
|
134
|
-
if (!isDragging) {
|
|
135
|
-
setHoverTime(null)
|
|
136
|
-
dispatch({ type: MediaActionTypes.CLEAR_PREVIEW })
|
|
137
|
-
}
|
|
138
|
-
}, [isDragging, dispatch])
|
|
139
|
-
|
|
140
|
-
const handleMouseDown = React.useCallback(
|
|
141
|
-
(e: React.MouseEvent) => {
|
|
142
|
-
setIsDragging(true)
|
|
143
|
-
const time = getTimeFromPosition(e.clientX)
|
|
144
|
-
dispatch({ type: MediaActionTypes.SEEK, detail: time })
|
|
145
|
-
},
|
|
146
|
-
[getTimeFromPosition, dispatch]
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
const handleTouchStart = React.useCallback(
|
|
150
|
-
(e: React.TouchEvent) => {
|
|
151
|
-
setIsDragging(true)
|
|
152
|
-
const time = getTimeFromPosition(e.touches[0]?.clientX || 0)
|
|
153
|
-
dispatch({ type: MediaActionTypes.SEEK, detail: time })
|
|
154
|
-
},
|
|
155
|
-
[getTimeFromPosition, dispatch]
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
React.useEffect(() => {
|
|
159
|
-
const handleMouseMove = (e: MouseEvent) => {
|
|
160
|
-
if (isDragging) {
|
|
161
|
-
const time = getTimeFromPosition(e.clientX)
|
|
162
|
-
dispatch({ type: MediaActionTypes.SEEK, detail: time })
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const handleMouseUp = () => {
|
|
167
|
-
setIsDragging(false)
|
|
168
|
-
setHoverTime(null)
|
|
169
|
-
dispatch({ type: MediaActionTypes.CLEAR_PREVIEW })
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const handleTouchMove = (e: TouchEvent) => {
|
|
173
|
-
if (isDragging) {
|
|
174
|
-
const time = getTimeFromPosition(e.touches[0]?.clientX || 0)
|
|
175
|
-
dispatch({ type: MediaActionTypes.SEEK, detail: time })
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (isDragging) {
|
|
180
|
-
document.addEventListener("mousemove", handleMouseMove)
|
|
181
|
-
document.addEventListener("mouseup", handleMouseUp)
|
|
182
|
-
document.addEventListener("touchmove", handleTouchMove)
|
|
183
|
-
document.addEventListener("touchend", handleMouseUp)
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return () => {
|
|
187
|
-
document.removeEventListener("mousemove", handleMouseMove)
|
|
188
|
-
document.removeEventListener("mouseup", handleMouseUp)
|
|
189
|
-
document.removeEventListener("touchmove", handleTouchMove)
|
|
190
|
-
document.removeEventListener("touchend", handleMouseUp)
|
|
191
|
-
}
|
|
192
|
-
}, [isDragging, getTimeFromPosition, dispatch])
|
|
193
|
-
|
|
194
|
-
return (
|
|
195
|
-
<div className={cn("group relative flex items-center", className)}>
|
|
196
|
-
{/* Time preview tooltip */}
|
|
197
|
-
{(hoverTime !== null || previewTime !== null) && (
|
|
198
|
-
<div
|
|
199
|
-
className="pointer-events-none absolute bottom-full mb-2 rounded bg-black/80 px-2 py-1 text-white text-xs"
|
|
200
|
-
style={{
|
|
201
|
-
left: `${previewProgress || ((hoverTime || 0) / duration) * 100}%`,
|
|
202
|
-
transform: "translateX(-50%)"
|
|
203
|
-
}}>
|
|
204
|
-
{formatTime(hoverTime || previewTime || 0)}
|
|
205
|
-
</div>
|
|
206
|
-
)}
|
|
207
|
-
|
|
208
|
-
<div
|
|
209
|
-
ref={progressRef}
|
|
210
|
-
className="relative h-2 w-full cursor-pointer rounded-full bg-white/20 transition-all duration-200 group-hover:h-3"
|
|
211
|
-
onMouseMove={handleMouseMove}
|
|
212
|
-
onMouseLeave={handleMouseLeave}
|
|
213
|
-
onMouseDown={handleMouseDown}
|
|
214
|
-
onTouchStart={handleTouchStart}>
|
|
215
|
-
{/* Buffered ranges */}
|
|
216
|
-
{bufferedRanges.map((range, index) => (
|
|
217
|
-
<div
|
|
218
|
-
key={index}
|
|
219
|
-
className="absolute top-0 h-full rounded-full bg-white/30"
|
|
220
|
-
style={{
|
|
221
|
-
left: `${range.start}%`,
|
|
222
|
-
width: `${range.end - range.start}%`
|
|
223
|
-
}}
|
|
224
|
-
/>
|
|
225
|
-
))}
|
|
226
|
-
|
|
227
|
-
{/* Progress bar */}
|
|
228
|
-
<div
|
|
229
|
-
className="absolute top-0 h-full rounded-full bg-primary transition-all duration-100"
|
|
230
|
-
style={{ width: `${progress}%` }}
|
|
231
|
-
/>
|
|
232
|
-
|
|
233
|
-
{/* Preview indicator */}
|
|
234
|
-
{previewProgress !== null && (
|
|
235
|
-
<div
|
|
236
|
-
className="absolute top-0 h-full w-1 rounded-full bg-white/80"
|
|
237
|
-
style={{ left: `${previewProgress}%` }}
|
|
238
|
-
/>
|
|
239
|
-
)}
|
|
240
|
-
|
|
241
|
-
{/* Thumb */}
|
|
242
|
-
<div
|
|
243
|
-
className="-translate-y-1/2 absolute top-1/2 h-4 w-4 rounded-full border-2 border-white bg-primary opacity-0 shadow-lg transition-all duration-200 group-hover:opacity-100"
|
|
244
|
-
style={{ left: `${progress}%`, transform: "translateX(-50%) translateY(-50%)" }}
|
|
245
|
-
/>
|
|
246
|
-
</div>
|
|
247
|
-
</div>
|
|
248
|
-
)
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Enhanced volume control with better UX
|
|
252
|
-
export const EnhancedVolumeControl = function EnhancedVolumeControl({
|
|
253
|
-
className
|
|
254
|
-
}: {
|
|
255
|
-
className?: string
|
|
256
|
-
}) {
|
|
257
|
-
const dispatch = useMediaDispatch()
|
|
258
|
-
const volume = useMediaSelector((state) => state.volume)
|
|
259
|
-
const isMuted = useMediaSelector((state) => state.isMuted)
|
|
260
|
-
|
|
261
|
-
const [showSlider, setShowSlider] = React.useState(false)
|
|
262
|
-
|
|
263
|
-
const handleToggleMute = React.useCallback(() => {
|
|
264
|
-
dispatch({ type: MediaActionTypes.TOGGLE_MUTE })
|
|
265
|
-
}, [dispatch])
|
|
266
|
-
|
|
267
|
-
const handleVolumeChange = React.useCallback(
|
|
268
|
-
(newVolume: number) => {
|
|
269
|
-
dispatch({ type: MediaActionTypes.SET_VOLUME, detail: newVolume })
|
|
270
|
-
},
|
|
271
|
-
[dispatch]
|
|
272
|
-
)
|
|
273
|
-
|
|
274
|
-
const VolumeIcon = React.useMemo(() => {
|
|
275
|
-
if (isMuted || volume === 0) {
|
|
276
|
-
return (
|
|
277
|
-
<svg viewBox="0 0 24 24" fill="currentColor" className="h-5 w-5">
|
|
278
|
-
<path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z" />
|
|
279
|
-
</svg>
|
|
280
|
-
)
|
|
281
|
-
} else if (volume < 0.5) {
|
|
282
|
-
return (
|
|
283
|
-
<svg viewBox="0 0 24 24" fill="currentColor" className="h-5 w-5">
|
|
284
|
-
<path d="M18.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM5 9v6h4l5 5V4L9 9H5z" />
|
|
285
|
-
</svg>
|
|
286
|
-
)
|
|
287
|
-
} else {
|
|
288
|
-
return (
|
|
289
|
-
<svg viewBox="0 0 24 24" fill="currentColor" className="h-5 w-5">
|
|
290
|
-
<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z" />
|
|
291
|
-
</svg>
|
|
292
|
-
)
|
|
293
|
-
}
|
|
294
|
-
}, [isMuted, volume])
|
|
295
|
-
|
|
296
|
-
return (
|
|
297
|
-
<div
|
|
298
|
-
className={cn("relative flex items-center", className)}
|
|
299
|
-
onMouseEnter={() => setShowSlider(true)}
|
|
300
|
-
onMouseLeave={() => setShowSlider(false)}>
|
|
301
|
-
<button
|
|
302
|
-
type="button"
|
|
303
|
-
onClick={handleToggleMute}
|
|
304
|
-
className="flex h-10 w-10 items-center justify-center rounded-full text-white transition-colors hover:bg-white/10"
|
|
305
|
-
aria-label={isMuted ? "Unmute" : "Mute"}>
|
|
306
|
-
{VolumeIcon}
|
|
307
|
-
</button>
|
|
308
|
-
|
|
309
|
-
{/* Volume slider */}
|
|
310
|
-
<div
|
|
311
|
-
className={cn(
|
|
312
|
-
"absolute left-full ml-2 flex items-center transition-all duration-200",
|
|
313
|
-
showSlider ? "translate-x-0 opacity-100" : "-translate-x-2 pointer-events-none opacity-0"
|
|
314
|
-
)}>
|
|
315
|
-
<input
|
|
316
|
-
type="range"
|
|
317
|
-
min="0"
|
|
318
|
-
max="1"
|
|
319
|
-
step="0.01"
|
|
320
|
-
value={isMuted ? 0 : volume}
|
|
321
|
-
onChange={(e) => handleVolumeChange(parseFloat(e.target.value))}
|
|
322
|
-
className="slider h-1 w-20 cursor-pointer appearance-none rounded-lg bg-white/20"
|
|
323
|
-
/>
|
|
324
|
-
</div>
|
|
325
|
-
</div>
|
|
326
|
-
)
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Enhanced backward skip button
|
|
330
|
-
export const EnhancedBackwardButton = function EnhancedBackwardButton({
|
|
331
|
-
className,
|
|
332
|
-
size = "md",
|
|
333
|
-
skipTime = 10
|
|
334
|
-
}: {
|
|
335
|
-
className?: string
|
|
336
|
-
size?: "sm" | "md" | "lg"
|
|
337
|
-
skipTime?: number
|
|
338
|
-
}) {
|
|
339
|
-
const dispatch = useMediaDispatch()
|
|
340
|
-
const currentTime = useMediaSelector((state) => state.currentTime)
|
|
341
|
-
|
|
342
|
-
const sizeClasses = {
|
|
343
|
-
sm: "h-8 w-8",
|
|
344
|
-
md: "h-10 w-10",
|
|
345
|
-
lg: "h-12 w-12"
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const iconSizes = {
|
|
349
|
-
sm: "h-4 w-4",
|
|
350
|
-
md: "h-5 w-5",
|
|
351
|
-
lg: "h-6 w-6"
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
const handleClick = React.useCallback(() => {
|
|
355
|
-
const newTime = Math.max(0, currentTime - skipTime)
|
|
356
|
-
dispatch({ type: MediaActionTypes.SEEK, detail: newTime })
|
|
357
|
-
}, [dispatch, currentTime, skipTime])
|
|
358
|
-
|
|
359
|
-
return (
|
|
360
|
-
<button
|
|
361
|
-
type="button"
|
|
362
|
-
onClick={handleClick}
|
|
363
|
-
className={cn(
|
|
364
|
-
"group relative flex items-center justify-center rounded-full bg-white/10 backdrop-blur-sm",
|
|
365
|
-
"border border-white/20 text-white transition-all duration-200",
|
|
366
|
-
"hover:scale-105 hover:bg-white/20 active:scale-95",
|
|
367
|
-
"focus:outline-none focus:ring-2 focus:ring-white/50",
|
|
368
|
-
sizeClasses[size],
|
|
369
|
-
className
|
|
370
|
-
)}
|
|
371
|
-
aria-label={`Backward ${skipTime} seconds`}
|
|
372
|
-
title={`Backward ${skipTime}s`}>
|
|
373
|
-
<div className={cn("flex items-center transition-transform duration-200", iconSizes[size])}>
|
|
374
|
-
<svg viewBox="0 0 24 24" fill="currentColor" className="h-full w-full">
|
|
375
|
-
<path d="M7 6c.55 0 1 .45 1 1v10c0 .55-.45 1-1 1s-1-.45-1-1V7c0-.55.45-1 1-1zM15.5 7.5c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v9c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5v-9z" />
|
|
376
|
-
<path d="M5.5 12l7-4.5v9l-7-4.5z" opacity="0.8" />
|
|
377
|
-
</svg>
|
|
378
|
-
{skipTime && <span className="-bottom-1 absolute font-bold text-[10px]">{skipTime}</span>}
|
|
379
|
-
</div>
|
|
380
|
-
</button>
|
|
381
|
-
)
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// Enhanced forward skip button
|
|
385
|
-
export const EnhancedForwardButton = function EnhancedForwardButton({
|
|
386
|
-
className,
|
|
387
|
-
size = "md",
|
|
388
|
-
skipTime = 10
|
|
389
|
-
}: {
|
|
390
|
-
className?: string
|
|
391
|
-
size?: "sm" | "md" | "lg"
|
|
392
|
-
skipTime?: number
|
|
393
|
-
}) {
|
|
394
|
-
const dispatch = useMediaDispatch()
|
|
395
|
-
const currentTime = useMediaSelector((state) => state.currentTime)
|
|
396
|
-
const duration = useMediaSelector((state) => state.duration)
|
|
397
|
-
|
|
398
|
-
const sizeClasses = {
|
|
399
|
-
sm: "h-8 w-8",
|
|
400
|
-
md: "h-10 w-10",
|
|
401
|
-
lg: "h-12 w-12"
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
const iconSizes = {
|
|
405
|
-
sm: "h-4 w-4",
|
|
406
|
-
md: "h-5 w-5",
|
|
407
|
-
lg: "h-6 w-6"
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
const handleClick = React.useCallback(() => {
|
|
411
|
-
const newTime = Math.min(duration, currentTime + skipTime)
|
|
412
|
-
dispatch({ type: MediaActionTypes.SEEK, detail: newTime })
|
|
413
|
-
}, [dispatch, currentTime, duration, skipTime])
|
|
414
|
-
|
|
415
|
-
return (
|
|
416
|
-
<button
|
|
417
|
-
type="button"
|
|
418
|
-
onClick={handleClick}
|
|
419
|
-
className={cn(
|
|
420
|
-
"group relative flex items-center justify-center rounded-full bg-white/10 backdrop-blur-sm",
|
|
421
|
-
"border border-white/20 text-white transition-all duration-200",
|
|
422
|
-
"hover:scale-105 hover:bg-white/20 active:scale-95",
|
|
423
|
-
"focus:outline-none focus:ring-2 focus:ring-white/50",
|
|
424
|
-
sizeClasses[size],
|
|
425
|
-
className
|
|
426
|
-
)}
|
|
427
|
-
aria-label={`Forward ${skipTime} seconds`}
|
|
428
|
-
title={`Forward ${skipTime}s`}>
|
|
429
|
-
<div className={cn("flex items-center transition-transform duration-200", iconSizes[size])}>
|
|
430
|
-
<svg viewBox="0 0 24 24" fill="currentColor" className="h-full w-full">
|
|
431
|
-
<path d="M17 6c.55 0 1 .45 1 1v10c0 .55-.45 1-1 1s-1-.45-1-1V7c0-.55.45-1 1-1zM8.5 7.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5v9c0 .83-.67 1.5-1.5 1.5s-1.5-.67-1.5-1.5v-9z" />
|
|
432
|
-
<path d="M18.5 12l-7-4.5v9l7-4.5z" opacity="0.8" />
|
|
433
|
-
</svg>
|
|
434
|
-
{skipTime && <span className="-bottom-1 absolute font-bold text-[10px]">{skipTime}</span>}
|
|
435
|
-
</div>
|
|
436
|
-
</button>
|
|
437
|
-
)
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// Enhanced time display
|
|
441
|
-
export const EnhancedTimeDisplay = function EnhancedTimeDisplay({
|
|
442
|
-
variant = "current",
|
|
443
|
-
className
|
|
444
|
-
}: {
|
|
445
|
-
variant?: "current" | "duration" | "remaining"
|
|
446
|
-
className?: string
|
|
447
|
-
}) {
|
|
448
|
-
const currentTime = useMediaSelector((state) => state.currentTime)
|
|
449
|
-
const duration = useMediaSelector((state) => state.duration)
|
|
450
|
-
|
|
451
|
-
const displayTime = React.useMemo(() => {
|
|
452
|
-
switch (variant) {
|
|
453
|
-
case "current":
|
|
454
|
-
return formatTime(currentTime)
|
|
455
|
-
case "duration":
|
|
456
|
-
return formatTime(duration)
|
|
457
|
-
case "remaining":
|
|
458
|
-
return `-${formatTime(duration - currentTime)}`
|
|
459
|
-
default:
|
|
460
|
-
return formatTime(currentTime)
|
|
461
|
-
}
|
|
462
|
-
}, [variant, currentTime, duration])
|
|
463
|
-
|
|
464
|
-
return (
|
|
465
|
-
<div className={cn("font-mono text-sm text-white tabular-nums", className)}>{displayTime}</div>
|
|
466
|
-
)
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
// Performance indicator component
|
|
470
|
-
export const PerformanceIndicator = function PerformanceIndicator({
|
|
471
|
-
metrics,
|
|
472
|
-
className
|
|
473
|
-
}: {
|
|
474
|
-
metrics?: {
|
|
475
|
-
fps?: number
|
|
476
|
-
bufferHealth?: number
|
|
477
|
-
quality?: number
|
|
478
|
-
bandwidth?: number
|
|
479
|
-
}
|
|
480
|
-
className?: string
|
|
481
|
-
}) {
|
|
482
|
-
if (!metrics || process.env.NODE_ENV !== "development") {
|
|
483
|
-
return null
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
const getPerformanceColor = (fps: number) => {
|
|
487
|
-
if (fps >= 50) return "text-green-400"
|
|
488
|
-
if (fps >= 30) return "text-yellow-400"
|
|
489
|
-
return "text-red-400"
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
const getBufferColor = (health: number) => {
|
|
493
|
-
if (health >= 5) return "text-green-400"
|
|
494
|
-
if (health >= 2) return "text-yellow-400"
|
|
495
|
-
return "text-red-400"
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
return (
|
|
499
|
-
<div className={cn("flex items-center gap-3 font-mono text-xs", className)}>
|
|
500
|
-
{metrics.fps !== undefined && (
|
|
501
|
-
<div className={cn("flex items-center gap-1", getPerformanceColor(metrics.fps))}>
|
|
502
|
-
<div className="h-2 w-2 rounded-full bg-current" />
|
|
503
|
-
<span>{metrics.fps} FPS</span>
|
|
504
|
-
</div>
|
|
505
|
-
)}
|
|
506
|
-
{metrics.bufferHealth !== undefined && (
|
|
507
|
-
<div className={cn("flex items-center gap-1", getBufferColor(metrics.bufferHealth))}>
|
|
508
|
-
<div className="h-2 w-2 rounded-full bg-current" />
|
|
509
|
-
<span>{metrics.bufferHealth.toFixed(1)}s</span>
|
|
510
|
-
</div>
|
|
511
|
-
)}
|
|
512
|
-
{metrics.quality !== undefined && (
|
|
513
|
-
<div className="flex items-center gap-1 text-blue-400">
|
|
514
|
-
<div className="h-2 w-2 rounded-full bg-current" />
|
|
515
|
-
<span>{metrics.quality}p</span>
|
|
516
|
-
</div>
|
|
517
|
-
)}
|
|
518
|
-
</div>
|
|
519
|
-
)
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
// Enhanced control bar that combines all controls
|
|
523
|
-
export const EnhancedControlBar = function EnhancedControlBar({
|
|
524
|
-
className,
|
|
525
|
-
showSkipButtons = true,
|
|
526
|
-
showVolumeControl = true,
|
|
527
|
-
showTimeDisplay = true,
|
|
528
|
-
playButtonSize = "xl",
|
|
529
|
-
skipButtonSize = "md",
|
|
530
|
-
skipTime = 10,
|
|
531
|
-
showPerformanceIndicators = process.env.NODE_ENV === "development",
|
|
532
|
-
performanceMetrics
|
|
533
|
-
}: {
|
|
534
|
-
className?: string
|
|
535
|
-
showSkipButtons?: boolean
|
|
536
|
-
showVolumeControl?: boolean
|
|
537
|
-
showTimeDisplay?: boolean
|
|
538
|
-
playButtonSize?: "sm" | "md" | "lg" | "xl"
|
|
539
|
-
skipButtonSize?: "sm" | "md" | "lg"
|
|
540
|
-
skipTime?: number
|
|
541
|
-
showPerformanceIndicators?: boolean
|
|
542
|
-
performanceMetrics?: {
|
|
543
|
-
fps?: number
|
|
544
|
-
bufferHealth?: number
|
|
545
|
-
quality?: number
|
|
546
|
-
bandwidth?: number
|
|
547
|
-
}
|
|
548
|
-
}) {
|
|
549
|
-
return (
|
|
550
|
-
<div
|
|
551
|
-
className={cn(
|
|
552
|
-
"flex flex-col gap-4 bg-gradient-to-t from-black/80 to-transparent p-4",
|
|
553
|
-
className
|
|
554
|
-
)}>
|
|
555
|
-
{/* Seek Bar */}
|
|
556
|
-
<EnhancedSeekBar className="w-full" />
|
|
557
|
-
|
|
558
|
-
{/* Control Buttons */}
|
|
559
|
-
<div className="flex items-center justify-between">
|
|
560
|
-
{/* Left side - Media controls */}
|
|
561
|
-
<div className="flex items-center gap-3">
|
|
562
|
-
{showSkipButtons && (
|
|
563
|
-
<EnhancedBackwardButton
|
|
564
|
-
size={skipButtonSize}
|
|
565
|
-
skipTime={skipTime}
|
|
566
|
-
className="shrink-0"
|
|
567
|
-
/>
|
|
568
|
-
)}
|
|
569
|
-
|
|
570
|
-
<EnhancedPlayButton size={playButtonSize} className="shrink-0" />
|
|
571
|
-
|
|
572
|
-
{showSkipButtons && (
|
|
573
|
-
<EnhancedForwardButton size={skipButtonSize} skipTime={skipTime} className="shrink-0" />
|
|
574
|
-
)}
|
|
575
|
-
|
|
576
|
-
{showVolumeControl && <EnhancedVolumeControl className="shrink-0" />}
|
|
577
|
-
|
|
578
|
-
{showTimeDisplay && (
|
|
579
|
-
<div className="flex items-center gap-1 font-mono text-sm">
|
|
580
|
-
<EnhancedTimeDisplay variant="current" />
|
|
581
|
-
<span className="text-white/60">/</span>
|
|
582
|
-
<EnhancedTimeDisplay variant="duration" />
|
|
583
|
-
</div>
|
|
584
|
-
)}
|
|
585
|
-
</div>
|
|
586
|
-
|
|
587
|
-
{/* Right side - Performance indicators and additional controls */}
|
|
588
|
-
<div className="flex items-center gap-2">
|
|
589
|
-
{showPerformanceIndicators && (
|
|
590
|
-
<PerformanceIndicator metrics={performanceMetrics} className="text-white/80" />
|
|
591
|
-
)}
|
|
592
|
-
{/* Future: Settings, Fullscreen, etc. */}
|
|
593
|
-
</div>
|
|
594
|
-
</div>
|
|
595
|
-
</div>
|
|
596
|
-
)
|
|
597
|
-
}
|