@livepeer-frameworks/player-react 0.1.0 → 0.1.2

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 (187) hide show
  1. package/README.md +7 -9
  2. package/dist/cjs/_virtual/_rollupPluginBabelHelpers.js +359 -0
  3. package/dist/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -0
  4. package/dist/cjs/assets/logomark.svg.js +8 -0
  5. package/dist/cjs/assets/logomark.svg.js.map +1 -0
  6. package/dist/cjs/components/DevModePanel.js +826 -0
  7. package/dist/cjs/components/DevModePanel.js.map +1 -0
  8. package/dist/cjs/components/DvdLogo.js +200 -0
  9. package/dist/cjs/components/DvdLogo.js.map +1 -0
  10. package/dist/cjs/components/Icons.js +439 -0
  11. package/dist/cjs/components/Icons.js.map +1 -0
  12. package/dist/cjs/components/IdleScreen.js +587 -0
  13. package/dist/cjs/components/IdleScreen.js.map +1 -0
  14. package/dist/cjs/components/LoadingScreen.js +523 -0
  15. package/dist/cjs/components/LoadingScreen.js.map +1 -0
  16. package/dist/cjs/components/Player.js +420 -0
  17. package/dist/cjs/components/Player.js.map +1 -0
  18. package/dist/cjs/components/PlayerControls.js +798 -0
  19. package/dist/cjs/components/PlayerControls.js.map +1 -0
  20. package/dist/cjs/components/PlayerErrorBoundary.js +80 -0
  21. package/dist/cjs/components/PlayerErrorBoundary.js.map +1 -0
  22. package/dist/cjs/components/SeekBar.js +253 -0
  23. package/dist/cjs/components/SeekBar.js.map +1 -0
  24. package/dist/cjs/components/SkipIndicator.js +92 -0
  25. package/dist/cjs/components/SkipIndicator.js.map +1 -0
  26. package/dist/cjs/components/SpeedIndicator.js +43 -0
  27. package/dist/cjs/components/SpeedIndicator.js.map +1 -0
  28. package/dist/cjs/components/StatsPanel.js +202 -0
  29. package/dist/cjs/components/StatsPanel.js.map +1 -0
  30. package/dist/cjs/components/StreamStateOverlay.js +229 -0
  31. package/dist/cjs/components/StreamStateOverlay.js.map +1 -0
  32. package/dist/cjs/components/ThumbnailOverlay.js +86 -0
  33. package/dist/cjs/components/ThumbnailOverlay.js.map +1 -0
  34. package/dist/cjs/components/TitleOverlay.js +32 -0
  35. package/dist/cjs/components/TitleOverlay.js.map +1 -0
  36. package/dist/cjs/context/PlayerContext.js +46 -0
  37. package/dist/cjs/context/PlayerContext.js.map +1 -0
  38. package/dist/cjs/hooks/useMetaTrack.js +165 -0
  39. package/dist/cjs/hooks/useMetaTrack.js.map +1 -0
  40. package/dist/cjs/hooks/usePlaybackQuality.js +131 -0
  41. package/dist/cjs/hooks/usePlaybackQuality.js.map +1 -0
  42. package/dist/cjs/hooks/usePlayerController.js +518 -0
  43. package/dist/cjs/hooks/usePlayerController.js.map +1 -0
  44. package/dist/cjs/hooks/usePlayerSelection.js +90 -0
  45. package/dist/cjs/hooks/usePlayerSelection.js.map +1 -0
  46. package/dist/cjs/hooks/useStreamState.js +360 -0
  47. package/dist/cjs/hooks/useStreamState.js.map +1 -0
  48. package/dist/cjs/hooks/useTelemetry.js +120 -0
  49. package/dist/cjs/hooks/useTelemetry.js.map +1 -0
  50. package/dist/cjs/hooks/useViewerEndpoints.js +222 -0
  51. package/dist/cjs/hooks/useViewerEndpoints.js.map +1 -0
  52. package/dist/cjs/index.js +97 -1
  53. package/dist/cjs/index.js.map +1 -1
  54. package/dist/cjs/ui/badge.js +34 -0
  55. package/dist/cjs/ui/badge.js.map +1 -0
  56. package/dist/cjs/ui/button.js +74 -0
  57. package/dist/cjs/ui/button.js.map +1 -0
  58. package/dist/cjs/ui/context-menu.js +163 -0
  59. package/dist/cjs/ui/context-menu.js.map +1 -0
  60. package/dist/cjs/ui/slider.js +60 -0
  61. package/dist/cjs/ui/slider.js.map +1 -0
  62. package/dist/esm/_virtual/_rollupPluginBabelHelpers.js +329 -0
  63. package/dist/esm/_virtual/_rollupPluginBabelHelpers.js.map +1 -0
  64. package/dist/esm/assets/logomark.svg.js +4 -0
  65. package/dist/esm/assets/logomark.svg.js.map +1 -0
  66. package/dist/esm/components/DevModePanel.js +822 -0
  67. package/dist/esm/components/DevModePanel.js.map +1 -0
  68. package/dist/esm/components/DvdLogo.js +196 -0
  69. package/dist/esm/components/DvdLogo.js.map +1 -0
  70. package/dist/esm/components/Icons.js +421 -0
  71. package/dist/esm/components/Icons.js.map +1 -0
  72. package/dist/esm/components/IdleScreen.js +582 -0
  73. package/dist/esm/components/IdleScreen.js.map +1 -0
  74. package/dist/esm/components/LoadingScreen.js +519 -0
  75. package/dist/esm/components/LoadingScreen.js.map +1 -0
  76. package/dist/esm/components/Player.js +416 -0
  77. package/dist/esm/components/Player.js.map +1 -0
  78. package/dist/esm/components/PlayerControls.js +794 -0
  79. package/dist/esm/components/PlayerControls.js.map +1 -0
  80. package/dist/esm/components/PlayerErrorBoundary.js +76 -0
  81. package/dist/esm/components/PlayerErrorBoundary.js.map +1 -0
  82. package/dist/esm/components/SeekBar.js +249 -0
  83. package/dist/esm/components/SeekBar.js.map +1 -0
  84. package/dist/esm/components/SkipIndicator.js +88 -0
  85. package/dist/esm/components/SkipIndicator.js.map +1 -0
  86. package/dist/esm/components/SpeedIndicator.js +39 -0
  87. package/dist/esm/components/SpeedIndicator.js.map +1 -0
  88. package/dist/esm/components/StatsPanel.js +198 -0
  89. package/dist/esm/components/StatsPanel.js.map +1 -0
  90. package/dist/esm/components/StreamStateOverlay.js +224 -0
  91. package/dist/esm/components/StreamStateOverlay.js.map +1 -0
  92. package/dist/esm/components/ThumbnailOverlay.js +82 -0
  93. package/dist/esm/components/ThumbnailOverlay.js.map +1 -0
  94. package/dist/esm/components/TitleOverlay.js +28 -0
  95. package/dist/esm/components/TitleOverlay.js.map +1 -0
  96. package/dist/esm/context/PlayerContext.js +41 -0
  97. package/dist/esm/context/PlayerContext.js.map +1 -0
  98. package/dist/esm/hooks/useMetaTrack.js +163 -0
  99. package/dist/esm/hooks/useMetaTrack.js.map +1 -0
  100. package/dist/esm/hooks/usePlaybackQuality.js +129 -0
  101. package/dist/esm/hooks/usePlaybackQuality.js.map +1 -0
  102. package/dist/esm/hooks/usePlayerController.js +516 -0
  103. package/dist/esm/hooks/usePlayerController.js.map +1 -0
  104. package/dist/esm/hooks/usePlayerSelection.js +88 -0
  105. package/dist/esm/hooks/usePlayerSelection.js.map +1 -0
  106. package/dist/esm/hooks/useStreamState.js +358 -0
  107. package/dist/esm/hooks/useStreamState.js.map +1 -0
  108. package/dist/esm/hooks/useTelemetry.js +118 -0
  109. package/dist/esm/hooks/useTelemetry.js.map +1 -0
  110. package/dist/esm/hooks/useViewerEndpoints.js +220 -0
  111. package/dist/esm/hooks/useViewerEndpoints.js.map +1 -0
  112. package/dist/esm/index.js +23 -1
  113. package/dist/esm/index.js.map +1 -1
  114. package/dist/esm/ui/badge.js +31 -0
  115. package/dist/esm/ui/badge.js.map +1 -0
  116. package/dist/esm/ui/button.js +52 -0
  117. package/dist/esm/ui/button.js.map +1 -0
  118. package/dist/esm/ui/context-menu.js +132 -0
  119. package/dist/esm/ui/context-menu.js.map +1 -0
  120. package/dist/esm/ui/slider.js +38 -0
  121. package/dist/esm/ui/slider.js.map +1 -0
  122. package/dist/types/components/DvdLogo.d.ts +1 -1
  123. package/dist/types/components/Icons.d.ts +1 -1
  124. package/dist/types/components/Player.d.ts +1 -1
  125. package/dist/types/components/PlayerErrorBoundary.d.ts +2 -1
  126. package/dist/types/components/StreamStateOverlay.d.ts +2 -2
  127. package/dist/types/components/SubtitleRenderer.d.ts +2 -2
  128. package/dist/types/context/PlayerContext.d.ts +2 -2
  129. package/dist/types/context/index.d.ts +2 -2
  130. package/dist/types/hooks/useMetaTrack.d.ts +3 -3
  131. package/dist/types/hooks/usePlaybackQuality.d.ts +2 -2
  132. package/dist/types/hooks/usePlayerController.d.ts +26 -3
  133. package/dist/types/hooks/usePlayerSelection.d.ts +1 -1
  134. package/dist/types/hooks/useStreamState.d.ts +1 -1
  135. package/dist/types/hooks/useTelemetry.d.ts +1 -1
  136. package/dist/types/hooks/useViewerEndpoints.d.ts +3 -3
  137. package/dist/types/index.d.ts +28 -28
  138. package/dist/types/types.d.ts +3 -3
  139. package/dist/types/ui/select.d.ts +1 -1
  140. package/package.json +22 -14
  141. package/src/components/DevModePanel.tsx +244 -143
  142. package/src/components/DvdLogo.tsx +1 -1
  143. package/src/components/Icons.tsx +105 -25
  144. package/src/components/IdleScreen.tsx +262 -128
  145. package/src/components/LoadingScreen.tsx +169 -151
  146. package/src/components/LogoOverlay.tsx +3 -6
  147. package/src/components/Player.tsx +126 -59
  148. package/src/components/PlayerControls.tsx +384 -272
  149. package/src/components/PlayerErrorBoundary.tsx +7 -13
  150. package/src/components/SeekBar.tsx +96 -88
  151. package/src/components/SkipIndicator.tsx +2 -12
  152. package/src/components/SpeedIndicator.tsx +2 -11
  153. package/src/components/StatsPanel.tsx +31 -22
  154. package/src/components/StreamStateOverlay.tsx +105 -49
  155. package/src/components/SubtitleRenderer.tsx +29 -29
  156. package/src/components/ThumbnailOverlay.tsx +5 -6
  157. package/src/components/TitleOverlay.tsx +2 -8
  158. package/src/context/PlayerContext.tsx +4 -8
  159. package/src/context/index.ts +3 -3
  160. package/src/hooks/useMetaTrack.ts +27 -27
  161. package/src/hooks/usePlaybackQuality.ts +3 -3
  162. package/src/hooks/usePlayerController.ts +246 -138
  163. package/src/hooks/usePlayerSelection.ts +6 -6
  164. package/src/hooks/useStreamState.ts +51 -56
  165. package/src/hooks/useTelemetry.ts +18 -3
  166. package/src/hooks/useViewerEndpoints.ts +34 -23
  167. package/src/index.tsx +36 -28
  168. package/src/types.ts +8 -8
  169. package/src/ui/badge.tsx +6 -5
  170. package/src/ui/button.tsx +9 -8
  171. package/src/ui/context-menu.tsx +42 -61
  172. package/src/ui/select.tsx +13 -7
  173. package/src/ui/slider.tsx +18 -29
  174. package/dist/types/components/players/DashJsPlayer.d.ts +0 -18
  175. package/dist/types/components/players/HlsJsPlayer.d.ts +0 -18
  176. package/dist/types/components/players/MewsWsPlayer/index.d.ts +0 -18
  177. package/dist/types/components/players/MistPlayer.d.ts +0 -20
  178. package/dist/types/components/players/MistWebRTCPlayer/index.d.ts +0 -20
  179. package/dist/types/components/players/NativePlayer.d.ts +0 -19
  180. package/dist/types/components/players/VideoJsPlayer.d.ts +0 -18
  181. package/src/components/players/DashJsPlayer.tsx +0 -56
  182. package/src/components/players/HlsJsPlayer.tsx +0 -56
  183. package/src/components/players/MewsWsPlayer/index.tsx +0 -56
  184. package/src/components/players/MistPlayer.tsx +0 -60
  185. package/src/components/players/MistWebRTCPlayer/index.tsx +0 -59
  186. package/src/components/players/NativePlayer.tsx +0 -58
  187. package/src/components/players/VideoJsPlayer.tsx +0 -56
