@livepeer-frameworks/player-react 0.0.3

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 (88) hide show
  1. package/dist/cjs/index.js +2 -0
  2. package/dist/cjs/index.js.map +1 -0
  3. package/dist/esm/index.js +2 -0
  4. package/dist/esm/index.js.map +1 -0
  5. package/dist/types/components/DevModePanel.d.ts +47 -0
  6. package/dist/types/components/DvdLogo.d.ts +4 -0
  7. package/dist/types/components/Icons.d.ts +33 -0
  8. package/dist/types/components/IdleScreen.d.ts +16 -0
  9. package/dist/types/components/LoadingScreen.d.ts +6 -0
  10. package/dist/types/components/LogoOverlay.d.ts +11 -0
  11. package/dist/types/components/Player.d.ts +11 -0
  12. package/dist/types/components/PlayerControls.d.ts +60 -0
  13. package/dist/types/components/PlayerErrorBoundary.d.ts +23 -0
  14. package/dist/types/components/SeekBar.d.ts +33 -0
  15. package/dist/types/components/SkipIndicator.d.ts +14 -0
  16. package/dist/types/components/SpeedIndicator.d.ts +12 -0
  17. package/dist/types/components/StatsPanel.d.ts +31 -0
  18. package/dist/types/components/StreamStateOverlay.d.ts +24 -0
  19. package/dist/types/components/SubtitleRenderer.d.ts +69 -0
  20. package/dist/types/components/ThumbnailOverlay.d.ts +4 -0
  21. package/dist/types/components/TitleOverlay.d.ts +13 -0
  22. package/dist/types/components/players/DashJsPlayer.d.ts +18 -0
  23. package/dist/types/components/players/HlsJsPlayer.d.ts +18 -0
  24. package/dist/types/components/players/MewsWsPlayer/index.d.ts +18 -0
  25. package/dist/types/components/players/MistPlayer.d.ts +20 -0
  26. package/dist/types/components/players/MistWebRTCPlayer/index.d.ts +20 -0
  27. package/dist/types/components/players/NativePlayer.d.ts +19 -0
  28. package/dist/types/components/players/VideoJsPlayer.d.ts +18 -0
  29. package/dist/types/context/PlayerContext.d.ts +40 -0
  30. package/dist/types/context/index.d.ts +5 -0
  31. package/dist/types/hooks/useMetaTrack.d.ts +54 -0
  32. package/dist/types/hooks/usePlaybackQuality.d.ts +42 -0
  33. package/dist/types/hooks/usePlayerController.d.ts +163 -0
  34. package/dist/types/hooks/usePlayerSelection.d.ts +47 -0
  35. package/dist/types/hooks/useStreamState.d.ts +27 -0
  36. package/dist/types/hooks/useTelemetry.d.ts +57 -0
  37. package/dist/types/hooks/useViewerEndpoints.d.ts +14 -0
  38. package/dist/types/index.d.ts +33 -0
  39. package/dist/types/types.d.ts +94 -0
  40. package/dist/types/ui/badge.d.ts +9 -0
  41. package/dist/types/ui/button.d.ts +11 -0
  42. package/dist/types/ui/context-menu.d.ts +27 -0
  43. package/dist/types/ui/select.d.ts +10 -0
  44. package/dist/types/ui/slider.d.ts +13 -0
  45. package/package.json +71 -0
  46. package/src/assets/logomark.svg +56 -0
  47. package/src/components/DevModePanel.tsx +822 -0
  48. package/src/components/DvdLogo.tsx +201 -0
  49. package/src/components/Icons.tsx +282 -0
  50. package/src/components/IdleScreen.tsx +664 -0
  51. package/src/components/LoadingScreen.tsx +710 -0
  52. package/src/components/LogoOverlay.tsx +75 -0
  53. package/src/components/Player.tsx +419 -0
  54. package/src/components/PlayerControls.tsx +820 -0
  55. package/src/components/PlayerErrorBoundary.tsx +70 -0
  56. package/src/components/SeekBar.tsx +291 -0
  57. package/src/components/SkipIndicator.tsx +113 -0
  58. package/src/components/SpeedIndicator.tsx +57 -0
  59. package/src/components/StatsPanel.tsx +150 -0
  60. package/src/components/StreamStateOverlay.tsx +200 -0
  61. package/src/components/SubtitleRenderer.tsx +235 -0
  62. package/src/components/ThumbnailOverlay.tsx +90 -0
  63. package/src/components/TitleOverlay.tsx +48 -0
  64. package/src/components/players/DashJsPlayer.tsx +56 -0
  65. package/src/components/players/HlsJsPlayer.tsx +56 -0
  66. package/src/components/players/MewsWsPlayer/index.tsx +56 -0
  67. package/src/components/players/MistPlayer.tsx +60 -0
  68. package/src/components/players/MistWebRTCPlayer/index.tsx +59 -0
  69. package/src/components/players/NativePlayer.tsx +58 -0
  70. package/src/components/players/VideoJsPlayer.tsx +56 -0
  71. package/src/context/PlayerContext.tsx +71 -0
  72. package/src/context/index.ts +11 -0
  73. package/src/global.d.ts +4 -0
  74. package/src/hooks/useMetaTrack.ts +187 -0
  75. package/src/hooks/usePlaybackQuality.ts +126 -0
  76. package/src/hooks/usePlayerController.ts +525 -0
  77. package/src/hooks/usePlayerSelection.ts +117 -0
  78. package/src/hooks/useStreamState.ts +381 -0
  79. package/src/hooks/useTelemetry.ts +138 -0
  80. package/src/hooks/useViewerEndpoints.ts +120 -0
  81. package/src/index.tsx +75 -0
  82. package/src/player.css +2 -0
  83. package/src/types.ts +135 -0
  84. package/src/ui/badge.tsx +27 -0
  85. package/src/ui/button.tsx +47 -0
  86. package/src/ui/context-menu.tsx +193 -0
  87. package/src/ui/select.tsx +105 -0
  88. package/src/ui/slider.tsx +67 -0
