@therealtinhtute/baroiplayer 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/core.js +1 -1
  2. package/dist/core.mjs +2 -2
  3. package/dist/hooks-BCMilGRf.cjs +4 -0
  4. package/dist/hooks-BCMilGRf.cjs.map +1 -0
  5. package/dist/{hooks-BhW_i-9L.js → hooks-BPtHV1ZT.js} +793 -764
  6. package/dist/hooks-BPtHV1ZT.js.map +1 -0
  7. package/dist/index.d.ts +38 -1
  8. package/dist/{minimal-components-BsqodtY8.cjs → minimal-components-BHJMuaL7.cjs} +2 -2
  9. package/dist/{minimal-components-BsqodtY8.cjs.map → minimal-components-BHJMuaL7.cjs.map} +1 -1
  10. package/dist/{minimal-components-B0bILe18.js → minimal-components-BmqRefl1.js} +2 -2
  11. package/dist/{minimal-components-B0bILe18.js.map → minimal-components-BmqRefl1.js.map} +1 -1
  12. package/dist/package.json +38 -4
  13. package/dist/src/components/media-player-controls.tsx +1 -1
  14. package/dist/src/components/media-player-root.tsx +1 -1
  15. package/dist/src/components/ui/button.tsx +49 -0
  16. package/dist/src/components/ui/dropdown-menu.tsx +137 -0
  17. package/dist/src/components/ui/icons.tsx +174 -0
  18. package/dist/src/components/ui/slider.tsx +55 -0
  19. package/dist/src/components/ui/tooltip.tsx +131 -0
  20. package/dist/src/hooks/use-enhanced-media-player.ts +12 -12
  21. package/dist/src/hooks/use-intelligent-preloading.ts +27 -22
  22. package/dist/src/hooks/use-media-player.ts +0 -2
  23. package/dist/src/hooks/use-memory-optimization.ts +19 -19
  24. package/dist/src/hooks/use-mobile.ts +125 -0
  25. package/dist/src/hooks/use-touch-gestures.js +1 -1
  26. package/dist/src/index-legacy.ts +2 -1
  27. package/dist/styles/globals.css +462 -0
  28. package/dist/styles/pixel-art.css +46 -0
  29. package/dist/styles/videojs.css +100 -0
  30. package/package.json +23 -3
  31. package/src/components/media-player-controls.tsx +1 -1
  32. package/src/components/media-player-root.tsx +1 -1
  33. package/src/components/ui/button.tsx +49 -0
  34. package/src/components/ui/dropdown-menu.tsx +137 -0
  35. package/src/components/ui/icons.tsx +174 -0
  36. package/src/components/ui/slider.tsx +55 -0
  37. package/src/components/ui/tooltip.tsx +131 -0
  38. package/src/hooks/use-enhanced-media-player.ts +12 -12
  39. package/src/hooks/use-intelligent-preloading.ts +27 -22
  40. package/src/hooks/use-media-player.ts +0 -2
  41. package/src/hooks/use-memory-optimization.ts +19 -19
  42. package/src/hooks/use-mobile.ts +125 -0
  43. package/src/hooks/use-touch-gestures.js +1 -1
  44. package/src/index-legacy.ts +2 -1
  45. package/dist/hooks-BhW_i-9L.js.map +0 -1
  46. package/dist/hooks-DTXeB5Z-.cjs +0 -4
  47. package/dist/hooks-DTXeB5Z-.cjs.map +0 -1
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@therealtinhtute/baroiplayer",
3
- "version": "1.0.0",
3
+ "version": "1.0.5",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "module": "./index.mjs",
@@ -12,17 +12,51 @@
12
12
  "require": "./index.js"
13
13
  },
14
14
  "./globals.css": "./index.css",