@@ -27,20 +27,23 @@ const AnimatedBubble: React.FC<AnimatedBubbleProps> = ({ index }) => {
27
27
  // Fade in
28
28
  setOpacity(0.15);
29
29
 
30
- setTimeout(() => {
31
- // Fade out
32
- setOpacity(0);
33
-
34
- setTimeout(() => {
35
- // Move to new random position with new size while invisible
36
- setPosition(getRandomPosition());
37
- setSize(getRandomSize());
38
- // Start next cycle after a brief delay to ensure position change is complete
30
+ setTimeout(
31
+ () => {
32
+ // Fade out
33
+ setOpacity(0);
34
+
39
35
  setTimeout(() => {
40
- animationCycle();
41
- }, 200);
42
- }, 1500); // Wait for fade out to complete
43
- }, 4000 + Math.random() * 3000); // Stay visible for 4-7 seconds (was 2-4)
36
+ // Move to new random position with new size while invisible
37
+ setPosition(getRandomPosition());
38
+ setSize(getRandomSize());
39
+ // Start next cycle after a brief delay to ensure position change is complete
40
+ setTimeout(() => {
41
+ animationCycle();
42
+ }, 200);
43
+ }, 1500); // Wait for fade out to complete
44
+ },
45
+ 4000 + Math.random() * 3000
46
+ ); // Stay visible for 4-7 seconds (was 2-4)
44
47
  };