package/src/index.tsx ADDED
@@ -0,0 +1,75 @@
1
+ /**
2
+ * @livepeer-frameworks/player-react
3
+ *
4
+ * React components for FrameWorks streaming player.
5
+ */
6
+
7
+ // Main player component
8
+ export { default as Player } from './components/Player';
9
+ export { default as PlayerControls } from './components/PlayerControls';
10
+
11
+ // Overlay components
12
+ export { default as LoadingScreen } from './components/LoadingScreen';
13
+ export { default as IdleScreen } from './components/IdleScreen';
14
+ export { default as ThumbnailOverlay } from './components/ThumbnailOverlay';
15
+ export { default as TitleOverlay } from './components/TitleOverlay';
16
+ export { default as StreamStateOverlay } from './components/StreamStateOverlay';
17
+ export { default as StatsPanel } from './components/StatsPanel';
18
+ export { default as DevModePanel } from './components/DevModePanel';
19
+ export { default as PlayerErrorBoundary } from './components/PlayerErrorBoundary';
20
+
21
+ // Icon components
22
+ export * from './components/Icons';
23
+
24
+ // UI primitives
25
+ export { Button } from './ui/button';
26
+ export { Badge } from './ui/badge';
27
+ export { Slider } from './ui/slider';
28
+
29
+ // Context
30
+ export { PlayerProvider, usePlayerContext, usePlayerContextOptional, PlayerContext } from './context/PlayerContext';
31
+ export type { PlayerContextValue } from './context/PlayerContext';
32
+
33
+ // Hooks
34
+ export { useStreamState } from './hooks/useStreamState';
35
+ export { usePlaybackQuality } from './hooks/usePlaybackQuality';
36
+ export { useViewerEndpoints } from './hooks/useViewerEndpoints';
37
+ export { useMetaTrack } from './hooks/useMetaTrack';
38
+ export { useTelemetry } from './hooks/useTelemetry';
39
+ export { usePlayerSelection } from './hooks/usePlayerSelection';
40
+ export type { UsePlayerSelectionOptions, UsePlayerSelectionReturn } from './hooks/usePlayerSelection';
41
+ export { usePlayerController } from './hooks/usePlayerController';
42
+ export type {
43
+ UsePlayerControllerConfig,
44
+ UsePlayerControllerReturn,
45
+ PlayerControllerState,
46
+ } from './hooks/usePlayerController';
47
+
48
+ // Types
49
+ export * from './types';
50
+
51
+ // Re-export commonly used core items
52
+ export {
53
+ PlayerManager,
54
+ globalPlayerManager,
55
+ PlayerController,
56
+ GatewayClient,
57
+ StreamStateClient,
58
+ QualityMonitor,
59
+ cn,
60
+ } from '@livepeer-frameworks/player-core';
61
+
62
+ export type {
63
+ PlayerState,
64
+ PlayerStateContext,
65
+ StreamState,
66
+ StreamStatus,
67
+ MistStreamInfo,
68
+ PlaybackQuality,
69
+ PlaybackMode,
70
+ ContentEndpoints,
71
+ EndpointInfo,
72
+ PlayerSelection,
73
+ PlayerCombination,
74
+ PlayerManagerEvents,
75
+ } from '@livepeer-frameworks/player-core';
package/src/player.css ADDED
@@ -0,0 +1,2 @@
1
+ /* Re-export core player CSS - apps import from wrapper, not core */
2
+ @import "@livepeer-frameworks/player-core/player.css";
package/src/types.ts ADDED
@@ -0,0 +1,135 @@
1
+ /**
2
+ * React-specific types for FrameWorks player
3
+ */
4
+ import type React from 'react';
5
+ import type {
6
+ PlayerOptions,
7
+ PlayerState,
8
+ PlayerStateContext,
9
+ ContentEndpoints,
10
+ MetaTrackSubscription,
11
+ PlaybackQuality,
12
+ QualityThresholds,
13
+ ContentType
14
+ } from '@livepeer-frameworks/player-core';
15
+
16
+ export interface PlayerProps {
17
+ /** Content identifier or stream name */
18
+ contentId: string;
19
+ /** Content type */
20
+ contentType: ContentType;
21
+ /** Pre-resolved endpoints/capabilities from Gateway/Foghorn */
22
+ endpoints?: ContentEndpoints;
23
+ /** Optional thumbnail/poster image */
24
+ thumbnailUrl?: string | null;
25
+ /** Unified options (branding, playback prefs, etc.) */
26
+ options?: Partial<PlayerOptions>;
27
+ /** Detailed state updates for UI (booting, gateway, connecting, playing, etc.) */
28
+ onStateChange?: (state: PlayerState, context?: PlayerStateContext) => void;
29
+ }
30
+
31
+ export interface MistPlayerProps {
32
+ streamName: string;
33
+ htmlUrl?: string;
34
+ playerJsUrl?: string;
35
+ developmentMode?: boolean;
36
+ muted?: boolean;
37
+ poster?: string;
38
+ }
39
+
40
+ export interface LoadingScreenProps {
41
+ message?: string;
42
+ }
43
+
44
+ export interface ThumbnailOverlayProps {
45
+ thumbnailUrl?: string | null | undefined;
46
+ onPlay?: () => void;
47
+ message?: string | null;
48
+ showUnmuteMessage?: boolean;
49
+ style?: React.CSSProperties;
50
+ className?: string;
51
+ }
52
+
53
+ export interface AnimatedBubbleProps {
54
+ index: number;
55
+ }
56
+
57
+ export interface CenterLogoProps {
58
+ containerRef: React.RefObject<HTMLDivElement>;
59
+ scale?: number;
60
+ onHitmarker?: (event: React.MouseEvent) => void;
61
+ }
62
+
63
+ export interface HitmarkerData {
64
+ id: number;
65
+ x: number;
66
+ y: number;
67
+ }
68
+
69
+ export interface DvdLogoProps {
70
+ parentRef: React.RefObject<HTMLDivElement>;
71
+ scale?: number;
72
+ }
73
+
74
+ // React-specific hook option types
75
+ export interface UsePlaybackQualityOptions {
76
+ videoElement: HTMLVideoElement | null;
77
+ enabled?: boolean;
78
+ sampleInterval?: number;
79
+ thresholds?: Partial<QualityThresholds>;
80
+ onQualityDegraded?: (quality: PlaybackQuality) => void;
81
+ }
82
+
83
+ export interface UseMetaTrackOptions {
84
+ mistBaseUrl: string;
85
+ streamName: string;
86
+ enabled?: boolean;
87
+ subscriptions?: MetaTrackSubscription[];
88
+ }
89
+
90
+ export interface UseStreamStateOptions {
91
+ /** MistServer base URL */
92
+ mistBaseUrl: string;
93
+ /** Stream name */
94
+ streamName: string;
95
+ /** Poll interval in ms (default: 3000) */
96
+ pollInterval?: number;
97
+ /** Enable/disable the hook (default: true) */
98
+ enabled?: boolean;
99
+ /** Use WebSocket instead of HTTP polling (default: true) */
100
+ useWebSocket?: boolean;
101
+ /** Enable debug logging (default: false) */
102
+ debug?: boolean;
103
+ }
104
+
105
+ export interface UseTelemetryOptions {
106
+ enabled?: boolean;
107
+ endpoint?: string;
108
+ authToken?: string;
109
+ interval?: number;
110
+ batchSize?: number;
111
+ contentId?: string;
112
+ contentType?: string;
113
+ playerType?: string;
114
+ protocol?: string;
115
+ }
116
+
117
+ // Re-export core types for convenience
118
+ export type {
119
+ PlayerState,
120
+ PlayerStateContext,
121
+ ContentEndpoints,
122
+ PlayerOptions,
123
+ PlaybackQuality,
124
+ TelemetryOptions,
125
+ TelemetryPayload,
126
+ MetaTrackEvent,
127
+ MetaTrackEventType,
128
+ SubtitleCue,
129
+ PlaybackMode,
130
+ MistStreamInfo,
131
+ StreamState,
132
+ StreamStatus,
133
+ EndpointInfo,
134
+ ContentMetadata,
135
+ } from '@livepeer-frameworks/player-core';
@@ -0,0 +1,27 @@
1
+ import * as React from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { cn } from "@livepeer-frameworks/player-core";
4
+
5
+ const badgeVariants = cva(
6
+ "inline-flex items-center rounded-full border border-transparent px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 ring-offset-background",
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: "bg-primary text-primary-foreground hover:bg-primary/80",
11
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
12
+ outline: "border-border text-foreground"
13
+ }
14
+ },
15
+ defaultVariants: {
16
+ variant: "default"
17
+ }
18
+ }
19
+ );
20
+
21
+ export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {}
22
+
23
+ function Badge({ className, variant, ...props }: BadgeProps) {
24
+ return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
25
+ }
26
+
27
+ export { Badge, badgeVariants };
@@ -0,0 +1,47 @@
1
+ import * as React from "react";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+ import { cn } from "@livepeer-frameworks/player-core";
5
+
6
+ const buttonVariants = cva(
7
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium 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 ring-offset-background",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
12
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
13
+ ghost: "hover:bg-accent hover:text-accent-foreground",
14
+ outline: "border border-border bg-transparent hover:bg-accent hover:text-accent-foreground",
15
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
16
+ subtle: "bg-muted text-muted-foreground hover:bg-muted/80",
17
+ link: "text-primary underline-offset-4 hover:underline"
18
+ },
19
+ size: {
20
+ default: "h-10 px-4 py-2",
21
+ sm: "h-9 rounded-md px-3",
22
+ lg: "h-11 rounded-md px-8",
23
+ icon: "h-9 w-9"
24
+ }
25
+ },
26
+ defaultVariants: {
27
+ variant: "default",
28
+ size: "default"
29
+ }
30
+ }
31
+ );
32
+
33
+ export interface ButtonProps
34
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
35
+ VariantProps<typeof buttonVariants> {
36
+ asChild?: boolean;
37
+ }
38
+
39
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
40
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
41
+ const Comp = asChild ? Slot : "button";
42
+ return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;
43
+ }
44
+ );
45
+ Button.displayName = "Button";
46
+
47
+ export { Button, buttonVariants };
@@ -0,0 +1,193 @@
1
+ import * as React from "react"
2
+ import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"
3
+ import { Check, ChevronRight, Circle } from "lucide-react"
4
+ import { cn } from "@livepeer-frameworks/player-core"
5
+
6
+ const ContextMenu = ContextMenuPrimitive.Root
7
+
8
+ const ContextMenuTrigger = ContextMenuPrimitive.Trigger
9
+
10
+ const ContextMenuGroup = ContextMenuPrimitive.Group
11
+
12
+ const ContextMenuPortal = ContextMenuPrimitive.Portal
13
+
14
+ const ContextMenuSub = ContextMenuPrimitive.Sub
15
+
16
+ const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup
17
+
18
+ const ContextMenuSubTrigger = React.forwardRef<
19
+ React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>,
20
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & {
21
+ inset?: boolean
22
+ }
23
+ >(({ className, inset, children, ...props }, ref) => (
24
+ <ContextMenuPrimitive.SubTrigger
25
+ ref={ref}
26
+ className={cn(
27
+ "fw-context-menu-item",
28
+ inset && "fw-context-menu-item--inset",
29
+ className
30
+ )}
31
+ {...props}
32
+ >
33
+ {children}
34
+ <ChevronRight className="ml-auto h-4 w-4" />
35
+ </ContextMenuPrimitive.SubTrigger>
36
+ ))
37
+ ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName
38
+
39
+ const ContextMenuSubContent = React.forwardRef<
40
+ React.ElementRef<typeof ContextMenuPrimitive.SubContent>,
41
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent>
42
+ >(({ className, ...props }, ref) => (
43
+ <ContextMenuPrimitive.SubContent
44
+ ref={ref}
45
+ className={cn("fw-player-surface fw-context-menu", className)}
46
+ {...props}
47
+ />
48
+ ))
49
+ ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName
50
+
51
+ const ContextMenuContent = React.forwardRef<
52
+ React.ElementRef<typeof ContextMenuPrimitive.Content>,
53
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content>
54
+ >(({ className, style, ...props }, ref) => (
55
+ <ContextMenuPrimitive.Portal>
56
+ <ContextMenuPrimitive.Content
57
+ ref={ref}
58
+ className={cn("fw-player-surface fw-context-menu", className)}
59
+ style={{
60
+ // Inline styles for portal elements (rendered outside .fw-player-root)
61
+ // These ensure styles work even without CSS variable inheritance
62
+ backgroundColor: 'hsl(235 19% 13%)',
63
+ color: 'hsl(229 35% 75%)',
64
+ border: '1px solid rgba(90, 96, 127, 0.3)',
65
+ ...style
66
+ }}
67
+ {...props}
68
+ />
69
+ </ContextMenuPrimitive.Portal>
70
+ ))
71
+ ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName
72
+
73
+ const ContextMenuItem = React.forwardRef<
74
+ React.ElementRef<typeof ContextMenuPrimitive.Item>,
75
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {
76
+ inset?: boolean
77
+ }
78
+ >(({ className, inset, ...props }, ref) => (
79
+ <ContextMenuPrimitive.Item
80
+ ref={ref}
81
+ className={cn(
82
+ "fw-context-menu-item",
83
+ inset && "fw-context-menu-item--inset",
84
+ className
85
+ )}
86
+ {...props}
87
+ />
88
+ ))
89
+ ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName
90
+
91
+ const ContextMenuCheckboxItem = React.forwardRef<
92
+ React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>,
93
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem>
94
+ >(({ className, children, checked, ...props }, ref) => (
95
+ <ContextMenuPrimitive.CheckboxItem
96
+ ref={ref}
97
+ className={cn("fw-context-menu-checkbox", className)}
98
+ checked={checked}
99
+ {...props}
100
+ >
101
+ <span className="fw-context-menu-indicator">
102
+ <ContextMenuPrimitive.ItemIndicator>
103
+ <Check className="h-4 w-4" />
104
+ </ContextMenuPrimitive.ItemIndicator>
105
+ </span>
106
+ {children}
107
+ </ContextMenuPrimitive.CheckboxItem>
108
+ ))
109
+ ContextMenuCheckboxItem.displayName =
110
+ ContextMenuPrimitive.CheckboxItem.displayName
111
+
112
+ const ContextMenuRadioItem = React.forwardRef<
113
+ React.ElementRef<typeof ContextMenuPrimitive.RadioItem>,
114
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem>
115
+ >(({ className, children, ...props }, ref) => (
116
+ <ContextMenuPrimitive.RadioItem
117
+ ref={ref}
118
+ className={cn("fw-context-menu-checkbox", className)}
119
+ {...props}
120
+ >
121
+ <span className="fw-context-menu-indicator">
122
+ <ContextMenuPrimitive.ItemIndicator>
123
+ <Circle className="h-2 w-2 fill-current" />
124
+ </ContextMenuPrimitive.ItemIndicator>
125
+ </span>
126
+ {children}
127
+ </ContextMenuPrimitive.RadioItem>
128
+ ))
129
+ ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName
130
+
131
+ const ContextMenuLabel = React.forwardRef<
132
+ React.ElementRef<typeof ContextMenuPrimitive.Label>,
133
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {
134
+ inset?: boolean
135
+ }
136
+ >(({ className, inset, ...props }, ref) => (
137
+ <ContextMenuPrimitive.Label
138
+ ref={ref}
139
+ className={cn(
140
+ "fw-context-menu-label",
141
+ inset && "fw-context-menu-item--inset",
142
+ className
143
+ )}
144
+ {...props}
145
+ />
146
+ ))
147
+ ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName
148
+
149
+ const ContextMenuSeparator = React.forwardRef<
150
+ React.ElementRef<typeof ContextMenuPrimitive.Separator>,
151
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator>
152
+ >(({ className, ...props }, ref) => (
153
+ <ContextMenuPrimitive.Separator
154
+ ref={ref}
155
+ className={cn("fw-context-menu-separator", className)}
156
+ {...props}
157
+ />
158
+ ))
159
+ ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName
160
+
161
+ const ContextMenuShortcut = ({
162
+ className,
163
+ ...props
164
+ }: React.HTMLAttributes<HTMLSpanElement>) => {
165
+ return (
166
+ <span
167
+ className={cn(
168
+ "ml-auto text-xs tracking-widest text-muted-foreground",
169
+ className
170
+ )}
171
+ {...props}
172
+ />
173
+ )
174
+ }
175
+ ContextMenuShortcut.displayName = "ContextMenuShortcut"
176
+
177
+ export {
178
+ ContextMenu,
179
+ ContextMenuTrigger,
180
+ ContextMenuContent,
181
+ ContextMenuItem,
182
+ ContextMenuCheckboxItem,
183
+ ContextMenuRadioItem,
184
+ ContextMenuLabel,
185
+ ContextMenuSeparator,
186
+ ContextMenuShortcut,
187
+ ContextMenuGroup,
188
+ ContextMenuPortal,
189
+ ContextMenuSub,
190
+ ContextMenuSubContent,
191
+ ContextMenuSubTrigger,
192
+ ContextMenuRadioGroup,
193
+ }
@@ -0,0 +1,105 @@
1
+ import * as SelectPrimitive from "@radix-ui/react-select";
2
+ import { Check, ChevronDown } from "lucide-react";
3
+ import * as React from "react";
4
+ import { cn } from "@livepeer-frameworks/player-core";
5
+
6
+ const Select = SelectPrimitive.Root;
7
+
8
+ const SelectValue = SelectPrimitive.Value;
9
+
10
+ const SelectTrigger = React.forwardRef<
11
+ React.ElementRef<typeof SelectPrimitive.Trigger>,
12
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
13
+ >(({ className, children, ...props }, ref) => (
14
+ <SelectPrimitive.Trigger
15
+ ref={ref}
16
+ className={cn(
17
+ "inline-flex h-9 w-full items-center justify-between gap-2 whitespace-nowrap rounded-md border border-border bg-background px-3 py-1 text-sm text-foreground shadow-sm transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
18
+ className
19
+ )}
20
+ {...props}
21
+ >
22
+ {children}
23
+ <SelectPrimitive.Icon asChild>
24
+ <ChevronDown className="fw-h-4 w-4 opacity-60" />
25
+ </SelectPrimitive.Icon>
26
+ </SelectPrimitive.Trigger>
27
+ ));
28
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
29
+
30
+ const SelectContent = React.forwardRef<
31
+ React.ElementRef<typeof SelectPrimitive.Content>,
32
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
33
+ >(({ className, children, position = "popper", ...props }, ref) => (
34
+ <SelectPrimitive.Portal>
35
+ <SelectPrimitive.Content
36
+ ref={ref}
37
+ className={cn(
38
+ // Explicit Tokyo Night colors for portal (doesn't inherit CSS vars)
39
+ "z-50 min-w-[8rem] overflow-hidden border border-[#414868]/50 bg-[#1a1b26] text-[#a9b1d6] shadow-md",
40
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
41
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1",
42
+ className
43
+ )}
44
+ style={{ backgroundColor: '#1a1b26' }} // Inline fallback for opaque background
45
+ position={position}
46
+ {...props}
47
+ >
48
+ <SelectPrimitive.Viewport className="p-1">
49
+ {children}
50
+ </SelectPrimitive.Viewport>
51
+ </SelectPrimitive.Content>
52
+ </SelectPrimitive.Portal>
53
+ ));
54
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
55
+
56
+ const SelectItem = React.forwardRef<
57
+ React.ElementRef<typeof SelectPrimitive.Item>,
58
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
59
+ >(({ className, children, ...props }, ref) => (
60
+ <SelectPrimitive.Item
61
+ ref={ref}
62
+ className={cn(
63
+ // Explicit Tokyo Night colors for portal context
64
+ "relative flex w-full cursor-pointer select-none items-center py-1.5 pl-8 pr-2 text-sm outline-none",
65
+ "focus:bg-[#292e42] focus:text-[#c0caf5] hover:bg-[#292e42]",
66
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
67
+ className
68
+ )}
69
+ {...props}
70
+ >
71
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
72
+ <SelectPrimitive.ItemIndicator>
73
+ <Check className="h-4 w-4" />
74
+ </SelectPrimitive.ItemIndicator>
75
+ </span>
76
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
77
+ </SelectPrimitive.Item>
78
+ ));
79
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
80
+
81
+ const SelectLabel = React.forwardRef<
82
+ React.ElementRef<typeof SelectPrimitive.Label>,
83
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
84
+ >(({ className, ...props }, ref) => (
85
+ <SelectPrimitive.Label ref={ref} className={cn("px-2 py-1.5 text-xs font-semibold text-muted-foreground", className)} {...props} />
86
+ ));
87
+ SelectLabel.displayName = SelectPrimitive.Label.displayName;
88
+
89
+ const SelectSeparator = React.forwardRef<
90
+ React.ElementRef<typeof SelectPrimitive.Separator>,
91
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
92
+ >(({ className, ...props }, ref) => (
93
+ <SelectPrimitive.Separator ref={ref} className={cn("-mx-1 my-1 h-px bg-muted", className)} {...props} />
94
+ ));
95
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
96
+
97
+ export {
98
+ Select,
99
+ SelectContent,
100
+ SelectItem,
101
+ SelectLabel,
102
+ SelectSeparator,
103
+ SelectTrigger,
104
+ SelectValue
105
+ };
@@ -0,0 +1,67 @@
1
+ import * as React from "react";
2
+ import * as SliderPrimitive from "@radix-ui/react-slider";
3
+ import { cn } from "@livepeer-frameworks/player-core";
4
+
5
+ export interface SliderProps extends React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root> {
6
+ showTrack?: boolean;
7
+ trackClassName?: string;
8
+ thumbClassName?: string;
9
+ /** Show thumb only on hover (YouTube-style) - but always shows a smaller thumb when not hovered */
10
+ hoverThumb?: boolean;
11
+ /** Use cyan accent color (matches SeekBar styling) */
12
+ accentColor?: boolean;
13
+ }
14
+
15
+ const Slider = React.forwardRef<React.ElementRef<typeof SliderPrimitive.Root>, SliderProps>(
16
+ ({ className, trackClassName, thumbClassName, showTrack = true, hoverThumb = false, accentColor = false, orientation = "horizontal", ...props }, ref) => {
17
+ // Colors based on accentColor prop
18
+ const rangeColorClass = accentColor ? "bg-[hsl(var(--tn-cyan,195_100%_50%))]" : "bg-white/90";
19
+ const thumbColorClass = accentColor ? "bg-[hsl(var(--tn-cyan,195_100%_50%))]" : "bg-white";
20
+
21
+ return (
22
+ <SliderPrimitive.Root
23
+ ref={ref}
24
+ orientation={orientation}
25
+ className={cn(
26
+ "group relative flex touch-none select-none items-center cursor-pointer",
27
+ orientation === "horizontal" ? "w-full h-5" : "h-full flex-col w-5",
28
+ className
29
+ )}
30
+ {...props}
31
+ >
32
+ {showTrack && (
33
+ <SliderPrimitive.Track
34
+ className={cn(
35
+ "absolute rounded-full bg-white/30 transition-all duration-150",
36
+ orientation === "horizontal"
37
+ ? "inset-x-0 h-1 group-hover:h-1.5"
38
+ : "inset-y-0 w-1 group-hover:w-1.5",
39
+ trackClassName
40
+ )}
41
+ >
42
+ <SliderPrimitive.Range
43
+ className={cn(
44
+ "absolute rounded-full transition-all duration-150",
45
+ orientation === "horizontal" ? "h-full" : "w-full bottom-0",
46
+ rangeColorClass
47
+ )}
48
+ />
49
+ </SliderPrimitive.Track>
50
+ )}
51
+ <SliderPrimitive.Thumb
52
+ className={cn(
53
+ "block rounded-full border-0 cursor-pointer shadow-md transition-all duration-150",
54
+ "w-2.5 h-2.5 group-hover:w-3.5 group-hover:h-3.5",
55
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/50",
56
+ "disabled:pointer-events-none disabled:opacity-50",
57
+ thumbColorClass,
58
+ thumbClassName
59
+ )}
60
+ />
61
+ </SliderPrimitive.Root>
62
+ );
63
+ }
64
+ );
65
+ Slider.displayName = SliderPrimitive.Root.displayName;
66
+
67
+ export { Slider };