@therealtinhtute/baroiplayer 1.0.0

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 (244) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +384 -0
  3. package/dist/context/media.js +2 -0
  4. package/dist/context/media.js.map +1 -0
  5. package/dist/context/media.mjs +15 -0
  6. package/dist/context/media.mjs.map +1 -0
  7. package/dist/context/store.js +2 -0
  8. package/dist/context/store.js.map +1 -0
  9. package/dist/context/store.mjs +5 -0
  10. package/dist/context/store.mjs.map +1 -0
  11. package/dist/context-BMteAJDN.js +1140 -0
  12. package/dist/context-BMteAJDN.js.map +1 -0
  13. package/dist/context-DCJ_scmO.cjs +23 -0
  14. package/dist/context-DCJ_scmO.cjs.map +1 -0
  15. package/dist/core-events-B5W_j8yU.js +480 -0
  16. package/dist/core-events-B5W_j8yU.js.map +1 -0
  17. package/dist/core-events-CN0-Vhhq.cjs +2 -0
  18. package/dist/core-events-CN0-Vhhq.cjs.map +1 -0
  19. package/dist/core-plugins-Bq9KLany.js +696 -0
  20. package/dist/core-plugins-Bq9KLany.js.map +1 -0
  21. package/dist/core-plugins-CPDSHMiQ.cjs +2 -0
  22. package/dist/core-plugins-CPDSHMiQ.cjs.map +1 -0
  23. package/dist/core.js +37 -0
  24. package/dist/core.js.map +1 -0
  25. package/dist/core.mjs +1217 -0
  26. package/dist/core.mjs.map +1 -0
  27. package/dist/enhanced-components-DHv0MFvG.js +1073 -0
  28. package/dist/enhanced-components-DHv0MFvG.js.map +1 -0
  29. package/dist/enhanced-components-DTIObgB7.cjs +2 -0
  30. package/dist/enhanced-components-DTIObgB7.cjs.map +1 -0
  31. package/dist/hooks-C_r8wexn.cjs +4 -0
  32. package/dist/hooks-C_r8wexn.cjs.map +1 -0
  33. package/dist/hooks-Da2wVXlF.js +1630 -0
  34. package/dist/hooks-Da2wVXlF.js.map +1 -0
  35. package/dist/index.css +1 -0
  36. package/dist/index.js +112 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/index.mjs +6126 -0
  39. package/dist/index.mjs.map +1 -0
  40. package/dist/minimal-components-DSq5sQpy.js +2002 -0
  41. package/dist/minimal-components-DSq5sQpy.js.map +1 -0
  42. package/dist/minimal-components-DflWiihN.cjs +2 -0
  43. package/dist/minimal-components-DflWiihN.cjs.map +1 -0
  44. package/dist/minimal-components.css +1 -0
  45. package/dist/mona-loading.gif +0 -0
  46. package/dist/performance-monitor-CUW6f5ll.cjs +2 -0
  47. package/dist/performance-monitor-CUW6f5ll.cjs.map +1 -0
  48. package/dist/performance-monitor-rMW1RgV3.js +302 -0
  49. package/dist/performance-monitor-rMW1RgV3.js.map +1 -0
  50. package/dist/utils-BnscpYYd.js +2798 -0
  51. package/dist/utils-BnscpYYd.js.map +1 -0
  52. package/dist/utils-D_czS_0K.cjs +9 -0
  53. package/dist/utils-D_czS_0K.cjs.map +1 -0
  54. package/dist/utils.js +2 -0
  55. package/dist/utils.js.map +1 -0
  56. package/dist/utils.mjs +7 -0
  57. package/dist/utils.mjs.map +1 -0
  58. package/package.json +114 -0
  59. package/src/adapters/videojs-adapter.tsx +470 -0
  60. package/src/assets/mona-loading.gif +0 -0
  61. package/src/components/AnimateLoading.tsx +115 -0
  62. package/src/components/LoadingState.tsx +248 -0
  63. package/src/components/accessibility/keyboard-help-overlay.tsx +213 -0
  64. package/src/components/audio-player.tsx +85 -0
  65. package/src/components/basic/index.ts +37 -0
  66. package/src/components/enhanced/advanced-features.tsx +316 -0
  67. package/src/components/enhanced/debug-panel.tsx +422 -0
  68. package/src/components/enhanced/enhanced-controls.tsx +600 -0
  69. package/src/components/enhanced/enhanced-media-player.tsx +509 -0
  70. package/src/components/enhanced/error-handling.tsx +345 -0
  71. package/src/components/enhanced/index.ts +39 -0
  72. package/src/components/enhanced/media-player-enhanced.tsx +349 -0
  73. package/src/components/examples/basic-video-player.tsx +55 -0
  74. package/src/components/examples/custom-controls-example.tsx +63 -0
  75. package/src/components/examples/enhanced-controls-example.tsx +114 -0
  76. package/src/components/examples/enhanced-media-player-example.tsx +293 -0
  77. package/src/components/examples/loading-states-demo.tsx +142 -0
  78. package/src/components/examples/react-19-demo.tsx +381 -0
  79. package/src/components/examples/volume-orientation-demo.tsx +114 -0
  80. package/src/components/icon-demo.tsx +209 -0
  81. package/src/components/icons/index.tsx +166 -0
  82. package/src/components/icons/svg-icons/ArrowLeftIcon.tsx +18 -0
  83. package/src/components/icons/svg-icons/ArrowRightIcon.tsx +18 -0
  84. package/src/components/icons/svg-icons/AudioIcon.tsx +22 -0
  85. package/src/components/icons/svg-icons/BackwardIcon.tsx +12 -0
  86. package/src/components/icons/svg-icons/CameraIcon.tsx +19 -0
  87. package/src/components/icons/svg-icons/CheckIcon.tsx +19 -0
  88. package/src/components/icons/svg-icons/EnablePiPIcon.tsx +17 -0
  89. package/src/components/icons/svg-icons/ForwardIcon.tsx +12 -0
  90. package/src/components/icons/svg-icons/FullscreenEnterIcon.tsx +23 -0
  91. package/src/components/icons/svg-icons/FullscreenExitIcon.tsx +23 -0
  92. package/src/components/icons/svg-icons/LoadingIcon.tsx +32 -0
  93. package/src/components/icons/svg-icons/PauseIcon.tsx +23 -0
  94. package/src/components/icons/svg-icons/PlayIcon.tsx +21 -0
  95. package/src/components/icons/svg-icons/PlaybackSpeedIcon.tsx +23 -0
  96. package/src/components/icons/svg-icons/QualityIcon.tsx +21 -0
  97. package/src/components/icons/svg-icons/SettingsIcon.tsx +12 -0
  98. package/src/components/icons/svg-icons/SliderIcon.tsx +21 -0
  99. package/src/components/icons/svg-icons/SubtitleIcon.tsx +21 -0
  100. package/src/components/icons/svg-icons/SubtitleOffIcon.tsx +25 -0
  101. package/src/components/icons/svg-icons/UnPiPIcon.tsx +17 -0
  102. package/src/components/icons/svg-icons/VolumeMutedIcon.tsx +16 -0
  103. package/src/components/icons/svg-icons/VolumeOneIcon.tsx +22 -0
  104. package/src/components/icons/svg-icons/VolumeThreeIcon.tsx +16 -0
  105. package/src/components/icons/svg-icons/VolumeTwoIcon.tsx +16 -0
  106. package/src/components/legacy.tsx +93 -0
  107. package/src/components/media-metadata-display.tsx +100 -0
  108. package/src/components/media-player-audio.tsx +39 -0
  109. package/src/components/media-player-controls.tsx +226 -0
  110. package/src/components/media-player-error.tsx +163 -0
  111. package/src/components/media-player-fullscreen.tsx +109 -0
  112. package/src/components/media-player-hls.tsx +111 -0
  113. package/src/components/media-player-loading.tsx +76 -0
  114. package/src/components/media-player-play.tsx +75 -0
  115. package/src/components/media-player-root.tsx +340 -0
  116. package/src/components/media-player-seek.tsx +168 -0
  117. package/src/components/media-player-time.tsx +99 -0
  118. package/src/components/media-player-tooltip.tsx +58 -0
  119. package/src/components/media-player-video.tsx +39 -0
  120. package/src/components/media-player-volume.tsx +155 -0
  121. package/src/components/media-player.tsx +257 -0
  122. package/src/components/media-suspense.tsx +362 -0
  123. package/src/components/player-settings-form.tsx +163 -0
  124. package/src/components/react-player-wrapper.tsx +651 -0
  125. package/src/components/simple-video-player.tsx +60 -0
  126. package/src/components/ui/enhanced-seek-bar.tsx +235 -0
  127. package/src/components/ui/fullscreen-button.tsx +60 -0
  128. package/src/components/ui/media-resource-preloader.tsx +395 -0
  129. package/src/components/ui/play-button.tsx +107 -0
  130. package/src/components/ui/seek-bar.tsx +161 -0
  131. package/src/components/ui/time-display.tsx +40 -0
  132. package/src/components/ui/volume-control.tsx +143 -0
  133. package/src/components/unified/base-media-player.tsx +246 -0
  134. package/src/components/unified/media-player.tsx +75 -0
  135. package/src/components/variants/minimal/AccessibleMinimalPlayer.tsx +561 -0
  136. package/src/components/variants/minimal/EnhancedMinimalMediaPlayer.tsx +552 -0
  137. package/src/components/variants/minimal/MinimalControls.tsx +142 -0
  138. package/src/components/variants/minimal/MinimalLoading.tsx +32 -0
  139. package/src/components/variants/minimal/MinimalMediaPlayer.tsx +315 -0
  140. package/src/components/variants/minimal/MinimalOverlay.tsx +64 -0
  141. package/src/components/variants/minimal/MinimalPlayButton.tsx +65 -0
  142. package/src/components/variants/minimal/MinimalSeekBar.tsx +150 -0
  143. package/src/components/variants/minimal/MinimalVolumeControl.tsx +80 -0
  144. package/src/components/variants/minimal/SimpleMinimalPlayer.tsx +210 -0
  145. package/src/components/variants/minimal/components/CenterPlayButton.tsx +22 -0
  146. package/src/components/variants/minimal/components/ControlBar.tsx +146 -0
  147. package/src/components/variants/minimal/components/EnhancedSettingsMenu.tsx +361 -0
  148. package/src/components/variants/minimal/components/PlaybackControls.tsx +51 -0
  149. package/src/components/variants/minimal/components/RightControls.tsx +173 -0
  150. package/src/components/variants/minimal/components/SeekBar.tsx +131 -0
  151. package/src/components/variants/minimal/components/SettingsDropdown.tsx +70 -0
  152. package/src/components/variants/minimal/components/SubtitleDisplay.tsx +21 -0
  153. package/src/components/variants/minimal/components/TimeDisplay.tsx +18 -0
  154. package/src/components/variants/minimal/components/VideoElement.tsx +40 -0
  155. package/src/components/variants/minimal/components/VolumeControl.tsx +217 -0
  156. package/src/components/variants/minimal/index.ts +23 -0
  157. package/src/components/video-player.tsx +100 -0
  158. package/src/context/error-handling.ts +394 -0
  159. package/src/context/media-context.tsx +208 -0
  160. package/src/context/media-provider.tsx +265 -0
  161. package/src/context/media-store.ts +379 -0
  162. package/src/context/persistence.ts +453 -0
  163. package/src/core/events/event-system.ts +442 -0
  164. package/src/core/events/hooks.ts +302 -0
  165. package/src/core/events/index.ts +22 -0
  166. package/src/core/events/middleware.ts +269 -0
  167. package/src/core/index.ts +22 -0
  168. package/src/core/optimization/bundle-optimizer.tsx +308 -0
  169. package/src/core/performance/cicd.ts +96 -0
  170. package/src/core/performance/index.ts +38 -0
  171. package/src/core/performance/media-loading-optimizer.ts +538 -0
  172. package/src/core/performance/performance-budgets.ts +432 -0
  173. package/src/core/performance/performance-monitor.tsx +476 -0
  174. package/src/core/performance/performance-testing.ts +459 -0
  175. package/src/core/plugins/base-plugin.ts +224 -0
  176. package/src/core/plugins/enhanced-plugin-system.ts +411 -0
  177. package/src/core/plugins/examples/analytics-plugin.ts +242 -0
  178. package/src/core/plugins/index.ts +39 -0
  179. package/src/core/plugins/registry.ts +327 -0
  180. package/src/core/plugins/types.ts +159 -0
  181. package/src/core/plugins/utils.ts +316 -0
  182. package/src/core/progressive-loading.tsx +322 -0
  183. package/src/core/providers/enhanced-hls-provider.ts +339 -0
  184. package/src/core/providers/service-provider.tsx +168 -0
  185. package/src/core/utils.tsx +38 -0
  186. package/src/features/media-session/media-session-manager.ts +429 -0
  187. package/src/features/picture-in-picture/pip-manager.ts +429 -0
  188. package/src/features/playlist/playlist-manager.ts +606 -0
  189. package/src/features/subtitles/subtitle-manager.ts +441 -0
  190. package/src/features/subtitles/subtitle-parser.ts +280 -0
  191. package/src/features/thumbnails/thumbnail-generator.ts +519 -0
  192. package/src/hooks/accessibility/index.ts +27 -0
  193. package/src/hooks/accessibility/use-accessibility-preferences.ts +188 -0
  194. package/src/hooks/accessibility/use-focus-management.ts +176 -0
  195. package/src/hooks/accessibility/use-keyboard-shortcuts.ts +144 -0
  196. package/src/hooks/accessibility/use-screen-reader.ts +182 -0
  197. package/src/hooks/core/index.ts +47 -0
  198. package/src/hooks/legacy.ts +27 -0
  199. package/src/hooks/use-enhanced-media-player.ts +468 -0
  200. package/src/hooks/use-intelligent-preloading.ts +358 -0
  201. package/src/hooks/use-keyboard-controls.ts +115 -0
  202. package/src/hooks/use-media-actions.ts +175 -0
  203. package/src/hooks/use-media-metadata.ts +89 -0
  204. package/src/hooks/use-media-optimistic.ts +270 -0
  205. package/src/hooks/use-media-player.ts +312 -0
  206. package/src/hooks/use-media-state.ts +190 -0
  207. package/src/hooks/use-memory-optimization.ts +358 -0
  208. package/src/hooks/use-optimistic-controls.ts +92 -0
  209. package/src/hooks/use-optimized-media.ts +263 -0
  210. package/src/hooks/use-touch-gestures.ts +417 -0
  211. package/src/hooks/useEnhancedSubtitles.ts +258 -0
  212. package/src/hooks/useKeyboardShortcuts.ts +74 -0
  213. package/src/hooks/useMediaControls.ts +52 -0
  214. package/src/hooks/useScreenshot.ts +57 -0
  215. package/src/hooks/useSubtitles.ts +91 -0
  216. package/src/hooks/useVideoPlayer.ts +232 -0
  217. package/src/icons/components/FullscreenIcon.tsx +37 -0
  218. package/src/icons/components/SettingsIcon.tsx +38 -0
  219. package/src/icons/components/animated/LoadingIcon.tsx +43 -0
  220. package/src/icons/components/animated/PlayPauseIcon.tsx +50 -0
  221. package/src/icons/components/media/PauseIcon.tsx +39 -0
  222. package/src/icons/components/media/PlayIcon.tsx +38 -0
  223. package/src/icons/components/volume/VolumeIcon.tsx +39 -0
  224. package/src/icons/components/volume/VolumeOffIcon.tsx +39 -0
  225. package/src/icons/index.ts +56 -0
  226. package/src/icons/provider.tsx +64 -0
  227. package/src/icons/types.ts +40 -0
  228. package/src/index-enhanced.ts +35 -0
  229. package/src/index-legacy.ts +326 -0
  230. package/src/index.css +102 -0
  231. package/src/index.ts +216 -0
  232. package/src/lib/deprecation.ts +51 -0
  233. package/src/lib/format-time.ts +37 -0
  234. package/src/lib/index.ts +3 -0
  235. package/src/lib/utils.ts +6 -0
  236. package/src/styles/globals.css +462 -0
  237. package/src/styles/minimal-player.css +589 -0
  238. package/src/styles/pixel-art.css +46 -0
  239. package/src/styles/videojs.css +100 -0
  240. package/src/test/setup.ts +79 -0
  241. package/src/types/events.ts +47 -0
  242. package/src/types/formats.ts +95 -0
  243. package/src/types/index.ts +190 -0
  244. package/src/types/videojs-plugins.d.ts +14 -0