45
48
 
46
49
  // Start the animation cycle with staggered timing
@@ -55,32 +58,34 @@ const AnimatedBubble: React.FC<AnimatedBubbleProps> = ({ index }) => {
55
58
  const bubbleColors = [
56
59
  "rgba(122, 162, 247, 0.2)", // Terminal Blue
57
60
  "rgba(187, 154, 247, 0.2)", // Terminal Magenta
58
- "rgba(158, 206, 106, 0.2)", // Strings/CSS classes
61
+ "rgba(158, 206, 106, 0.2)", // Strings/CSS classes
59
62
  "rgba(115, 218, 202, 0.2)", // Terminal Green
60
63
  "rgba(125, 207, 255, 0.2)", // Terminal Cyan
61
64
  "rgba(247, 118, 142, 0.2)", // Keywords/Terminal Red
62
65
  "rgba(224, 175, 104, 0.2)", // Terminal Yellow
63
- "rgba(42, 195, 222, 0.2)", // Language functions
66
+ "rgba(42, 195, 222, 0.2)", // Language functions
64
67
  ];
65
68
 
66
69
  return (
67
70
  <div
68
- style={{
69
- position: "absolute",
70
- top: `${position.top}%`,
71
- left: `${position.left}%`,
72
- width: `${size}px`,
73
- height: `${size}px`,
74
- borderRadius: "50%",
75
- background: bubbleColors[index % bubbleColors.length],
76
- opacity: opacity,
77
- transition: "opacity 1s ease-in-out",
78
- pointerEvents: "none",
79
- userSelect: "none",
80
- WebkitUserSelect: "none",
81
- MozUserSelect: "none",
82
- msUserSelect: "none",
83
- } as React.CSSProperties}
71
+ style={
72
+ {
73
+ position: "absolute",
74
+ top: `${position.top}%`,
75
+ left: `${position.left}%`,
76
+ width: `${size}px`,
77
+ height: `${size}px`,
78
+ borderRadius: "50%",
79
+ background: bubbleColors[index % bubbleColors.length],
80
+ opacity: opacity,
81
+ transition: "opacity 1s ease-in-out",
82
+ pointerEvents: "none",
83
+ userSelect: "none",
84
+ WebkitUserSelect: "none",
85
+ MozUserSelect: "none",
86
+ msUserSelect: "none",
87
+ } as React.CSSProperties
88
+ }
84
89
  />
85
90
  );
86
91
  };