15
- "./dist/*": "./dist/*"
15
+ "./minimal-player.css": "./minimal-components.css",
16
+ "./dist/styles/minimal-player.css": "./minimal-components.css",
17
+ "./styles/minimal-player.css": "./minimal-components.css",
18
+ "./dist/*": "./*",
19
+ "./lib/*": "./src/lib/*.ts",
20
+ "./components/*": "./src/components/*.tsx",
21
+ "./hooks/*": "./src/hooks/*.ts",
22
+ "./types/*": "./src/types/*.ts"
16
23
  },
17
24
  "files": [
18
25
  "*.js",
19
26
  "*.mjs",
20
27
  "*.d.ts",
21
28
  "*.css",
22
- "src/"
29
+ "src/",
30
+ "styles/"
23
31
  ],
24
32
  "peerDependencies": {
25
33
  "react": ">=18.0.0",
26
- "react-dom": ">=18.0.0"
34
+ "react-dom": ">=18.0.0",
35
+ "tailwindcss": ">=4.0.0",
36
+ "video.js": ">=7.0.0",
37
+ "hls.js": ">=1.0.0",
38
+ "@videojs/http-streaming": ">=2.0.0",
39
+ "videojs-contrib-quality-levels": ">=2.0.0",
40
+ "videojs-hls-quality-selector": ">=2.0.0"
41
+ },
42
+ "peerDependenciesMeta": {
43
+ "tailwindcss": {
44
+ "optional": true
45
+ },
46
+ "video.js": {
47
+ "optional": true
48
+ },
49
+ "hls.js": {
50
+ "optional": true
51
+ },
52
+ "@videojs/http-streaming": {
53
+ "optional": true
54
+ },
55
+ "videojs-contrib-quality-levels": {
56
+ "optional": true
57
+ },
58
+ "videojs-hls-quality-selector": {
59
+ "optional": true
60
+ }
27
61
  }
28
62
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  import React from "react"
4
4
  import { useMediaSelector } from "../context/media-context"
5
- import { useDeviceInfo, useContainerQuery } from "@rp/ui/hooks/use-mobile"
5
+ import { useDeviceInfo, useContainerQuery } from "../hooks/use-mobile"
6
6
  import { cn } from "../lib/utils"
7
7
 