@@ -0,0 +1,248 @@
1
+ "use client"
2
+
3
+ import React from "react"
4
+ import { cn } from "../lib/utils"
5
+ import { AnimateLoading } from "./AnimateLoading"
6
+
7
+ export type LoadingVariant = "animate" | "spinner" | "skeleton" | "dots"
8
+ export type LoadingContext = "initial" | "seeking" | "buffering"
9
+ export type LoadingSize = "sm" | "md" | "lg" | "xl"
10
+
11
+ export interface LoadingStateProps {
12
+ variant?: LoadingVariant
13
+ context?: LoadingContext
14
+ size?: LoadingSize
15
+ color?: string
16
+ message?: string
17
+ showMessage?: boolean
18
+ className?: string
19
+ style?: React.CSSProperties
20
+ }
21
+
22
+ const sizeMap: Record<LoadingSize, { container: string; spinner: string; dots: string }> = {
23
+ sm: {
24
+ container: "h-8 w-8",
25
+ spinner: "h-6 w-6 border-2",
26
+ dots: "h-1.5 w-1.5"
27
+ },
28
+ md: {
29
+ container: "h-12 w-12",
30
+ spinner: "h-10 w-10 border-3",
31
+ dots: "h-2 w-2"
32
+ },
33
+ lg: {
34
+ container: "h-16 w-16",
35
+ spinner: "h-14 w-14 border-4",
36
+ dots: "h-2.5 w-2.5"
37
+ },
38
+ xl: {
39
+ container: "h-20 w-20",
40
+ spinner: "h-18 w-18 border-4",
41
+ dots: "h-3 w-3"
42
+ }
43
+ }
44
+
45
+ const contextStyles: Record<LoadingContext, { wrapper: string; background: string }> = {
46
+ initial: {
47
+ wrapper: "absolute inset-0 z-50 flex items-center justify-center",
48
+ background: "bg-black/80"
49
+ },
50
+ seeking: {
51
+ wrapper: "absolute inset-0 z-40 flex items-center justify-center",
52
+ background: "bg-black/40"
53
+ },
54
+ buffering: {
55
+ wrapper: "absolute inset-0 z-45 flex items-center justify-center",
56
+ background: "bg-black/60"
57
+ }
58
+ }
59
+
60
+ // Default AnimateLoading wrapper component
61
+ export const AnimateLoadingWrapper = function AnimateLoadingWrapper({
62
+ context = "initial",
63
+ message,
64
+ className,
65
+ textSize = "text-sm"
66
+ }: {
67
+ context?: LoadingContext
68
+ message?: string
69
+ className?: string
70
+ textSize?: string
71
+ }) {
72
+ const contextStyle = contextStyles[context]
73
+
74
+ const getLoadingText = () => {
75
+ if (message) return message
76
+
77
+ switch (context) {
78
+ case "initial":
79
+ return "Loading video..."
80
+ case "seeking":
81
+ return "Seeking..."
82
+ case "buffering":
83
+ return "Buffering..."
84
+ default:
85
+ return "Loading..."
86
+ }
87
+ }
88
+
89
+ return (
90
+ <div className={cn(contextStyle.wrapper, contextStyle.background, className)}>
91
+ <AnimateLoading
92
+ loadingText={getLoadingText()}
93
+ className={cn("text-white", textSize)}
94
+ />
95
+ </div>
96
+ )
97
+ }
98
+
99
+ export const LoadingState = function LoadingState({
100
+ variant = "animate",
101
+ context = "initial",
102
+ size = "md",
103
+ color,
104
+ message,
105
+ showMessage = true,
106
+ className,
107
+ style
108
+ }: LoadingStateProps) {
109
+ const sizes = sizeMap[size]
110
+ const contextStyle = contextStyles[context]
111
+
112
+ // Use AnimateLoading as default
113
+ if (variant === "animate") {
114
+ return (
115
+ <AnimateLoadingWrapper
116
+ context={context}
117
+ message={message}
118
+ className={className}
119
+ textSize={size === "sm" ? "text-xs" : size === "lg" ? "text-base" : "text-sm"}
120
+ />
121
+ )
122
+ }
123
+
124
+ const renderLoadingIndicator = () => {
125
+ switch (variant) {
126
+ case "spinner":
127
+ return (
128
+ <div
129
+ className={cn(
130
+ "minimal-loading-spinner animate-spin rounded-full",
131
+ "border-white/20 border-t-cyan-500",
132
+ sizes.spinner,
133
+ className
134
+ )}
135
+ style={{
136
+ borderTopColor: color || undefined,
137
+ ...style
138
+ }}
139
+ />
140
+ )
141
+
142
+ case "dots":
143
+ return (
144
+ <div className="flex items-center gap-1">
145
+ {[0, 1, 2].map((i) => (
146
+ <div
147
+ key={i}
148
+ className={cn(
149
+ "minimal-loading-dot rounded-full bg-cyan-500",
150
+ sizes.dots,
151
+ className
152
+ )}
153
+ style={{
154
+ backgroundColor: color || undefined,
155
+ animationDelay: `${i * 150}ms`,
156
+ ...style
157
+ }}
158
+ />
159
+ ))}
160
+ </div>
161
+ )
162
+
163
+ case "skeleton":
164
+ return (
165
+ <div className={cn("space-y-3", className)}>
166
+ <div className="minimal-loading-skeleton h-4 w-32 rounded bg-white/10" />
167
+ <div className="minimal-loading-skeleton h-3 w-24 rounded bg-white/10" />
168
+ <div className="minimal-loading-skeleton h-3 w-28 rounded bg-white/10" />
169
+ </div>
170
+ )
171
+
172
+ default:
173
+ return null
174
+ }
175
+ }
176
+
177
+ const getMessage = () => {
178
+ if (message) return message
179
+
180
+ switch (context) {
181
+ case "initial":
182
+ return "Loading video..."
183
+ case "seeking":
184
+ return "Seeking..."
185
+ case "buffering":
186
+ return "Buffering..."
187
+ default:
188
+ return "Loading..."
189
+ }
190
+ }
191
+
192
+ return (
193
+ <div className={cn(contextStyle.wrapper, contextStyle.background)}>
194
+ <div className="flex flex-col items-center gap-4">
195
+ <div className={cn(sizes.container, "flex items-center justify-center")}>
196
+ {renderLoadingIndicator()}
197
+ </div>
198
+
199
+ {showMessage && (
200
+ <div className="text-center">
201
+ <p className="font-medium text-sm text-white/90">
202
+ {getMessage()}
203
+ </p>
204
+ {context === "buffering" && (
205
+ <p className="mt-1 text-white/60 text-xs">
206
+ Please wait...
207
+ </p>
208
+ )}
209
+ </div>
210
+ )}
211
+ </div>
212
+ </div>
213
+ )
214
+ }
215
+
216
+ // Custom loading component wrapper
217
+ export interface CustomLoadingProps {
218
+ children: React.ReactNode
219
+ context?: LoadingContext
220
+ className?: string
221
+ }
222
+
223
+ export const CustomLoading = function CustomLoading({
224
+ children,
225
+ context = "initial",
226
+ className
227
+ }: CustomLoadingProps) {
228
+ const contextStyle = contextStyles[context]
229
+
230
+ return (
231
+ <div className={cn(contextStyle.wrapper, contextStyle.background, className)}>
232
+ {children}
233
+ </div>
234
+ )
235
+ }
236
+
237
+ // Export default presets
238
+ export const LoadingPresets = {
239
+ Initial: (props: Partial<LoadingStateProps>) => (
240
+ <LoadingState variant="animate" context="initial" size="md" {...props} />
241
+ ),
242
+ Seeking: (props: Partial<LoadingStateProps>) => (
243
+ <LoadingState variant="animate" context="seeking" size="sm" {...props} />
244
+ ),
245
+ Buffering: (props: Partial<LoadingStateProps>) => (
246
+ <LoadingState variant="animate" context="buffering" size="sm" {...props} />
247
+ )
248
+ }
@@ -0,0 +1,213 @@
1
+ import React from 'react'
2
+ import { cn } from '../../lib/utils'
3
+ import type { KeyboardShortcut } from '../../hooks/accessibility/use-keyboard-shortcuts'
4
+
5
+ interface KeyboardHelpOverlayProps {
6
+ isOpen: boolean
7
+ onClose: () => void
8
+ shortcuts: Array<{
9
+ key: string
10
+ description: string
11
+ modifiers?: {
12
+ ctrl?: boolean
13
+ alt?: boolean
14
+ shift?: boolean
15
+ }
16
+ }>
17
+ className?: string
18
+ }
19
+
20
+ export function KeyboardHelpOverlay({
21
+ isOpen,
22
+ onClose,
23
+ shortcuts,
24
+ className
25
+ }: KeyboardHelpOverlayProps) {
26
+ // Handle escape key to close
27
+ React.useEffect(() => {
28
+ if (!isOpen) return
29
+
30
+ const handleKeyDown = (e: KeyboardEvent) => {
31
+ if (e.key === 'Escape') {
32
+ onClose()
33
+ }
34
+ }
35
+
36
+ document.addEventListener('keydown', handleKeyDown)
37
+ return () => document.removeEventListener('keydown', handleKeyDown)
38
+ }, [isOpen, onClose])
39
+
40
+ // Focus trap within the overlay
41
+ React.useEffect(() => {
42
+ if (!isOpen) return
43
+
44
+ const overlay = document.querySelector('[data-keyboard-help-overlay]')
45
+ if (!overlay) return
46
+
47
+ const focusableElements = Array.from(
48
+ overlay.querySelectorAll(
49
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
50
+ )
51
+ ) as HTMLElement[]
52
+
53
+ if (focusableElements.length === 0) return
54
+
55
+ const firstElement = focusableElements[0]
56
+ const lastElement = focusableElements[focusableElements.length - 1]
57
+
58
+ // Focus the close button initially
59
+ const closeButton = overlay.querySelector('[data-close-button]') as HTMLElement
60
+ closeButton?.focus()
61
+
62
+ const handleTabKey = (e: KeyboardEvent) => {
63
+ if (e.key !== 'Tab') return
64
+
65
+ if (e.shiftKey) {
66
+ if (document.activeElement === firstElement) {
67
+ e.preventDefault()
68
+ lastElement.focus()
69
+ }
70
+ } else {
71
+ if (document.activeElement === lastElement) {
72
+ e.preventDefault()
73
+ firstElement.focus()
74
+ }
75
+ }
76
+ }
77
+
78
+ document.addEventListener('keydown', handleTabKey)
79
+ return () => document.removeEventListener('keydown', handleTabKey)
80
+ }, [isOpen])
81
+
82
+ if (!isOpen) return null
83
+
84
+ const formatKey = (key: string, modifiers?: { ctrl?: boolean; alt?: boolean; shift?: boolean }) => {
85
+ const parts = []
86
+
87
+ if (modifiers?.ctrl) parts.push('Ctrl')
88
+ if (modifiers?.alt) parts.push('Alt')
89
+ if (modifiers?.shift) parts.push('Shift')
90
+
91
+ // Format special keys
92
+ let keyDisplay = key
93
+ switch (key) {
94
+ case ' ':
95
+ keyDisplay = 'Space'
96
+ break
97
+ case 'ArrowUp':
98
+ keyDisplay = '↑'
99
+ break
100
+ case 'ArrowDown':
101
+ keyDisplay = '↓'
102
+ break
103
+ case 'ArrowLeft':
104
+ keyDisplay = '←'
105
+ break
106
+ case 'ArrowRight':
107
+ keyDisplay = '→'
108
+ break
109
+ default:
110
+ keyDisplay = key.toUpperCase()
111
+ }
112
+
113
+ parts.push(keyDisplay)
114
+ return parts.join(' + ')
115
+ }
116
+
117
+ return (
118
+ <div
119
+ className={cn(
120
+ 'fixed inset-0 z-50 flex items-center justify-center bg-black/50',
121
+ 'backdrop-blur-sm',
122
+ className
123
+ )}
124
+ data-keyboard-help-overlay
125
+ onClick={(e) => {
126
+ if (e.target === e.currentTarget) {
127
+ onClose()
128
+ }
129
+ }}
130
+ role="dialog"
131
+ aria-modal="true"
132
+ aria-labelledby="keyboard-help-title"
133
+ aria-describedby="keyboard-help-description"
134
+ >
135
+ <div className={cn(
136
+ 'w-full max-w-md rounded-lg bg-white p-6 shadow-xl',
137
+ 'dark:bg-gray-900 dark:text-white',
138
+ 'focus-within:outline-none',
139
+ "fade-in-0 zoom-in-95 animate-in duration-200"
140
+ )}>
141
+ <div className="mb-4 flex items-center justify-between">
142
+ <h2
143
+ id="keyboard-help-title"
144
+ className="font-semibold text-lg"
145
+ >
146
+ Keyboard Shortcuts
147
+ </h2>
148
+ <button
149
+ data-close-button
150
+ onClick={onClose}
151
+ className={cn(
152
+ 'rounded-md p-2 text-gray-500 transition-colors',
153
+ 'hover:bg-gray-100 hover:text-gray-700',
154
+ 'focus:outline-none focus:ring-2 focus:ring-blue-500',
155
+ 'dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200'
156
+ )}
157
+ aria-label="Close keyboard shortcuts help"
158
+ >
159
+ <svg
160
+ className="h-5 w-5"
161
+ fill="none"
162
+ stroke="currentColor"
163
+ viewBox="0 0 24 24"
164
+ aria-hidden="true"
165
+ >
166
+ <path
167
+ strokeLinecap="round"
168
+ strokeLinejoin="round"
169
+ strokeWidth={2}
170
+ d="M6 18L18 6M6 6l12 12"
171
+ />
172
+ </svg>
173
+ </button>
174
+ </div>
175
+
176
+ <p
177
+ id="keyboard-help-description"
178
+ className="mb-4 text-gray-600 text-sm dark:text-gray-400"
179
+ >
180
+ Use these keyboard shortcuts to control the media player:
181
+ </p>
182
+
183
+ <div className="max-h-64 overflow-y-auto">
184
+ <dl className="space-y-3">
185
+ {shortcuts.map(({ key, description, modifiers }, index) => (
186
+ <div
187
+ key={`${key}-${index}`}
188
+ className="flex items-center justify-between"
189
+ >
190
+ <dt className="text-gray-600 text-sm dark:text-gray-400">
191
+ {description}
192
+ </dt>
193
+ <dd>
194
+ <kbd className={cn(
195
+ "inline-flex items-center rounded px-2 py-1 font-mono text-xs",
196
+ "border border-gray-300 bg-gray-100 text-gray-800",
197
+ "dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200"
198
+ )}>
199
+ {formatKey(key, modifiers)}
200
+ </kbd>
201
+ </dd>
202
+ </div>
203
+ ))}
204
+ </dl>
205
+ </div>
206
+
207
+ <div className="mt-4 text-gray-500 text-xs dark:text-gray-400">
208
+ Press <kbd className="rounded bg-gray-100 px-1 dark:bg-gray-800">Esc</kbd> to close this help
209
+ </div>
210
+ </div>
211
+ </div>
212
+ )
213
+ }
@@ -0,0 +1,85 @@
1
+ "use client"
2
+
3
+ import { forwardRef } from "react"
4
+ import { useKeyboardControls } from "../hooks/use-keyboard-controls"
5
+ import { useMediaPlayer } from "../hooks/use-media-player"
6
+ import { cn } from "../lib/utils"
7
+ import type { AudioPlayerProps } from "../types/index"
8
+
9
+ export const AudioPlayer = forwardRef<HTMLDivElement, AudioPlayerProps>(
10
+ (
11
+ {
12
+ src,
13
+ className,
14
+ children,
15
+ keyboardControls = true,
16
+ showControls = true,
17
+ customControls = false,
18
+ ...options
19
+ },
20
+ ref
21
+ ) => {
22
+ const { state, controls, mediaRef, containerRef } = useMediaPlayer({
23
+ src,
24
+ ...options
25
+ })
26
+
27
+ useKeyboardControls({
28
+ enabled: keyboardControls,
29
+ state,
30
+ controls
31
+ })
32
+
33
+ const renderSources = () => {
34
+ if (Array.isArray(src)) {
35
+ return src.map((source, index) => <source key={index} src={source} />)
36
+ }
37
+ return null
38
+ }
39
+
40
+ const audioProps = {
41
+ ref: mediaRef as React.RefObject<HTMLAudioElement>,
42
+ controls: showControls && !customControls,
43
+ className: "w-full",
44
+ ...(Array.isArray(src) ? {} : { src })
45
+ }
46
+
47
+ return (
48
+ <div
49
+ ref={ref || containerRef}
50
+ className={cn("relative", "focus-within:outline-none", className)}
51
+ tabIndex={0}>
52
+ <audio {...audioProps}>
53
+ {Array.isArray(src) && renderSources()}
54
+ Your browser does not support the audio element.
55
+ </audio>
56
+
57
+ {state.error && (
58
+ <div className="mt-2 rounded-md border border-red-300 bg-red-100 p-3">
59
+ <div className="text-red-800">
60
+ <p className="font-semibold">Error</p>
61
+ <p className="text-sm">{state.error}</p>
62
+ </div>
63
+ </div>
64
+ )}
65
+
66
+ {state.isLoading && (
67
+ <div className="mt-2 rounded-md border border-gray-300 bg-gray-100 p-3">
68
+ <div className="flex items-center space-x-2 text-gray-600">
69
+ <div className="h-4 w-4 animate-spin rounded-full border-gray-600 border-b-2"></div>
70
+ <span className="text-sm">Loading...</span>
71
+ </div>
72
+ </div>
73
+ )}
74
+
75
+ {customControls && typeof children === "function" && (
76
+ <div className="mt-2">{children({ state, controls })}</div>
77
+ )}
78
+
79
+ {customControls && typeof children !== "function" && <div className="mt-2">{children}</div>}
80
+ </div>
81
+ )
82
+ }
83
+ )
84
+
85
+ AudioPlayer.displayName = "AudioPlayer"
@@ -0,0 +1,37 @@
1
+ // Basic Media Player Components
2
+ export { AudioPlayer } from "../audio-player";
3
+ export { MediaPlayer } from "../media-player";
4
+ export { VideoPlayer } from "../video-player";
5
+ export { SimpleVideoPlayer } from "../simple-video-player";
6
+
7
+ // Modern React Player
8
+ export { ModernMediaPlayer } from "../react-player-wrapper";
9
+ export type { ModernMediaPlayerProps } from "../react-player-wrapper";
10
+
11
+ // Basic UI Components
12
+ export { FullscreenButton } from "../ui/fullscreen-button";
13
+ export { PlayButton } from "../ui/play-button";
14
+ export { SeekBar } from "../ui/seek-bar";
15
+ export { TimeDisplay } from "../ui/time-display";
16
+ export { VolumeControl } from "../ui/volume-control";
17
+
18
+ // DiceUI-style Basic Components
19
+ export { MediaPlayerAudio } from "../media-player-audio";
20
+ export {
21
+ MediaPlayerControls,
22
+ MediaPlayerControlsOverlay
23
+ } from "../media-player-controls";
24
+ export { MediaPlayerError } from "../media-player-error";
25
+ export { MediaPlayerFullscreen } from "../media-player-fullscreen";
26
+ export { MediaPlayerLoading } from "../media-player-loading";
27
+ export { MediaPlayerPlay } from "../media-player-play";
28
+ export { MediaPlayerRoot } from "../media-player-root";
29
+ export { MediaPlayerSeek } from "../media-player-seek";
30
+ export { MediaPlayerTime } from "../media-player-time";
31
+ export { MediaPlayerTooltip } from "../media-player-tooltip";
32
+ export { MediaPlayerVideo } from "../media-player-video";
33
+ export { MediaPlayerVolume } from "../media-player-volume";
34
+
35
+ // Basic Examples
36
+ export { BasicVideoPlayer } from "../examples/basic-video-player";
37
+ export { CustomControlsExample } from "../examples/custom-controls-example";