@@ -154,12 +159,12 @@ const CenterLogo: React.FC<CenterLogoProps> = ({ containerRef, scale = 0.2, onHi
154
159
  useEffect(() => {
155
160
  if (containerRef.current) {
156
161
  const container = containerRef.current;
157
- container.addEventListener('mousemove', handleMouseMove);
158
- container.addEventListener('mouseleave', handleMouseLeave);
162
+ container.addEventListener("mousemove", handleMouseMove);
163
+ container.addEventListener("mouseleave", handleMouseLeave);
159
164
 
160
165
  return () => {
161
- container.removeEventListener('mousemove', handleMouseMove);
162
- container.removeEventListener('mouseleave', handleMouseLeave);
166
+ container.removeEventListener("mousemove", handleMouseMove);
167
+ container.removeEventListener("mouseleave", handleMouseLeave);
163
168
  };
164
169
  }
165
170
  }, [logoSize, containerRef]);
@@ -190,7 +195,9 @@ const CenterLogo: React.FC<CenterLogoProps> = ({ containerRef, scale = 0.2, onHi
190
195
  height: `${logoSize * 1.4}px`,
191
196
  borderRadius: "50%",
192
197
  background: "rgba(122, 162, 247, 0.15)",
193
- animation: isHovered ? "logoPulse 1s ease-in-out infinite" : "logoPulse 3s ease-in-out infinite",
198
+ animation: isHovered
199
+ ? "logoPulse 1s ease-in-out infinite"
200
+ : "logoPulse 3s ease-in-out infinite",
194
201
  transform: isHovered ? "scale(1.2)" : "scale(1)",
195
202
  transition: "transform 0.3s ease-out",
196
203
  userSelect: "none",
@@ -206,24 +213,26 @@ const CenterLogo: React.FC<CenterLogoProps> = ({ containerRef, scale = 0.2, onHi
206
213
  src={logomarkAsset}
207
214
  alt="FrameWorks Logo"
208
215
  onClick={handleLogoClick}
209
- style={{
210
- width: `${logoSize}px`,
211
- height: `${logoSize}px`,
212
- position: "relative",
213
- zIndex: 1,
214
- filter: isHovered
215
- ? "drop-shadow(0 6px 12px rgba(36, 40, 59, 0.4)) brightness(1.1)"
216
- : "drop-shadow(0 4px 8px rgba(36, 40, 59, 0.3))",
217
- transform: isHovered ? "scale(1.1)" : "scale(1)",
218
- transition: "all 0.3s ease-out",
219
- cursor: isHovered ? "pointer" : "default",
220
- userSelect: "none",
221
- WebkitUserSelect: "none",
222
- MozUserSelect: "none",
223
- msUserSelect: "none",
224
- WebkitUserDrag: "none",
225
- WebkitTouchCallout: "none",
226
- } as React.CSSProperties}
216
+ style={
217
+ {
218
+ width: `${logoSize}px`,
219
+ height: `${logoSize}px`,
220
+ position: "relative",
221
+ zIndex: 1,
222
+ filter: isHovered
223
+ ? "drop-shadow(0 6px 12px rgba(36, 40, 59, 0.4)) brightness(1.1)"
224
+ : "drop-shadow(0 4px 8px rgba(36, 40, 59, 0.3))",
225
+ transform: isHovered ? "scale(1.1)" : "scale(1)",
226
+ transition: "all 0.3s ease-out",
227
+ cursor: isHovered ? "pointer" : "default",
228
+ userSelect: "none",
229
+ WebkitUserSelect: "none",
230
+ MozUserSelect: "none",
231
+ msUserSelect: "none",
232
+ WebkitUserDrag: "none",
233
+ WebkitTouchCallout: "none",
234
+ } as React.CSSProperties
235
+ }
227
236
  />
228
237
  </div>
229
238
  );
@@ -246,51 +255,52 @@ const LoadingScreen: React.FC<LoadingScreenProps> = ({ message = "Waiting for so
246
255
  const playHitmarkerSound = () => {
247
256
  try {
248
257
  // Embedded hitmarker sound as base64 data URL
249
- const hitmarkerDataUrl = 'data:audio/mpeg;base64,SUQzBAAAAAAANFRDT04AAAAHAAADT3RoZXIAVFNTRQAAAA8AAANMYXZmNTcuODMuMTAwAAAAAAAAAAAA' +
250
- 'AAD/+1QAAAAAAAAAAAAAAAAAAAAA' +
251
- 'AAAAAAAAAAAAAAAAAAAAAABJbmZvAAAADwAAAAYAAAnAADs7Ozs7Ozs7Ozs7Ozs7OztiYmJiYmJiYmJi' +
252
- 'YmJiYmJiYomJiYmJiYmJiYmJiYmJiYmxsbGxsbGxsbGxsbGxsbGxsdjY2NjY2NjY2NjY2NjY2NjY////' +
253
- '/////////////////wAAAABMYXZjNTcuMTAAAAAAAAAAAAAAAAAkAkAAAAAAAAAJwOuMZun/+5RkAA8S' +
254
- '/F23AGAaAi0AF0AAAAAInXsEAIRXyQ8D4OQgjEhE3cO7ujuHF0XCOu4G7xKbi3Funu7u7p9dw7unu7u7' +
255
- 'p7u7u6fXcW7om7u7uiU3dxdT67u7p7uHdxelN3cW6fXcW7oXXd3eJTd3d0+u4t3iXdw4up70W4uiPruL' +
256
- 'DzMw8Pz79Y99JfkyfPv5/h9uTJoy79Y99Y97q3vyZPJk0ZfrL6x73Vn+J35dKKS/STQyQ8CAiCPNuRAO' +
257
- 'OqquAx+fzJeBKDAsgAMBuWcBsHKhjJTcCwIALyAvABbI0ZIcCmP8jHJe8gZAdVRp2TpnU/kUXV4iQuBA' +
258
- 'AkAQgisLPvwQ2Jz7wIkIpQ8QOl/KFy75w+2HpTFnRqXLQo0fzlSYRe5Ce9yZMEzRM4xesu95Mo8QQsoM' +
259
- 'H4gLg+fJqkmY3GZJE2kwGfMECJiAdIttoEa2yotfC7jsS2mjKgbzAfEMeiwZpGSUFCQwPKQiWXh0TnkN' +
260
- 'or5SmrKvwHlX2zFxKxPCzRL/+5RkIwADvUxLawwb0GdF6Y1hJlgNNJk+DSRwyQwI6AD2JCiBmhaff0dz' +
261
- 'CEBjgFABAcDNFc3YAEV4hQn0L/QvQnevom+n13eIjoTvABLrHg/L9RzdWXYonHbbbE2K0pX+gkL2g56R' +
262
- 'iwrbuWwhoABzQoMKOAIGAfE4UKk6BhSIJpECBq0CEYmZKYIiAJt72H24dNou7y/Ee7a/3v+MgySemSTY' +
263
- 'mnBAFwIAAGfCJ8/D9YfkwQEBcP38uA1d/EB1T5dZKEsgnuhwZirY5fIMRMdRn7U4OcN2m5NWeYdcPBwX' +
264
- 'DBOsJF1DBYks62pAURqz1hGoGHH/QIoRC80tYAJ8g4f3MPD51sywAbhAn/X9P/75tvZww3gZ3pYPDx/+' +
265
- 'ACO/7//ffHj/D/AAfATC4DYGFA3MRABo0lqWjBOl2yAda1C1BdhduXgm8FGnAQB/lDiEi6j9qw9EHigI' +
266
- 'IOLB6F1eIPd+T6Agc4//lMo6+k3tdttJY2gArU7cN07m2FLSm4gCjyz/+5RECwACwSRZawkdLFGi2mVh' +
267
- '5h4LfFdPVPGACViTavaeMAAV0UkkEsDhxxJwqF04on002mZah8w9+5ItfSAoyZa1dchnPpLmAEKrVMRA' +
268
- '//sD8w0WsB4xiw4JqaZMB45TdpIuXXUPf8Bpa35p/jQIAOAuZkmUeJoM5W6L2gqqO6rTuHjUTDnhy4Qi' +
269
- 'K348vtFysOizShoHbBpsPRYcSINCbiN4XOLPPAgq3dW2Ga7SlyiKXBV7W1RQl5BiiVGkwayJfEnPxgXk' +
270
- 'QeZxxzyhTuLO2XFUDDstoc6CkM1J8QZAjUN3bM8580cRygNfmPAELGjIH0Z/0A+8csyH/4eHvgAf8APg' +
271
- 'ABmZ98AARAADP////Dw8PHEmIpgGttpJQJsmZjq5nPQ8j5VqWW1evqdjP182PA6tHJZgkC5iSbEQkyJS' +
272
- 'z/BvP3eucLKN0+Wiza4feKKFBqiAEBAMXyYni5NZc16CDl/QY9j6BAcWSmQYcIcoMHYoQNBiIBgIBUAz' +
273
- 'QUMSnjj/+5RkCwADsFLffjEAAjrJe63JHACO6WtlnPMACKaCK1uMMADU5dI6JhW2cam98UlRmY4ihyKF' +
274
- 'rNsgpZd5PYgBALnYofKEt82De0GbW1DLibvFDK+bSeOm8qKdqUFZ7uiK8XMPHyqm3pTxUvcunUfxXEo9' +
275
- 'RNe5b/8vfCD3kzDN7vTtHyaIcntVDAYBAUBAAAAQBI2vguYNsHWm5AR3mZtZib8WAHFvz2Kf9//iYvlR' +
276
- 'B/+n///////////+UH7XoIDMoJAEAMtj8JshJPRwklVqNSpYnalfE+VzNCAISCoxVHEpIo/WrTiMvP7V' +
277
- 'TujOPnOglLbMLN/pq/d2Y4lRJIkSnPlUSJEjSKJqM41d88zWtMzP+fCOORmc9NeM+f1nnO//efM52/fG' +
278
- '/ef385+5u+u1bRJkwU8FAkEItZpkRYeQYcAgZTEYlaZa2yROLeC0qdX73rZJJ/d2f6v6Or0u/+5FBYcn' +
279
- 'g0MlCiQTR9GUU5LScmSuSlH00IWqXA6jlw4BEcD/+5REEAAi3RtU+eYbGF1E+lk9g0YJzLUgh7BlQVGT' +
280
- 'ZJD0jKhhTNVilqrMzFRK+x/szcMKBWKep4NP1A0DR6RESkTp5Z1Q9Y8REgqMg1DpUBPleeqlRQcerBpM' +
281
- 'jiURHVD4XwAALhAgbxxlxYD5OFkG8oQRPB2EpsxSCNVlgcYUqoAyiVJmaARlkwplICfPoUy/zWEzM2pc' +
282
- 'NYzAQNJDSniEYecSEqxFEzQqEvUFGnvzwUfcRlpZ9T2LCR5QdDQDDhKICAjpJCagpRo9UQRPClZZlg6E' +
283
- 'p9DMTkTl+okuhRIVIzAQEf9L+Mx/DUjqmqN6kX7M36lS4zgLyJV3iV6j3xF8kJduJawVw1nndAlBaLLg' +
284
- 'JupwsTcLkxmJgFLgSzoCmHjSNGSqkGPCpnNqTXIwolf6qlVWN+q/su37HzgrES1pWGg3KnWh0FXCVniJ' +
285
- '9K5b4iCrpLEuIcFTqwkVLFiqgaDqCCSMVWqxBAVCFOLVrVahm2ahUThUKJnmFCw15hD0Qhb/+5REEAhC' +
286
- 'YSRCSQEb4FOGaBUMI6JIRYC0QIB2SQsgGpgwDghgIlS6FU8VBXDoiBp5Y9gtkVnhEhYBdJFQ7kQ3w1yp' +
287
- '0NB2CoNPEttZ1/aeDUAAA26FEghWgEKNVAVWkFAQEmMK2Uwk/qI0hqUb/4epVIZH1ai6szf6kzH1f2ar' +
288
- 'xYGS9FcOsN5UlJLQt///+oo0FRDTUQ0FBQr9f5LxXP+mEUfk0AIrf/5GRmQ0//mX//ZbLP5b5GrWSz+W' +
289
- 'SkZMrWyyyy2GRqyggVRyMv////////st//sn/yyVDI1l8mVgoYGDCOqiqIQBxmvxWCggTpZZZD//aWfy' +
290
- 'yWf/y/7KGDA0ssBggTof9k/+WS/8slQyMp/5Nfln8WAqGcUbULCrKxT9ISF+kKsxQWpMQU1FMy4xMDCq' +
291
- 'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq' +
292
- 'qqqqqqqqqqqqqqqqqqqqqqqqqqo=';
293
-
258
+ const hitmarkerDataUrl =
259
+ "data:audio/mpeg;base64,SUQzBAAAAAAANFRDT04AAAAHAAADT3RoZXIAVFNTRQAAAA8AAANMYXZmNTcuODMuMTAwAAAAAAAAAAAA" +
260
+ "AAD/+1QAAAAAAAAAAAAAAAAAAAAA" +
261
+ "AAAAAAAAAAAAAAAAAAAAAABJbmZvAAAADwAAAAYAAAnAADs7Ozs7Ozs7Ozs7Ozs7OztiYmJiYmJiYmJi" +
262
+ "YmJiYmJiYomJiYmJiYmJiYmJiYmJiYmxsbGxsbGxsbGxsbGxsbGxsdjY2NjY2NjY2NjY2NjY2NjY////" +
263
+ "/////////////////wAAAABMYXZjNTcuMTAAAAAAAAAAAAAAAAAkAkAAAAAAAAAJwOuMZun/+5RkAA8S" +
264
+ "/F23AGAaAi0AF0AAAAAInXsEAIRXyQ8D4OQgjEhE3cO7ujuHF0XCOu4G7xKbi3Funu7u7p9dw7unu7u7" +
265
+ "p7u7u6fXcW7om7u7uiU3dxdT67u7p7uHdxelN3cW6fXcW7oXXd3eJTd3d0+u4t3iXdw4up70W4uiPruL" +
266
+ "DzMw8Pz79Y99JfkyfPv5/h9uTJoy79Y99Y97q3vyZPJk0ZfrL6x73Vn+J35dKKS/STQyQ8CAiCPNuRAO" +
267
+ "OqquAx+fzJeBKDAsgAMBuWcBsHKhjJTcCwIALyAvABbI0ZIcCmP8jHJe8gZAdVRp2TpnU/kUXV4iQuBA" +
268
+ "AkAQgisLPvwQ2Jz7wIkIpQ8QOl/KFy75w+2HpTFnRqXLQo0fzlSYRe5Ce9yZMEzRM4xesu95Mo8QQsoM" +
269
+ "H4gLg+fJqkmY3GZJE2kwGfMECJiAdIttoEa2yotfC7jsS2mjKgbzAfEMeiwZpGSUFCQwPKQiWXh0TnkN" +
270
+ "or5SmrKvwHlX2zFxKxPCzRL/+5RkIwADvUxLawwb0GdF6Y1hJlgNNJk+DSRwyQwI6AD2JCiBmhaff0dz" +
271
+ "CEBjgFABAcDNFc3YAEV4hQn0L/QvQnevom+n13eIjoTvABLrHg/L9RzdWXYonHbbbE2K0pX+gkL2g56R" +
272
+ "iwrbuWwhoABzQoMKOAIGAfE4UKk6BhSIJpECBq0CEYmZKYIiAJt72H24dNou7y/Ee7a/3v+MgySemSTY" +
273
+ "mnBAFwIAAGfCJ8/D9YfkwQEBcP38uA1d/EB1T5dZKEsgnuhwZirY5fIMRMdRn7U4OcN2m5NWeYdcPBwX" +
274
+ "DBOsJF1DBYks62pAURqz1hGoGHH/QIoRC80tYAJ8g4f3MPD51sywAbhAn/X9P/75tvZww3gZ3pYPDx/+" +
275
+ "ACO/7//ffHj/D/AAfATC4DYGFA3MRABo0lqWjBOl2yAda1C1BdhduXgm8FGnAQB/lDiEi6j9qw9EHigI" +
276
+ "IOLB6F1eIPd+T6Agc4//lMo6+k3tdttJY2gArU7cN07m2FLSm4gCjyz/+5RECwACwSRZawkdLFGi2mVh" +
277
+ "5h4LfFdPVPGACViTavaeMAAV0UkkEsDhxxJwqF04on002mZah8w9+5ItfSAoyZa1dchnPpLmAEKrVMRA" +
278
+ "//sD8w0WsB4xiw4JqaZMB45TdpIuXXUPf8Bpa35p/jQIAOAuZkmUeJoM5W6L2gqqO6rTuHjUTDnhy4Qi" +
279
+ "K348vtFysOizShoHbBpsPRYcSINCbiN4XOLPPAgq3dW2Ga7SlyiKXBV7W1RQl5BiiVGkwayJfEnPxgXk" +
280
+ "QeZxxzyhTuLO2XFUDDstoc6CkM1J8QZAjUN3bM8580cRygNfmPAELGjIH0Z/0A+8csyH/4eHvgAf8APg" +
281
+ "ABmZ98AARAADP////Dw8PHEmIpgGttpJQJsmZjq5nPQ8j5VqWW1evqdjP182PA6tHJZgkC5iSbEQkyJS" +
282
+ "z/BvP3eucLKN0+Wiza4feKKFBqiAEBAMXyYni5NZc16CDl/QY9j6BAcWSmQYcIcoMHYoQNBiIBgIBUAz" +
283
+ "QUMSnjj/+5RkCwADsFLffjEAAjrJe63JHACO6WtlnPMACKaCK1uMMADU5dI6JhW2cam98UlRmY4ihyKF" +
284
+ "rNsgpZd5PYgBALnYofKEt82De0GbW1DLibvFDK+bSeOm8qKdqUFZ7uiK8XMPHyqm3pTxUvcunUfxXEo9" +
285
+ "RNe5b/8vfCD3kzDN7vTtHyaIcntVDAYBAUBAAAAQBI2vguYNsHWm5AR3mZtZib8WAHFvz2Kf9//iYvlR" +
286
+ "B/+n///////////+UH7XoIDMoJAEAMtj8JshJPRwklVqNSpYnalfE+VzNCAISCoxVHEpIo/WrTiMvP7V" +
287
+ "TujOPnOglLbMLN/pq/d2Y4lRJIkSnPlUSJEjSKJqM41d88zWtMzP+fCOORmc9NeM+f1nnO//efM52/fG" +
288
+ "/ef385+5u+u1bRJkwU8FAkEItZpkRYeQYcAgZTEYlaZa2yROLeC0qdX73rZJJ/d2f6v6Or0u/+5FBYcn" +
289
+ "g0MlCiQTR9GUU5LScmSuSlH00IWqXA6jlw4BEcD/+5REEAAi3RtU+eYbGF1E+lk9g0YJzLUgh7BlQVGT" +
290
+ "ZJD0jKhhTNVilqrMzFRK+x/szcMKBWKep4NP1A0DR6RESkTp5Z1Q9Y8REgqMg1DpUBPleeqlRQcerBpM" +
291
+ "jiURHVD4XwAALhAgbxxlxYD5OFkG8oQRPB2EpsxSCNVlgcYUqoAyiVJmaARlkwplICfPoUy/zWEzM2pc" +
292
+ "NYzAQNJDSniEYecSEqxFEzQqEvUFGnvzwUfcRlpZ9T2LCR5QdDQDDhKICAjpJCagpRo9UQRPClZZlg6E" +
293
+ "p9DMTkTl+okuhRIVIzAQEf9L+Mx/DUjqmqN6kX7M36lS4zgLyJV3iV6j3xF8kJduJawVw1nndAlBaLLg" +
294
+ "JupwsTcLkxmJgFLgSzoCmHjSNGSqkGPCpnNqTXIwolf6qlVWN+q/su37HzgrES1pWGg3KnWh0FXCVniJ" +
295
+ "9K5b4iCrpLEuIcFTqwkVLFiqgaDqCCSMVWqxBAVCFOLVrVahm2ahUThUKJnmFCw15hD0Qhb/+5REEAhC" +
296
+ "YSRCSQEb4FOGaBUMI6JIRYC0QIB2SQsgGpgwDghgIlS6FU8VBXDoiBp5Y9gtkVnhEhYBdJFQ7kQ3w1yp" +
297
+ "0NB2CoNPEttZ1/aeDUAAA26FEghWgEKNVAVWkFAQEmMK2Uwk/qI0hqUb/4epVIZH1ai6szf6kzH1f2ar" +
298
+ "xYGS9FcOsN5UlJLQt///+oo0FRDTUQ0FBQr9f5LxXP+mEUfk0AIrf/5GRmQ0//mX//ZbLP5b5GrWSz+W" +
299
+ "SkZMrWyyyy2GRqyggVRyMv////////st//sn/yyVDI1l8mVgoYGDCOqiqIQBxmvxWCggTpZZZD//aWfy" +
300
+ "yWf/y/7KGDA0ssBggTof9k/+WS/8slQyMp/5Nfln8WAqGcUbULCrKxT9ISF+kKsxQWpMQU1FMy4xMDCq" +
301
+ "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" +
302
+ "qqqqqqqqqqqqqqqqqqqqqqqqqqo=";
303
+
294
304
  const audio = new Audio(hitmarkerDataUrl);
295
305
  audio.volume = 0.3;
296
306
  audio.play().catch(() => {
@@ -306,106 +316,109 @@ const LoadingScreen: React.FC<LoadingScreenProps> = ({ message = "Waiting for so
306
316
  const createSyntheticHitmarkerSound = () => {
307
317
  try {
308
318
  const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
309
-
319
+
310
320
  // Create a more realistic hitmarker sound with noise and metallic ring
311
321
  const oscillator1 = audioContext.createOscillator();
312
322
  const oscillator2 = audioContext.createOscillator();
313
- const noiseBuffer = audioContext.createBuffer(1, audioContext.sampleRate * 0.1, audioContext.sampleRate);
323
+ const noiseBuffer = audioContext.createBuffer(
324
+ 1,
325
+ audioContext.sampleRate * 0.1,
326
+ audioContext.sampleRate
327
+ );
314
328
  const noiseSource = audioContext.createBufferSource();
315
-
329
+
316
330
  // Generate white noise for the initial "crack"
317
331
  const noiseData = noiseBuffer.getChannelData(0);
318
332
  for (let i = 0; i < noiseData.length; i++) {
319
333
  noiseData[i] = Math.random() * 2 - 1;
320
334
  }
321
335
  noiseSource.buffer = noiseBuffer;
322
-
336
+
323
337
  const gainNode1 = audioContext.createGain();
324
338
  const gainNode2 = audioContext.createGain();
325
339
  const noiseGain = audioContext.createGain();
326
340
  const masterGain = audioContext.createGain();
327
-
341
+
328
342
  // Connect everything
329
343
  oscillator1.connect(gainNode1);
330
344
  oscillator2.connect(gainNode2);
331
345
  noiseSource.connect(noiseGain);
332
-
346
+
333
347
  gainNode1.connect(masterGain);
334
348
  gainNode2.connect(masterGain);
335
349
  noiseGain.connect(masterGain);
336
350
  masterGain.connect(audioContext.destination);
337
-
351
+
338
352
  // Sharp metallic frequencies
339
353
  oscillator1.frequency.setValueAtTime(1800, audioContext.currentTime);
340
354
  oscillator1.frequency.exponentialRampToValueAtTime(900, audioContext.currentTime + 0.08);
341
-
355
+
342
356
  oscillator2.frequency.setValueAtTime(3600, audioContext.currentTime);
343
357
  oscillator2.frequency.exponentialRampToValueAtTime(1800, audioContext.currentTime + 0.04);
344
-
345
- oscillator1.type = 'triangle';
346
- oscillator2.type = 'sine';
347
-
358
+
359
+ oscillator1.type = "triangle";
360
+ oscillator2.type = "sine";
361
+
348
362
  // Sharp attack, quick decay
349
363
  gainNode1.gain.setValueAtTime(0, audioContext.currentTime);
350
364
  gainNode1.gain.linearRampToValueAtTime(0.4, audioContext.currentTime + 0.002);
351
365
  gainNode1.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.12);
352
-
366
+
353
367
  gainNode2.gain.setValueAtTime(0, audioContext.currentTime);
354
368
  gainNode2.gain.linearRampToValueAtTime(0.3, audioContext.currentTime + 0.001);
355
369
  gainNode2.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.06);
356
-
370
+
357
371
  // Noise burst for the initial crack
358
372
  noiseGain.gain.setValueAtTime(0, audioContext.currentTime);
359
373
  noiseGain.gain.linearRampToValueAtTime(0.2, audioContext.currentTime + 0.001);
360
374
  noiseGain.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.01);
361
-
375
+
362
376
  masterGain.gain.setValueAtTime(0.5, audioContext.currentTime);
363
-
377
+
364
378
  const startTime = audioContext.currentTime;
365
379
  const stopTime = startTime + 0.15;
366
-
380
+
367
381
  oscillator1.start(startTime);
368
382
  oscillator2.start(startTime);
369
383
  noiseSource.start(startTime);
370
-
384
+
371
385
  oscillator1.stop(stopTime);
372
386
  oscillator2.stop(stopTime);
373
387
  noiseSource.stop(startTime + 0.02);
374
-
375
388
  } catch {
376
- console.log('Audio context not available');
389
+ console.log("Audio context not available");
377
390
  }
378
391
  };
379
392
 
380
393
  const createHitmarker = (e: { clientX: number; clientY: number }) => {
381
394
  if (!containerRef.current) return;
382
-
395
+
383
396
  const rect = containerRef.current.getBoundingClientRect();
384
397
  const x = e.clientX - rect.left;
385
398
  const y = e.clientY - rect.top;
386
-
399
+
387
400
  const newHitmarker: Hitmarker = {
388
401
  id: Date.now() + Math.random(),
389
402
  x,
390
403
  y,
391
404
  };
392
-
393
- setHitmarkers(prev => [...prev, newHitmarker]);
394
-
405
+
406
+ setHitmarkers((prev) => [...prev, newHitmarker]);
407
+
395
408
  // Play sound
396
409
  playHitmarkerSound();
397
-
410
+
398
411
  // Remove hitmarker after animation
399
412
  setTimeout(() => {
400
- setHitmarkers(prev => prev.filter(h => h.id !== newHitmarker.id));
413
+ setHitmarkers((prev) => prev.filter((h) => h.id !== newHitmarker.id));
401
414
  }, 600);
402
415
  };
403
416
 
404
417
  // Inject CSS animations
405
418
  useEffect(() => {
406
- const styleId = 'loading-screen-animations';
419
+ const styleId = "loading-screen-animations";
407
420
  if (!document.getElementById(styleId)) {
408
- const style = document.createElement('style');
421
+ const style = document.createElement("style");
409
422
  style.id = styleId;
410
423
  style.textContent = `
411
424
  @keyframes fadeInOut {
@@ -516,12 +529,13 @@ const LoadingScreen: React.FC<LoadingScreenProps> = ({ message = "Waiting for so
516
529
  <div
517
530
  ref={containerRef}
518
531
  className="fw-player-root"
519
- style={{
520
- position: "relative",
521
- width: "100%",
522
- height: "100%",
523
- minHeight: "300px",
524
- background: `
532
+ style={
533
+ {
534
+ position: "relative",
535
+ width: "100%",
536
+ height: "100%",
537
+ minHeight: "300px",
538
+ background: `
525
539
  linear-gradient(135deg,
526
540
  hsl(var(--tn-bg-dark, 235 21% 11%)) 0%,
527
541
  hsl(var(--tn-bg, 233 23% 17%)) 25%,
@@ -530,22 +544,23 @@ const LoadingScreen: React.FC<LoadingScreenProps> = ({ message = "Waiting for so
530
544
  hsl(var(--tn-bg-dark, 235 21% 11%)) 100%
531
545
  )
532
546
  `,
533
- backgroundSize: "400% 400%",
534
- animation: "gradientShift 16s ease-in-out infinite",
535
- display: "flex",
536
- flexDirection: "column",
537
- alignItems: "center",
538
- justifyContent: "center",
539
- overflow: "hidden",
540
- borderRadius: "0", /* Slab - no rounded corners */
541
- userSelect: "none",
542
- WebkitUserSelect: "none",
543
- MozUserSelect: "none",
544
- msUserSelect: "none",
545
- } as React.CSSProperties}
547
+ backgroundSize: "400% 400%",
548
+ animation: "gradientShift 16s ease-in-out infinite",
549
+ display: "flex",
550
+ flexDirection: "column",
551
+ alignItems: "center",
552
+ justifyContent: "center",
553
+ overflow: "hidden",
554
+ borderRadius: "0" /* Slab - no rounded corners */,
555
+ userSelect: "none",
556
+ WebkitUserSelect: "none",
557
+ MozUserSelect: "none",
558
+ msUserSelect: "none",
559
+ } as React.CSSProperties
560
+ }
546
561
  >
547
562
  {/* Hitmarkers */}
548
- {hitmarkers.map(hitmarker => (
563
+ {hitmarkers.map((hitmarker) => (
549
564
  <div
550
565
  key={hitmarker.id}
551
566
  style={{
@@ -634,7 +649,7 @@ const LoadingScreen: React.FC<LoadingScreenProps> = ({ message = "Waiting for so
634
649
  borderRadius: "50%",
635
650
  background: [
636
651
  "#7aa2f7", // Terminal Blue
637
- "#bb9af7", // Terminal Magenta
652
+ "#bb9af7", // Terminal Magenta
638
653
  "#9ece6a", // Strings/CSS classes
639
654
  "#73daca", // Terminal Green
640
655
  "#7dcfff", // Terminal Cyan
@@ -657,7 +672,10 @@ const LoadingScreen: React.FC<LoadingScreenProps> = ({ message = "Waiting for so
657
672
  ))}
658
673
 
659
674
  {/* Center logo */}
660
- <CenterLogo containerRef={containerRef as React.RefObject<HTMLDivElement>} onHitmarker={createHitmarker} />
675
+ <CenterLogo
676
+ containerRef={containerRef as React.RefObject<HTMLDivElement>}
677
+ onHitmarker={createHitmarker}
678
+ />
661
679
 
662
680
  {/* Bouncing DVD Logo */}
663
681
  <DvdLogo parentRef={containerRef as React.RefObject<HTMLDivElement>} scale={0.08} />
@@ -707,4 +725,4 @@ const LoadingScreen: React.FC<LoadingScreenProps> = ({ message = "Waiting for so
707
725
  );
708
726
  };
709
727
 
710
- export default LoadingScreen;
728
+ export default LoadingScreen;
@@ -10,14 +10,11 @@ interface LogoOverlayProps {
10
10
  clickUrl?: string;
11
11
  }
12
12
 
13
- const POSITION_MAP: Record<
14
- NonNullable<LogoOverlayProps["position"]>,
15
- string
16
- > = {
13
+ const POSITION_MAP: Record<NonNullable<LogoOverlayProps["position"]>, string> = {
17
14
  "top-left": "left-3 top-3 sm:left-4 sm:top-4",
18
15
  "top-right": "right-3 top-3 sm:right-4 sm:top-4",
19
16
  "bottom-left": "left-3 bottom-3 sm:left-4 sm:bottom-4",
20
- "bottom-right": "right-3 bottom-3 sm:right-4 sm:bottom-4"
17
+ "bottom-right": "right-3 bottom-3 sm:right-4 sm:bottom-4",
21
18
  };
22
19
 
23
20
  const LogoOverlay: React.FC<LogoOverlayProps> = ({
@@ -26,7 +23,7 @@ const LogoOverlay: React.FC<LogoOverlayProps> = ({
26
23
  position = "bottom-right",
27
24
  width = 96,
28
25
  height = "auto",
29
- clickUrl
26
+ clickUrl,
30
27
  }) => {
31
28
  if (!show) return null;
32
29