@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,362 @@
1
+ "use client"
2
+
3
+ import React, { Suspense } from "react"
4
+ import { cn } from "../lib/utils"
5
+
6
+ // Loading state component for media operations
7
+ export interface MediaLoadingProps {
8
+ children?: React.ReactNode
9
+ className?: string
10
+ size?: "sm" | "md" | "lg"
11
+ variant?: "spinner" | "skeleton" | "pulse"
12
+ }
13
+
14
+ export function MediaLoading({
15
+ children,
16
+ className,
17
+ size = "md",
18
+ variant = "spinner"
19
+ }: MediaLoadingProps) {
20
+ const sizeClasses = {
21
+ sm: "w-4 h-4",
22
+ md: "w-8 h-8",
23
+ lg: "w-12 h-12"
24
+ }
25
+
26
+ if (variant === "spinner") {
27
+ return (
28
+ <div className={cn(
29
+ "flex items-center justify-center",
30
+ className
31
+ )}>
32
+ <div className={cn(
33
+ "animate-spin rounded-full border-2 border-current border-t-transparent",
34
+ sizeClasses[size]
35
+ )} />
36
+ {children && (
37
+ <span className="ml-2 text-muted-foreground text-sm">
38
+ {children}
39
+ </span>
40
+ )}
41
+ </div>
42
+ )
43
+ }
44
+
45
+ if (variant === "skeleton") {
46
+ return (
47
+ <div className={cn(
48
+ "animate-pulse rounded bg-muted",
49
+ sizeClasses[size],
50
+ className
51
+ )} />
52
+ )
53
+ }
54
+
55
+ // Pulse variant
56
+ return (
57
+ <div className={cn(
58
+ "animate-pulse bg-gradient-to-r bg-pos-0 bg-size-200 from-muted via-muted/50 to-muted",
59
+ "animate-shimmer rounded",
60
+ sizeClasses[size],
61
+ className
62
+ )} />
63
+ )
64
+ }
65
+
66
+ // Suspense wrapper for media components
67
+ export interface MediaSuspenseProps {
68
+ children: React.ReactNode
69
+ fallback?: React.ReactNode
70
+ className?: string
71
+ }
72
+
73
+ export function MediaSuspense({
74
+ children,
75
+ fallback = <MediaLoading />,
76
+ className
77
+ }: MediaSuspenseProps) {
78
+ return (
79
+ <div className={className}>
80
+ <Suspense fallback={fallback}>
81
+ {children}
82
+ </Suspense>
83
+ </div>
84
+ )
85
+ }
86
+
87
+ // Lazy loading wrapper for heavy media components
88
+ export function withMediaSuspense<P extends object>(
89
+ Component: React.ComponentType<P>,
90
+ loadingComponent?: React.ComponentType
91
+ ) {
92
+ const WrappedComponent = React.forwardRef<any, P>((props, ref) => {
93
+ const LoadingComp = loadingComponent
94
+ return (
95
+ <MediaSuspense fallback={LoadingComp ? <LoadingComp /> : <MediaLoading />}>
96
+ <Component {...(props as any)} ref={ref} />
97
+ </MediaSuspense>
98
+ )
99
+ })
100
+
101
+ WrappedComponent.displayName = `withMediaSuspense(${Component.displayName || Component.name})`
102
+
103
+ return WrappedComponent
104
+ }
105
+
106
+ // Hook for managing concurrent state updates
107
+ export function useMediaConcurrent() {
108
+ const [isPending, startTransition] = React.useTransition()
109
+
110
+ // Wrap state updates in transitions for better performance
111
+ const updateWithTransition = React.useCallback(
112
+ (updateFn: () => any): Promise<any> => {
113
+ return new Promise((resolve) => {
114
+ startTransition(() => {
115
+ const result = updateFn()
116
+ resolve(result)
117
+ })
118
+ })
119
+ },
120
+ [startTransition]
121
+ )
122
+
123
+ // Batch multiple updates together
124
+ const batchUpdates = React.useCallback(
125
+ (updates: Array<() => any>): Promise<any[]> => {
126
+ return new Promise((resolve) => {
127
+ startTransition(() => {
128
+ const results = updates.map(update => update())
129
+ resolve(results)
130
+ })
131
+ })
132
+ },
133
+ [startTransition]
134
+ )
135
+
136
+ return {
137
+ isPending,
138
+ updateWithTransition,
139
+ batchUpdates
140
+ }
141
+ }
142
+
143
+ // Error boundary for media components
144
+ export interface MediaErrorBoundaryProps {
145
+ children: React.ReactNode
146
+ fallback?: React.ComponentType<{ error: Error; resetError: () => void }>
147
+ onError?: (error: Error, errorInfo: React.ErrorInfo) => void
148
+ }
149
+
150
+ interface MediaErrorBoundaryState {
151
+ hasError: boolean
152
+ error: Error | null
153
+ }
154
+
155
+ // Default error fallback component
156
+ function DefaultErrorFallback({
157
+ error,
158
+ resetError
159
+ }: {
160
+ error: Error
161
+ resetError: () => void
162
+ }) {
163
+ return (
164
+ <div className="flex flex-col items-center justify-center rounded-lg border border-destructive/20 bg-destructive/10 p-6">
165
+ <div className="mb-2 font-semibold text-destructive text-lg">
166
+ Media Player Error
167
+ </div>
168
+ <div className="mb-4 text-center text-muted-foreground text-sm">
169
+ {error.message}
170
+ </div>
171
+ <button
172
+ onClick={resetError}
173
+ className="rounded bg-primary px-4 py-2 text-primary-foreground transition-colors hover:bg-primary/90"
174
+ >
175
+ Try Again
176
+ </button>
177
+ </div>
178
+ )
179
+ }
180
+
181
+ export class MediaErrorBoundary extends React.Component<
182
+ MediaErrorBoundaryProps,
183
+ MediaErrorBoundaryState
184
+ > {
185
+ constructor(props: MediaErrorBoundaryProps) {
186
+ super(props)
187
+ this.state = { hasError: false, error: null }
188
+ }
189
+
190
+ static getDerivedStateFromError(error: Error): MediaErrorBoundaryState {
191
+ return { hasError: true, error }
192
+ }
193
+
194
+ componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
195
+ this.props.onError?.(error, errorInfo)
196
+ console.error('MediaErrorBoundary caught an error:', error, errorInfo)
197
+ }
198
+
199
+ resetError = () => {
200
+ this.setState({ hasError: false, error: null })
201
+ }
202
+
203
+ render() {
204
+ if (this.state.hasError && this.state.error) {
205
+ const FallbackComponent = this.props.fallback || DefaultErrorFallback
206
+ return <FallbackComponent error={this.state.error} resetError={this.resetError} />
207
+ }
208
+
209
+ return this.props.children
210
+ }
211
+ }
212
+
213
+ // Concurrent media component wrapper
214
+ export interface ConcurrentMediaProps {
215
+ children: React.ReactNode
216
+ priority?: "high" | "normal" | "low"
217
+ deferredMs?: number
218
+ }
219
+
220
+ export function ConcurrentMedia({
221
+ children,
222
+ priority = "normal",
223
+ deferredMs = 0
224
+ }: ConcurrentMediaProps) {
225
+ const [isDeferred, setIsDeferred] = React.useState(deferredMs > 0)
226
+
227
+ React.useEffect(() => {
228
+ if (deferredMs > 0) {
229
+ const timer = setTimeout(() => {
230
+ setIsDeferred(false)
231
+ }, deferredMs)
232
+
233
+ return () => clearTimeout(timer)
234
+ }
235
+ }, [deferredMs])
236
+
237
+ if (isDeferred) {
238
+ return <MediaLoading variant="skeleton" />
239
+ }
240
+
241
+ // Use different rendering strategies based on priority
242
+ if (priority === "high") {
243
+ // Render immediately without deferring
244
+ return <>{children}</>
245
+ }
246
+
247
+ if (priority === "low") {
248
+ // Use deferred value for lower priority updates
249
+ const deferredChildren = React.useDeferredValue(children)
250
+ return <>{deferredChildren}</>
251
+ }
252
+
253
+ // Normal priority - use transition
254
+ return (
255
+ <React.Suspense fallback={<MediaLoading />}>
256
+ {children}
257
+ </React.Suspense>
258
+ )
259
+ }
260
+
261
+ // Hook for deferred loading of media resources
262
+ export function useDeferredMediaLoading(delay: number = 100) {
263
+ const [isReady, setIsReady] = React.useState(false)
264
+ const deferredIsReady = React.useDeferredValue(isReady)
265
+
266
+ React.useEffect(() => {
267
+ const timer = setTimeout(() => {
268
+ setIsReady(true)
269
+ }, delay)
270
+
271
+ return () => clearTimeout(timer)
272
+ }, [delay])
273
+
274
+ return {
275
+ isReady: deferredIsReady,
276
+ actualIsReady: isReady
277
+ }
278
+ }
279
+
280
+ // Media component with full React 19 concurrent features
281
+ export interface EnhancedMediaWrapperProps {
282
+ children: React.ReactNode
283
+ className?: string
284
+ loadingFallback?: React.ReactNode
285
+ errorFallback?: React.ComponentType<{ error: Error; resetError: () => void }>
286
+ priority?: "high" | "normal" | "low"
287
+ deferredMs?: number
288
+ onError?: (error: Error, errorInfo: React.ErrorInfo) => void
289
+ }
290
+
291
+ export function EnhancedMediaWrapper({
292
+ children,
293
+ className,
294
+ loadingFallback = <MediaLoading />,
295
+ errorFallback,
296
+ priority = "normal",
297
+ deferredMs = 0,
298
+ onError
299
+ }: EnhancedMediaWrapperProps) {
300
+ return (
301
+ <div className={className}>
302
+ <MediaErrorBoundary fallback={errorFallback} onError={onError}>
303
+ <MediaSuspense fallback={loadingFallback}>
304
+ <ConcurrentMedia priority={priority} deferredMs={deferredMs}>
305
+ {children}
306
+ </ConcurrentMedia>
307
+ </MediaSuspense>
308
+ </MediaErrorBoundary>
309
+ </div>
310
+ )
311
+ }
312
+
313
+ // Export enhanced components with concurrent features
314
+ export const EnhancedMediaSuspense = MediaSuspense
315
+ export const EnhancedMediaLoading = MediaLoading
316
+
317
+ // Performance monitoring hook for concurrent features
318
+ export function useMediaPerformance() {
319
+ const [metrics, setMetrics] = React.useState({
320
+ renderTime: 0,
321
+ suspenseCount: 0,
322
+ errorCount: 0,
323
+ transitionCount: 0
324
+ })
325
+
326
+ const recordRenderTime = React.useCallback((startTime: number) => {
327
+ const renderTime = performance.now() - startTime
328
+ setMetrics(prev => ({
329
+ ...prev,
330
+ renderTime: Math.round(renderTime * 100) / 100
331
+ }))
332
+ }, [])
333
+
334
+ const incrementSuspense = React.useCallback(() => {
335
+ setMetrics(prev => ({
336
+ ...prev,
337
+ suspenseCount: prev.suspenseCount + 1
338
+ }))
339
+ }, [])
340
+
341
+ const incrementError = React.useCallback(() => {
342
+ setMetrics(prev => ({
343
+ ...prev,
344
+ errorCount: prev.errorCount + 1
345
+ }))
346
+ }, [])
347
+
348
+ const incrementTransition = React.useCallback(() => {
349
+ setMetrics(prev => ({
350
+ ...prev,
351
+ transitionCount: prev.transitionCount + 1
352
+ }))
353
+ }, [])
354
+
355
+ return {
356
+ metrics,
357
+ recordRenderTime,
358
+ incrementSuspense,
359
+ incrementError,
360
+ incrementTransition
361
+ }
362
+ }
@@ -0,0 +1,163 @@
1
+ import { useActionState } from 'react'
2
+
3
+ export interface PlayerSettings {
4
+ volume: number
5
+ quality: string
6
+ subtitles: boolean
7
+ playbackRate: number
8
+ }
9
+
10
+ interface SettingsState {
11
+ values: PlayerSettings
12
+ error?: string
13
+ success?: boolean
14
+ }
15
+
16
+ const defaultSettings: PlayerSettings = {
17
+ volume: 0.8,
18
+ quality: 'auto',
19
+ subtitles: false,
20
+ playbackRate: 1
21
+ }
22
+
23
+ async function updateSettings(
24
+ prevState: SettingsState,
25
+ formData: FormData
26
+ ): Promise<SettingsState> {
27
+ try {
28
+ // Simulate async operation
29
+ await new Promise(resolve => setTimeout(resolve, 500))
30
+
31
+ const settings: PlayerSettings = {
32
+ volume: Number(formData.get('volume')) || 0.8,
33
+ quality: (formData.get('quality') as string) || 'auto',
34
+ subtitles: formData.get('subtitles') === 'on',
35
+ playbackRate: Number(formData.get('playbackRate')) || 1
36
+ }
37
+
38
+ // Validate settings
39
+ if (settings.volume < 0 || settings.volume > 1) {
40
+ throw new Error('Volume must be between 0 and 1')
41
+ }
42
+
43
+ if (settings.playbackRate < 0.25 || settings.playbackRate > 2) {
44
+ throw new Error('Playback rate must be between 0.25 and 2')
45
+ }
46
+
47
+ // Save to localStorage or send to server
48
+ localStorage.setItem('player-settings', JSON.stringify(settings))
49
+
50
+ return {
51
+ values: settings,
52
+ success: true
53
+ }
54
+ } catch (error) {
55
+ return {
56
+ values: prevState.values,
57
+ error: error instanceof Error ? error.message : 'Unknown error'
58
+ }
59
+ }
60
+ }
61
+
62
+ export function PlayerSettingsForm() {
63
+ const [state, submitAction, isPending] = useActionState(
64
+ updateSettings,
65
+ {
66
+ values: defaultSettings,
67
+ success: false
68
+ }
69
+ )
70
+
71
+ return (
72
+ <div className="mx-auto w-full max-w-md">
73
+ <h3 className="mb-4 font-semibold text-lg text-white">Player Settings</h3>
74
+
75
+ <form action={submitAction} className="space-y-4">
76
+ {state.error && (
77
+ <div className="rounded-lg border border-red-700 bg-red-900/20 p-3">
78
+ <p className="text-red-400 text-sm">{state.error}</p>
79
+ </div>
80
+ )}
81
+
82
+ {state.success && (
83
+ <div className="rounded-lg border border-green-700 bg-green-900/20 p-3">
84
+ <p className="text-green-400 text-sm">Settings saved successfully!</p>
85
+ </div>
86
+ )}
87
+
88
+ <fieldset disabled={isPending} className="space-y-4 disabled:opacity-50">
89
+ <div>
90
+ <label htmlFor="volume" className="mb-2 block font-medium text-sm text-white">
91
+ Volume: {Math.round(state.values.volume * 100)}%
92
+ </label>
93
+ <input
94
+ type="range"
95
+ id="volume"
96
+ name="volume"
97
+ min="0"
98
+ max="1"
99
+ step="0.01"
100
+ defaultValue={state.values.volume}
101
+ className="h-2 w-full cursor-pointer appearance-none rounded-lg bg-gray-700"
102
+ />
103
+ </div>
104
+
105
+ <div>
106
+ <label htmlFor="quality" className="mb-2 block font-medium text-sm text-white">
107
+ Quality
108
+ </label>
109
+ <select
110
+ id="quality"
111
+ name="quality"
112
+ defaultValue={state.values.quality}
113
+ className="w-full rounded-lg border border-gray-600 bg-gray-700 p-2 text-white focus:ring-2 focus:ring-blue-500"
114
+ >
115
+ <option value="auto">Auto</option>
116
+ <option value="1080p">1080p</option>
117
+ <option value="720p">720p</option>
118
+ <option value="480p">480p</option>
119
+ <option value="360p">360p</option>
120
+ </select>
121
+ </div>
122
+
123
+ <div>
124
+ <label htmlFor="playbackRate" className="mb-2 block font-medium text-sm text-white">
125
+ Playback Rate: {state.values.playbackRate}x
126
+ </label>
127
+ <input
128
+ type="range"
129
+ id="playbackRate"
130
+ name="playbackRate"
131
+ min="0.25"
132
+ max="2"
133
+ step="0.25"
134
+ defaultValue={state.values.playbackRate}
135
+ className="h-2 w-full cursor-pointer appearance-none rounded-lg bg-gray-700"
136
+ />
137
+ </div>
138
+
139
+ <div className="flex items-center">
140
+ <input
141
+ type="checkbox"
142
+ id="subtitles"
143
+ name="subtitles"
144
+ defaultChecked={state.values.subtitles}
145
+ className="h-4 w-4 rounded border-gray-600 bg-gray-700 text-blue-600 focus:ring-blue-500"
146
+ />
147
+ <label htmlFor="subtitles" className="ml-2 font-medium text-sm text-white">
148
+ Enable Subtitles
149
+ </label>
150
+ </div>
151
+
152
+ <button
153
+ type="submit"
154
+ disabled={isPending}
155
+ className="w-full rounded-lg bg-blue-600 px-4 py-2 font-medium text-white transition-colors hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 disabled:bg-gray-600"
156
+ >
157
+ {isPending ? 'Saving...' : 'Save Settings'}
158
+ </button>
159
+ </fieldset>
160
+ </form>
161
+ </div>
162
+ )
163
+ }