8
8
  export interface MediaPlayerControlsProps {
@@ -9,7 +9,7 @@ import {
9
9
  } from "../context/media-context"
10
10
  import { MediaProvider } from "../context/media-provider"
11
11
  import { useMediaGestures } from "../hooks/use-touch-gestures"
12
- import { useMobileOptimizations, useDeviceInfo } from "@rp/ui/hooks/use-mobile"
12
+ import { useMobileOptimizations, useDeviceInfo } from "../hooks/use-mobile"
13
13
  import { cn } from "../lib/utils"
14
14
 
15
15
  // Utility function to compose refs
@@ -0,0 +1,49 @@
1
+ import React, { forwardRef } from 'react';
2
+ import { cn } from '../lib/utils';
3
+
4
+ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
5
+ variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link';
6
+ size?: 'default' | 'sm' | 'lg' | 'icon';
7
+ asChild?: boolean;
8
+ }
9
+
10
+ const Button = forwardRef<HTMLButtonElement, ButtonProps>(
11
+ ({ className, variant = 'default', size = 'default', asChild = false, ...props }, ref) => {
12
+ const baseClasses = 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50';
13
+
14
+ const variants = {
15
+ default: 'bg-primary text-primary-foreground hover:bg-primary/90',
16
+ destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
17
+ outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
18
+ secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
19
+ ghost: 'hover:bg-accent hover:text-accent-foreground',
20
+ link: 'text-primary underline-offset-4 hover:underline',
21
+ };
22
+
23
+ const sizes = {
24
+ default: 'h-10 px-4 py-2',
25
+ sm: 'h-9 rounded-md px-3',
26
+ lg: 'h-11 rounded-md px-8',
27
+ icon: 'h-10 w-10',
28
+ };
29
+
30
+ const Comp = asChild ? 'span' : 'button';
31
+
32
+ return (
33
+ <Comp
34
+ className={cn(
35
+ baseClasses,
36
+ variants[variant],
37
+ sizes[size],
38
+ className
39
+ )}
40
+ ref={ref}
41
+ {...props}
42
+ />
43
+ );
44
+ }
45
+ );
46
+
47
+ Button.displayName = 'Button';
48
+
49
+ export { Button };
@@ -0,0 +1,137 @@
1
+ import React, { useState, useRef, useEffect, forwardRef } from 'react';
2
+ import { cn } from '../lib/utils';
3
+
4
+ interface DropdownMenuProps {
5
+ children: React.ReactNode;
6
+ }
7
+
8
+ interface DropdownMenuTriggerProps {
9
+ children: React.ReactNode;
10
+ asChild?: boolean;
11
+ }
12
+
13
+ interface DropdownMenuContentProps {
14
+ children: React.ReactNode;
15
+ className?: string;
16
+ }
17
+
18
+ interface DropdownMenuItemProps {
19
+ children: React.ReactNode;
20
+ className?: string;
21
+ onClick?: () => void;
22
+ }
23
+
24
+ interface DropdownMenuLabelProps {
25
+ children: React.ReactNode;
26
+ className?: string;
27
+ }
28
+
29
+ interface DropdownMenuSeparatorProps {
30
+ className?: string;
31
+ }
32
+
33
+ const DropdownMenu: React.FC<DropdownMenuProps> = ({ children }) => {
34
+ const [isOpen, setIsOpen] = useState(false);
35
+ const dropdownRef = useRef<HTMLDivElement>(null);
36
+
37
+ useEffect(() => {
38
+ const handleClickOutside = (event: MouseEvent) => {
39
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
40
+ setIsOpen(false);
41
+ }
42
+ };
43
+
44
+ document.addEventListener('mousedown', handleClickOutside);
45
+ return () => document.removeEventListener('mousedown', handleClickOutside);
46
+ }, []);
47
+
48
+ return (
49
+ <div ref={dropdownRef} className="relative inline-block text-left">
50
+ {React.Children.map(children, (child) => {
51
+ if (React.isValidElement(child)) {
52
+ return React.cloneElement(child as React.ReactElement, { isOpen, setIsOpen });
53
+ }
54
+ return child;
55
+ })}
56
+ </div>
57
+ );
58
+ };
59
+
60
+ const DropdownMenuTrigger: React.FC<DropdownMenuTriggerProps> = ({ children, asChild = false, isOpen, setIsOpen }) => {
61
+ const handleClick = () => {
62
+ setIsOpen?.(!isOpen);
63
+ };
64
+
65
+ const TriggerComponent = asChild ? 'div' : 'button';
66
+
67
+ return (
68
+ <TriggerComponent
69
+ onClick={handleClick}
70
+ className={cn(
71
+ 'inline-flex justify-center w-full rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500',
72
+ !asChild && 'cursor-pointer'
73
+ )}
74
+ >
75
+ {children}
76
+ </TriggerComponent>
77
+ );
78
+ };
79
+
80
+ const DropdownMenuContent: React.FC<DropdownMenuContentProps> = ({ children, className, isOpen }) => {
81
+ if (!isOpen) return null;
82
+
83
+ return (
84
+ <div className={cn(
85
+ 'absolute right-0 z-10 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none',
86
+ className
87
+ )}>
88
+ <div className="py-1">
89
+ {children}
90
+ </div>
91
+ </div>
92
+ );
93
+ };
94
+
95
+ const DropdownMenuItem: React.FC<DropdownMenuItemProps> = ({ children, className, onClick }) => {
96
+ return (
97
+ <button
98
+ onClick={onClick}
99
+ className={cn(
100
+ 'w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900',
101
+ className
102
+ )}
103
+ role="menuitem"
104
+ >
105
+ {children}
106
+ </button>
107
+ );
108
+ };
109
+
110
+ const DropdownMenuLabel: React.FC<DropdownMenuLabelProps> = ({ children, className }) => {
111
+ return (
112
+ <div className={cn(
113
+ 'px-4 py-2 text-sm font-medium text-gray-700',
114
+ className
115
+ )}>
116
+ {children}
117
+ </div>
118
+ );
119
+ };
120
+
121
+ const DropdownMenuSeparator: React.FC<DropdownMenuSeparatorProps> = ({ className }) => {
122
+ return (
123
+ <div className={cn(
124
+ 'border-t border-gray-100 my-1',
125
+ className
126
+ )} />
127
+ );
128
+ };
129
+
130
+ export {
131
+ DropdownMenu,
132
+ DropdownMenuTrigger,
133
+ DropdownMenuContent,
134
+ DropdownMenuItem,
135
+ DropdownMenuLabel,
136
+ DropdownMenuSeparator
137
+ };
@@ -0,0 +1,174 @@
1
+ import React from 'react';
2
+
3
+ interface IconProps {
4
+ className?: string;
5
+ size?: number | string;
6
+ }
7
+
8
+ // Button Icons
9
+ export const PlayIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
10
+ <svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
11
+ <polygon points="5 3 19 12 5 21 5 3"></polygon>
12
+ </svg>
13
+ );
14
+
15
+ export const PauseIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
16
+ <svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
17
+ <rect x="6" y="4" width="4" height="16"></rect>
18
+ <rect x="14" y="4" width="4" height="16"></rect>
19
+ </svg>
20
+ );
21
+
22
+ export const VolumeIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
23
+ <svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
24
+ <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon>
25
+ <path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"></path>
26
+ </svg>
27
+ );
28
+
29
+ export const Volume1Icon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
30
+ <svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
31
+ <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon>
32
+ <path d="M15.54 8.46a5 5 0 0 1 0 7.07"></path>
33
+ </svg>
34
+ );
35
+
36
+ export const Volume2Icon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
37
+ <svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
38
+ <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon>
39
+ <path d="M15.54 8.46a5 5 0 0 1 0 7.07"></path>
40
+ <path d="M19.07 4.93a10 10 0 0 1 0 14.14"></path>
41
+ </svg>
42
+ );
43
+
44
+ export const VolumeXIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
45
+ <svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
46
+ <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon>
47
+ <line x1="23" y1="9" x2="17" y2="15"></line>
48
+ <line x1="17" y1="9" x2="23" y2="15"></line>
49
+ </svg>
50
+ );
51
+
52
+ export const MuteIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
53
+ <VolumeXIcon className={className} size={size} />
54
+ );
55
+
56
+ // Fullscreen Icons
57
+ export const Maximize2Icon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
58
+ <svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
59
+ <polyline points="15 3 21 3 21 9"></polyline>
60
+ <polyline points="9 21 3 21 3 15"></polyline>
61
+ <line x1="21" y1="3" x2="14" y2="10"></line>
62
+ <line x1="3" y1="21" x2="10" y2="14"></line>
63
+ </svg>
64
+ );
65
+
66
+ export const Minimize2Icon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
67
+ <svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
68
+ <polyline points="4 14 10 14 10 20"></polyline>
69
+ <polyline points="20 10 14 10 14 4"></polyline>
70
+ <line x1="14" y1="10" x2="21" y2="3"></line>
71
+ <line x1="3" y1="21" x2="10" y2="14"></line>
72
+ </svg>
73
+ );
74
+
75
+ export const FullscreenIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
76
+ <Maximize2Icon className={className} size={size} />
77
+ );
78
+
79
+ export const ExitFullscreenIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
80
+ <Minimize2Icon className={className} size={size} />
81
+ );
82
+
83
+ // Control Icons
84
+ export const Rewind10Icon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
85
+ <svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
86
+ <polygon points="1 4 1 10 7 7"></polygon>
87
+ <polygon points="15 4 15 10 21 7"></polygon>
88
+ <path d="M7 4a9 9 0 0 1 9 9"></path>
89
+ </svg>
90
+ );
91
+
92
+ export const Forward10Icon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
93
+ <svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
94
+ <polygon points="5 4 5 10 11 7"></polygon>
95
+ <polygon points="19 4 19 10 13 7"></polygon>
96
+ <path d="M17 20a9 9 0 0 1-9-9"></path>
97
+ </svg>
98
+ );
99
+
100
+ export const SpeedIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
101
+ <svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
102
+ <path d="M3 12h18"></path>
103
+ <path d="M3 6h18"></path>
104
+ <path d="M3 18h18"></path>
105
+ </svg>
106
+ );
107
+
108
+ export const QualityIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
109
+ <svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
110
+ <path d="M12 2L2 7l10 5 10-5-10-5z"></path>
111
+ <path d="M2 17l10 5 10-5"></path>
112
+ <path d="M2 12l10 5 10-5"></path>
113
+ </svg>
114
+ );
115
+
116
+ export const SubtitlesIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
117
+ <svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
118
+ <rect x="3" y="5" width="18" height="14" rx="2" ry="2"></rect>
119
+ <line x1="8" y1="10" x2="16" y2="10"></line>
120
+ <line x1="8" y1="14" x2="12" y2="14"></line>
121
+ </svg>
122
+ );
123
+
124
+ export const PictureInPictureIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
125
+ <svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
126
+ <path d="M8 4H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"></path>
127
+ <rect x="10" y="8" width="8" height="8" rx="1"></rect>
128
+ </svg>
129
+ );
130
+
131
+ export const DownloadIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
132
+ <svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
133
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
134
+ <polyline points="7 10 12 15 17 10"></polyline>
135
+ <line x1="12" y1="15" x2="12" y2="3"></line>
136
+ </svg>
137
+ );
138
+
139
+ export const ShareIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
140
+ <svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
141
+ <circle cx="18" cy="5" r="3"></circle>
142
+ <circle cx="6" cy="12" r="3"></circle>
143
+ <circle cx="18" cy="19" r="3"></circle>
144
+ <line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line>
145
+ <line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line>
146
+ </svg>
147
+ );
148
+
149
+ export const LoadingIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
150
+ <svg className={className + ' animate-spin'} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
151
+ <path d="M21 12a9 9 0 1 1-6.219-8.56"></path>
152
+ </svg>
153
+ );
154
+
155
+ // Error Icons
156
+ export const AlertTriangleIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
157
+ <svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
158
+ <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
159
+ <line x1="12" y1="9" x2="12" y2="13"></line>
160
+ <line x1="12" y1="17" x2="12.01" y2="17"></line>
161
+ </svg>
162
+ );
163
+
164
+ export const RefreshCcwIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
165
+ <svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
166
+ <polyline points="1 4 1 10 7 10"></polyline>
167
+ <polyline points="23 20 23 14 17 14"></polyline>
168
+ <path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15"></path>
169
+ </svg>
170
+ );
171
+
172
+ export const RotateCcwIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
173
+ <RefreshCcwIcon className={className} size={size} />
174
+ );
@@ -0,0 +1,55 @@
1
+ import React, { forwardRef } from 'react';
2
+ import { cn } from '../lib/utils';
3
+
4
+ export interface SliderProps extends React.InputHTMLAttributes<HTMLInputElement> {
5
+ min?: number;
6
+ max?: number;
7
+ step?: number;
8
+ value?: number[];
9
+ onValueChange?: (value: number[]) => void;
10
+ }
11
+
12
+ const Slider = forwardRef<HTMLInputElement, SliderProps>(
13
+ ({ className, min = 0, max = 100, step = 1, value = [0], onValueChange, ...props }, ref) => {
14
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
15
+ const newValue = [Number(e.target.value)];
16
+ onValueChange?.(newValue);
17
+ };
18
+
19
+ return (
20
+ <div className={cn('relative flex w-full touch-none select-none items-center', className)}>
21
+ <div className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary">
22
+ <div
23
+ className="absolute h-full bg-primary"
24
+ style={{
25
+ left: '0%',
26
+ right: `${100 - ((value[0] - min) / (max - min)) * 100}%`
27
+ }}
28
+ />
29
+ </div>
30
+ <input
31
+ type="range"
32
+ min={min}
33
+ max={max}
34
+ step={step}
35
+ value={value[0]}
36
+ onChange={handleChange}
37
+ className="absolute h-2 w-full cursor-pointer opacity-0 outline-none"
38
+ ref={ref}
39
+ {...props}
40
+ />
41
+ <div
42
+ className="absolute h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
43
+ style={{
44
+ left: `${((value[0] - min) / (max - min)) * 100}%`,
45
+ transform: 'translateX(-50%)'
46
+ }}
47
+ />
48
+ </div>
49
+ );
50
+ }
51
+ );
52
+
53
+ Slider.displayName = 'Slider';
54
+
55
+ export { Slider };
@@ -0,0 +1,131 @@
1
+ import React, { useState, useRef, useEffect, forwardRef } from 'react';
2
+ import { cn } from '../lib/utils';
3
+
4
+ interface TooltipProviderProps {
5
+ children: React.ReactNode;
6
+ delayDuration?: number;
7
+ }
8
+
9
+ const TooltipProvider: React.FC<TooltipProviderProps> = ({ children, delayDuration = 0 }) => {
10
+ return <>{children}</>;
11
+ };
12
+
13
+ interface TooltipProps {
14
+ children: React.ReactNode;
15
+ content: React.ReactNode;
16
+ delayDuration?: number;
17
+ sideOffset?: number;
18
+ }
19
+
20
+ const Tooltip: React.FC<TooltipProps> = ({ children, content, delayDuration = 0, sideOffset = 0 }) => {
21
+ const [isOpen, setIsOpen] = useState(false);
22
+ const [position, setPosition] = useState({ top: 0, left: 0 });
23
+ const triggerRef = useRef<HTMLDivElement>(null);
24
+ const timeoutRef = useRef<NodeJS.Timeout>();
25
+
26
+ const updatePosition = () => {
27
+ if (triggerRef.current) {
28
+ const rect = triggerRef.current.getBoundingClientRect();
29
+ setPosition({
30
+ top: rect.bottom + sideOffset,
31
+ left: rect.left + rect.width / 2
32
+ });
33
+ }
34
+ };
35
+
36
+ const showTooltip = () => {
37
+ if (timeoutRef.current) {
38
+ clearTimeout(timeoutRef.current);
39
+ }
40
+
41
+ if (delayDuration > 0) {
42
+ timeoutRef.current = setTimeout(() => {
43
+ setIsOpen(true);
44
+ updatePosition();
45
+ }, delayDuration);
46
+ } else {
47
+ setIsOpen(true);
48
+ updatePosition();
49
+ }
50
+ };
51
+
52
+ const hideTooltip = () => {
53
+ if (timeoutRef.current) {
54
+ clearTimeout(timeoutRef.current);
55
+ }
56
+ setIsOpen(false);
57
+ };
58
+
59
+ useEffect(() => {
60
+ if (isOpen) {
61
+ updatePosition();
62
+ window.addEventListener('scroll', updatePosition);
63
+ window.addEventListener('resize', updatePosition);
64
+
65
+ return () => {
66
+ window.removeEventListener('scroll', updatePosition);
67
+ window.removeEventListener('resize', updatePosition);
68
+ };
69
+ }
70
+ }, [isOpen]);
71
+
72
+ useEffect(() => {
73
+ return () => {
74
+ if (timeoutRef.current) {
75
+ clearTimeout(timeoutRef.current);
76
+ }
77
+ };
78
+ }, []);
79
+
80
+ return (
81
+ <TooltipProvider delayDuration={delayDuration}>
82
+ <div
83
+ ref={triggerRef}
84
+ onMouseEnter={showTooltip}
85
+ onMouseLeave={hideTooltip}
86
+ onFocus={showTooltip}
87
+ onBlur={hideTooltip}
88
+ >
89
+ {children}
90
+ </div>
91
+
92
+ {isOpen && (
93
+ <div
94
+ className={cn(
95
+ 'fixed z-50 px-3 py-2 text-sm bg-popover text-popover-foreground shadow-md rounded-md border',
96
+ 'max-w-xs break-words pointer-events-none'
97
+ )}
98
+ style={{
99
+ top: `${position.top}px`,
100
+ left: `${position.left}px`,
101
+ transform: 'translateX(-50%)'
102
+ }}
103
+ >
104
+ {content}
105
+ </div>
106
+ )}
107
+ </TooltipProvider>
108
+ );
109
+ };
110
+
111
+ interface TooltipContentProps {
112
+ children: React.ReactNode;
113
+ className?: string;
114
+ }
115
+
116
+ const TooltipContent: React.FC<TooltipContentProps> = ({ children, className }) => {
117
+ return <div className={cn('z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md', className)}>
118
+ {children}
119
+ </div>;
120
+ };
121
+
122
+ interface TooltipTriggerProps {
123
+ children: React.ReactNode;
124
+ asChild?: boolean;
125
+ }
126
+
127
+ const TooltipTrigger: React.FC<TooltipTriggerProps> = ({ children, asChild = false }) => {
128
+ return <>{children}</>;
129
+ };
130
+
131
+ export { Tooltip, TooltipContent, TooltipTrigger, TooltipProvider };
@@ -429,16 +429,16 @@ export function useEnhancedMediaPlayer(options: UseEnhancedMediaPlayerOptions) {
429
429
 
430
430
  // Computed state
431
431
  const computedState = useMemo(() => ({
432
- isPlaying: optimisticState?.isPlaying ?? isPlaying,
433
- currentTime: optimisticState?.currentTime ?? currentTime,
434
- duration: optimisticState?.duration ?? duration,
435
- volume: optimisticState?.volume ?? optimisticVolume,
436
- isMuted: optimisticState?.isMuted ?? isMuted,
437
- isLoading: optimisticState?.isBuffering ?? isLoading,
438
- isBuffering: optimisticState?.isBuffering ?? isBuffering,
439
- error: optimisticState?.error ?? error,
440
- buffered: optimisticState?.buffered ?? buffered,
441
- isFullscreen: optimisticState?.isFullscreen ?? isFullscreen,
432
+ isPlaying: optimisticState?.state?.isPlaying ?? isPlaying,
433
+ currentTime: optimisticState?.state?.currentTime ?? currentTime,
434
+ duration: optimisticState?.state?.duration ?? duration,
435
+ volume: optimisticState?.state?.volume ?? optimisticVolume,
436
+ isMuted: optimisticState?.state?.isMuted ?? isMuted,
437
+ isLoading: optimisticState?.state?.isBuffering ?? isLoading,
438
+ isBuffering: optimisticState?.state?.isBuffering ?? isBuffering,
439
+ error: optimisticState?.state?.error ?? error,
440
+ buffered: optimisticState?.state?.buffered ?? buffered,
441
+ isFullscreen: optimisticState?.state?.isFullscreen ?? isFullscreen,
442
442
  progress: duration > 0 ? currentTime / duration : 0,
443
443
  isTransitioning: isPending || isSeeking,
444
444
  canPlay: !isLoading && !isBuffering && !error
@@ -475,8 +475,8 @@ export function useEnhancedMediaPlayer(options: UseEnhancedMediaPlayerOptions) {
475
475
  isTransitioning: isPending || isSeeking,
476
476
 
477
477
  // Enhanced features
478
- actions: optimisticState?.actions,
479
- actionState: optimisticState?.actionState,
478
+ actions: optimisticState, // The optimisticState object contains all the actions
479
+ actionState: optimisticState?.state,
480
480
 
481
481
  // Optimistic features
482
482
  optimisticState,