@livepeer-frameworks/player-svelte 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 (169) hide show
  1. package/dist/DevModePanel.svelte +650 -0
  2. package/dist/DevModePanel.svelte.d.ts +31 -0
  3. package/dist/DvdLogo.svelte +213 -0
  4. package/dist/DvdLogo.svelte.d.ts +7 -0
  5. package/dist/Icons.svelte +27 -0
  6. package/dist/Icons.svelte.d.ts +25 -0
  7. package/dist/IdleScreen.svelte +752 -0
  8. package/dist/IdleScreen.svelte.d.ts +11 -0
  9. package/dist/LoadingScreen.svelte +689 -0
  10. package/dist/LoadingScreen.svelte.d.ts +7 -0
  11. package/dist/Player.svelte +482 -0
  12. package/dist/Player.svelte.d.ts +26 -0
  13. package/dist/PlayerControls.svelte +739 -0
  14. package/dist/PlayerControls.svelte.d.ts +20 -0
  15. package/dist/SeekBar.svelte +274 -0
  16. package/dist/SeekBar.svelte.d.ts +25 -0
  17. package/dist/SkipIndicator.svelte +95 -0
  18. package/dist/SkipIndicator.svelte.d.ts +14 -0
  19. package/dist/SpeedIndicator.svelte +38 -0
  20. package/dist/SpeedIndicator.svelte.d.ts +8 -0
  21. package/dist/StatsPanel.svelte +155 -0
  22. package/dist/StatsPanel.svelte.d.ts +27 -0
  23. package/dist/StreamStateOverlay.svelte +266 -0
  24. package/dist/StreamStateOverlay.svelte.d.ts +18 -0
  25. package/dist/SubtitleRenderer.svelte +234 -0
  26. package/dist/SubtitleRenderer.svelte.d.ts +41 -0
  27. package/dist/ThumbnailOverlay.svelte +96 -0
  28. package/dist/ThumbnailOverlay.svelte.d.ts +11 -0
  29. package/dist/TitleOverlay.svelte +47 -0
  30. package/dist/TitleOverlay.svelte.d.ts +9 -0
  31. package/dist/assets/logomark.svg +56 -0
  32. package/dist/components/VolumeIcons.svelte +53 -0
  33. package/dist/components/VolumeIcons.svelte.d.ts +10 -0
  34. package/dist/global.d.ts +15 -0
  35. package/dist/icons/FullscreenExitIcon.svelte +33 -0
  36. package/dist/icons/FullscreenExitIcon.svelte.d.ts +8 -0
  37. package/dist/icons/FullscreenIcon.svelte +33 -0
  38. package/dist/icons/FullscreenIcon.svelte.d.ts +8 -0
  39. package/dist/icons/PauseIcon.svelte +28 -0
  40. package/dist/icons/PauseIcon.svelte.d.ts +8 -0
  41. package/dist/icons/PictureInPictureIcon.svelte +28 -0
  42. package/dist/icons/PictureInPictureIcon.svelte.d.ts +8 -0
  43. package/dist/icons/PlayIcon.svelte +27 -0
  44. package/dist/icons/PlayIcon.svelte.d.ts +8 -0
  45. package/dist/icons/SeekToLiveIcon.svelte +30 -0
  46. package/dist/icons/SeekToLiveIcon.svelte.d.ts +8 -0
  47. package/dist/icons/SettingsIcon.svelte +40 -0
  48. package/dist/icons/SettingsIcon.svelte.d.ts +8 -0
  49. package/dist/icons/SkipBackIcon.svelte +32 -0
  50. package/dist/icons/SkipBackIcon.svelte.d.ts +8 -0
  51. package/dist/icons/SkipForwardIcon.svelte +32 -0
  52. package/dist/icons/SkipForwardIcon.svelte.d.ts +8 -0
  53. package/dist/icons/StatsIcon.svelte +29 -0
  54. package/dist/icons/StatsIcon.svelte.d.ts +8 -0
  55. package/dist/icons/VolumeOffIcon.svelte +29 -0
  56. package/dist/icons/VolumeOffIcon.svelte.d.ts +8 -0
  57. package/dist/icons/VolumeUpIcon.svelte +34 -0
  58. package/dist/icons/VolumeUpIcon.svelte.d.ts +8 -0
  59. package/dist/icons/index.d.ts +17 -0
  60. package/dist/icons/index.js +17 -0
  61. package/dist/index.d.ts +50 -0
  62. package/dist/index.js +54 -0
  63. package/dist/player.css +2 -0
  64. package/dist/stores/index.d.ts +15 -0
  65. package/dist/stores/index.js +21 -0
  66. package/dist/stores/playbackQuality.d.ts +43 -0
  67. package/dist/stores/playbackQuality.js +107 -0
  68. package/dist/stores/playerContext.d.ts +73 -0
  69. package/dist/stores/playerContext.js +166 -0
  70. package/dist/stores/playerController.d.ts +178 -0
  71. package/dist/stores/playerController.js +358 -0
  72. package/dist/stores/playerSelection.d.ts +84 -0
  73. package/dist/stores/playerSelection.js +159 -0
  74. package/dist/stores/streamState.d.ts +44 -0
  75. package/dist/stores/streamState.js +314 -0
  76. package/dist/stores/viewerEndpoints.d.ts +48 -0
  77. package/dist/stores/viewerEndpoints.js +178 -0
  78. package/dist/types.d.ts +4 -0
  79. package/dist/types.js +4 -0
  80. package/dist/ui/Badge.svelte +21 -0
  81. package/dist/ui/Badge.svelte.d.ts +32 -0
  82. package/dist/ui/Button.svelte +42 -0
  83. package/dist/ui/Button.svelte.d.ts +35 -0
  84. package/dist/ui/Slider.svelte +100 -0
  85. package/dist/ui/Slider.svelte.d.ts +17 -0
  86. package/dist/ui/badge.d.ts +6 -0
  87. package/dist/ui/badge.js +10 -0
  88. package/dist/ui/button.d.ts +8 -0
  89. package/dist/ui/button.js +21 -0
  90. package/dist/ui/context-menu/ContextMenuCheckboxItem.svelte +34 -0
  91. package/dist/ui/context-menu/ContextMenuCheckboxItem.svelte.d.ts +31 -0
  92. package/dist/ui/context-menu/ContextMenuContent.svelte +17 -0
  93. package/dist/ui/context-menu/ContextMenuContent.svelte.d.ts +7 -0
  94. package/dist/ui/context-menu/ContextMenuItem.svelte +22 -0
  95. package/dist/ui/context-menu/ContextMenuItem.svelte.d.ts +8 -0
  96. package/dist/ui/context-menu/ContextMenuLabel.svelte +22 -0
  97. package/dist/ui/context-menu/ContextMenuLabel.svelte.d.ts +8 -0
  98. package/dist/ui/context-menu/ContextMenuPortal.svelte +11 -0
  99. package/dist/ui/context-menu/ContextMenuPortal.svelte.d.ts +6 -0
  100. package/dist/ui/context-menu/ContextMenuRadioItem.svelte +21 -0
  101. package/dist/ui/context-menu/ContextMenuRadioItem.svelte.d.ts +31 -0
  102. package/dist/ui/context-menu/ContextMenuSeparator.svelte +14 -0
  103. package/dist/ui/context-menu/ContextMenuSeparator.svelte.d.ts +6 -0
  104. package/dist/ui/context-menu/ContextMenuShortcut.svelte +19 -0
  105. package/dist/ui/context-menu/ContextMenuShortcut.svelte.d.ts +7 -0
  106. package/dist/ui/context-menu/ContextMenuSubContent.svelte +20 -0
  107. package/dist/ui/context-menu/ContextMenuSubContent.svelte.d.ts +7 -0
  108. package/dist/ui/context-menu/ContextMenuSubTrigger.svelte +34 -0
  109. package/dist/ui/context-menu/ContextMenuSubTrigger.svelte.d.ts +8 -0
  110. package/dist/ui/context-menu/index.d.ts +17 -0
  111. package/dist/ui/context-menu/index.js +17 -0
  112. package/package.json +51 -0
  113. package/src/DevModePanel.svelte +650 -0
  114. package/src/DvdLogo.svelte +213 -0
  115. package/src/Icons.svelte +27 -0
  116. package/src/IdleScreen.svelte +739 -0
  117. package/src/LoadingScreen.svelte +674 -0
  118. package/src/Player.svelte +483 -0
  119. package/src/PlayerControls.svelte +752 -0
  120. package/src/SeekBar.svelte +274 -0
  121. package/src/SkipIndicator.svelte +95 -0
  122. package/src/SpeedIndicator.svelte +37 -0
  123. package/src/StatsPanel.svelte +155 -0
  124. package/src/StreamStateOverlay.svelte +266 -0
  125. package/src/SubtitleRenderer.svelte +234 -0
  126. package/src/ThumbnailOverlay.svelte +96 -0
  127. package/src/TitleOverlay.svelte +47 -0
  128. package/src/assets/logomark.svg +56 -0
  129. package/src/components/VolumeIcons.svelte +53 -0
  130. package/src/global.d.ts +15 -0
  131. package/src/icons/FullscreenExitIcon.svelte +33 -0
  132. package/src/icons/FullscreenIcon.svelte +33 -0
  133. package/src/icons/PauseIcon.svelte +28 -0
  134. package/src/icons/PictureInPictureIcon.svelte +28 -0
  135. package/src/icons/PlayIcon.svelte +27 -0
  136. package/src/icons/SeekToLiveIcon.svelte +30 -0
  137. package/src/icons/SettingsIcon.svelte +40 -0
  138. package/src/icons/SkipBackIcon.svelte +32 -0
  139. package/src/icons/SkipForwardIcon.svelte +32 -0
  140. package/src/icons/StatsIcon.svelte +29 -0
  141. package/src/icons/VolumeOffIcon.svelte +29 -0
  142. package/src/icons/VolumeUpIcon.svelte +34 -0
  143. package/src/icons/index.ts +18 -0
  144. package/src/index.ts +84 -0
  145. package/src/player.css +2 -0
  146. package/src/stores/index.ts +88 -0
  147. package/src/stores/playbackQuality.ts +137 -0
  148. package/src/stores/playerContext.ts +221 -0
  149. package/src/stores/playerController.ts +568 -0
  150. package/src/stores/playerSelection.ts +216 -0
  151. package/src/stores/streamState.ts +367 -0
  152. package/src/stores/viewerEndpoints.ts +224 -0
  153. package/src/types.ts +6 -0
  154. package/src/ui/Badge.svelte +21 -0
  155. package/src/ui/Button.svelte +42 -0
  156. package/src/ui/Slider.svelte +100 -0
  157. package/src/ui/badge.ts +20 -0
  158. package/src/ui/button.ts +35 -0
  159. package/src/ui/context-menu/ContextMenuCheckboxItem.svelte +34 -0
  160. package/src/ui/context-menu/ContextMenuContent.svelte +17 -0
  161. package/src/ui/context-menu/ContextMenuItem.svelte +22 -0
  162. package/src/ui/context-menu/ContextMenuLabel.svelte +22 -0
  163. package/src/ui/context-menu/ContextMenuPortal.svelte +11 -0
  164. package/src/ui/context-menu/ContextMenuRadioItem.svelte +21 -0
  165. package/src/ui/context-menu/ContextMenuSeparator.svelte +14 -0
  166. package/src/ui/context-menu/ContextMenuShortcut.svelte +19 -0
  167. package/src/ui/context-menu/ContextMenuSubContent.svelte +20 -0
  168. package/src/ui/context-menu/ContextMenuSubTrigger.svelte +34 -0
  169. package/src/ui/context-menu/index.ts +36 -0
@@ -0,0 +1,11 @@
1
+ import type { StreamStatus } from '@livepeer-frameworks/player-core';
2
+ interface Props {
3
+ status?: StreamStatus;
4
+ message?: string;
5
+ percentage?: number;
6
+ error?: string;
7
+ onRetry?: () => void;
8
+ }
9
+ declare const IdleScreen: import("svelte").Component<Props, {}, "">;
10
+ type IdleScreen = ReturnType<typeof IdleScreen>;
11
+ export default IdleScreen;
@@ -0,0 +1,689 @@
1
+ <!--
2
+ LoadingScreen.svelte - Animated loading screen with Tokyo Night theme
3
+ Port of src/components/LoadingScreen.tsx
4
+
5
+ Features:
6
+ - Animated colored bubbles with Tokyo Night palette
7
+ - Center logo with mouse-tracking "push away" effect
8
+ - Bouncing DVD logo component
9
+ - Hitmarker sound effects (Web Audio API synthesis)
10
+ - Floating particles with gradients
11
+ - Pulsing circle around logo
12
+ - Animated background gradient shifts
13
+ -->
14
+ <script lang="ts">
15
+ import { onMount, onDestroy } from 'svelte';
16
+ import DvdLogo from './DvdLogo.svelte';
17
+ import logomarkAsset from './assets/logomark.svg';
18
+
19
+ interface Props {
20
+ message?: string;
21
+ logoSrc?: string;
22
+ }
23
+
24
+ let { message = 'Waiting for source...', logoSrc }: Props = $props();
25
+
26
+ // Use imported asset as default if logoSrc not provided
27
+ let effectiveLogoSrc = $derived(logoSrc || logomarkAsset);
28
+
29
+ // Container ref for mouse tracking
30
+ let containerRef: HTMLDivElement | undefined = $state();
31
+
32
+ // Hitmarker state
33
+ interface Hitmarker {
34
+ id: number;
35
+ x: number;
36
+ y: number;
37
+ }
38
+ let hitmarkers = $state<Hitmarker[]>([]);
39
+
40
+ // Center logo state
41
+ let logoSize = $state(100);
42
+ let offset = $state({ x: 0, y: 0 });
43
+ let isHovered = $state(false);
44
+
45
+ // Tokyo Night inspired pastel colors for bubbles
46
+ const bubbleColors = [
47
+ 'rgba(122, 162, 247, 0.2)', // Terminal Blue
48
+ 'rgba(187, 154, 247, 0.2)', // Terminal Magenta
49
+ 'rgba(158, 206, 106, 0.2)', // Strings/CSS classes
50
+ 'rgba(115, 218, 202, 0.2)', // Terminal Green
51
+ 'rgba(125, 207, 255, 0.2)', // Terminal Cyan
52
+ 'rgba(247, 118, 142, 0.2)', // Keywords/Terminal Red
53
+ 'rgba(224, 175, 104, 0.2)', // Terminal Yellow
54
+ 'rgba(42, 195, 222, 0.2)', // Language functions
55
+ ];
56
+
57
+ // Particle colors
58
+ const particleColors = [
59
+ '#7aa2f7', // Terminal Blue
60
+ '#bb9af7', // Terminal Magenta
61
+ '#9ece6a', // Strings/CSS classes
62
+ '#73daca', // Terminal Green
63
+ '#7dcfff', // Terminal Cyan
64
+ '#f7768e', // Keywords/Terminal Red
65
+ '#e0af68', // Terminal Yellow
66
+ '#2ac3de', // Language functions
67
+ ];
68
+
69
+ // Generate random particles
70
+ const particles = Array.from({ length: 12 }, (_, i) => ({
71
+ left: Math.random() * 100,
72
+ size: Math.random() * 4 + 2,
73
+ color: particleColors[i % 8],
74
+ duration: 8 + Math.random() * 4,
75
+ delay: Math.random() * 8,
76
+ }));
77
+
78
+ // Animated bubble component state
79
+ interface BubbleState {
80
+ position: { top: number; left: number };
81
+ size: number;
82
+ opacity: number;
83
+ color: string;
84
+ timeoutId: ReturnType<typeof setTimeout> | null;
85
+ }
86
+
87
+ let bubbles = $state<BubbleState[]>(
88
+ Array.from({ length: 8 }, (_, i) => ({
89
+ position: { top: Math.random() * 80 + 10, left: Math.random() * 80 + 10 },
90
+ size: Math.random() * 60 + 30,
91
+ opacity: 0,
92
+ color: bubbleColors[i % bubbleColors.length],
93
+ timeoutId: null,
94
+ }))
95
+ );
96
+
97
+ // Bubble animation cycle
98
+ function animateBubble(index: number) {
99
+ // Fade in
100
+ bubbles[index].opacity = 0.15;
101
+
102
+ const visibleDuration = 4000 + Math.random() * 3000; // 4-7 seconds visible
103
+
104
+ const timeout1 = setTimeout(() => {
105
+ // Fade out
106
+ bubbles[index].opacity = 0;
107
+
108
+ const timeout2 = setTimeout(() => {
109
+ // Move to new random position while invisible
110
+ bubbles[index].position = {
111
+ top: Math.random() * 80 + 10,
112
+ left: Math.random() * 80 + 10,
113
+ };
114
+ bubbles[index].size = Math.random() * 60 + 30;
115
+
116
+ const timeout3 = setTimeout(() => {
117
+ animateBubble(index);
118
+ }, 200);
119
+ bubbles[index].timeoutId = timeout3;
120
+ }, 1500); // Wait for fade out
121
+ bubbles[index].timeoutId = timeout2;
122
+ }, visibleDuration);
123
+ bubbles[index].timeoutId = timeout1;
124
+ }
125
+
126
+ // Hitmarker sound synthesis
127
+ function createSyntheticHitmarkerSound() {
128
+ try {
129
+ const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
130
+
131
+ const oscillator1 = audioContext.createOscillator();
132
+ const oscillator2 = audioContext.createOscillator();
133
+ const noiseBuffer = audioContext.createBuffer(1, audioContext.sampleRate * 0.1, audioContext.sampleRate);
134
+ const noiseSource = audioContext.createBufferSource();
135
+
136
+ // Generate white noise for the initial "crack"
137
+ const noiseData = noiseBuffer.getChannelData(0);
138
+ for (let i = 0; i < noiseData.length; i++) {
139
+ noiseData[i] = Math.random() * 2 - 1;
140
+ }
141
+ noiseSource.buffer = noiseBuffer;
142
+
143
+ const gainNode1 = audioContext.createGain();
144
+ const gainNode2 = audioContext.createGain();
145
+ const noiseGain = audioContext.createGain();
146
+ const masterGain = audioContext.createGain();
147
+
148
+ oscillator1.connect(gainNode1);
149
+ oscillator2.connect(gainNode2);
150
+ noiseSource.connect(noiseGain);
151
+
152
+ gainNode1.connect(masterGain);
153
+ gainNode2.connect(masterGain);
154
+ noiseGain.connect(masterGain);
155
+ masterGain.connect(audioContext.destination);
156
+
157
+ // Sharp metallic frequencies
158
+ oscillator1.frequency.setValueAtTime(1800, audioContext.currentTime);
159
+ oscillator1.frequency.exponentialRampToValueAtTime(900, audioContext.currentTime + 0.08);
160
+
161
+ oscillator2.frequency.setValueAtTime(3600, audioContext.currentTime);
162
+ oscillator2.frequency.exponentialRampToValueAtTime(1800, audioContext.currentTime + 0.04);
163
+
164
+ oscillator1.type = 'triangle';
165
+ oscillator2.type = 'sine';
166
+
167
+ // Sharp attack, quick decay
168
+ gainNode1.gain.setValueAtTime(0, audioContext.currentTime);
169
+ gainNode1.gain.linearRampToValueAtTime(0.4, audioContext.currentTime + 0.002);
170
+ gainNode1.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.12);
171
+
172
+ gainNode2.gain.setValueAtTime(0, audioContext.currentTime);
173
+ gainNode2.gain.linearRampToValueAtTime(0.3, audioContext.currentTime + 0.001);
174
+ gainNode2.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.06);
175
+
176
+ // Noise burst
177
+ noiseGain.gain.setValueAtTime(0, audioContext.currentTime);
178
+ noiseGain.gain.linearRampToValueAtTime(0.2, audioContext.currentTime + 0.001);
179
+ noiseGain.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.01);
180
+
181
+ masterGain.gain.setValueAtTime(0.5, audioContext.currentTime);
182
+
183
+ const startTime = audioContext.currentTime;
184
+ const stopTime = startTime + 0.15;
185
+
186
+ oscillator1.start(startTime);
187
+ oscillator2.start(startTime);
188
+ noiseSource.start(startTime);
189
+
190
+ oscillator1.stop(stopTime);
191
+ oscillator2.stop(stopTime);
192
+ noiseSource.stop(startTime + 0.02);
193
+ } catch {
194
+ // Audio context not available
195
+ }
196
+ }
197
+
198
+ function playHitmarkerSound() {
199
+ try {
200
+ // Embedded hitmarker sound as base64 data URL (matches React implementation)
201
+ const hitmarkerDataUrl = 'data:audio/mpeg;base64,SUQzBAAAAAAANFRDT04AAAAHAAADT3RoZXIAVFNTRQAAAA8AAANMYXZmNTcuODMuMTAwAAAAAAAAAAAA' +
202
+ 'AAD/+1QAAAAAAAAAAAAAAAAAAAAA' +
203
+ 'AAAAAAAAAAAAAAAAAAAAAABJbmZvAAAADwAAAAYAAAnAADs7Ozs7Ozs7Ozs7Ozs7OztiYmJiYmJiYmJi' +
204
+ 'YmJiYmJiYomJiYmJiYmJiYmJiYmJiYmxsbGxsbGxsbGxsbGxsbGxsdjY2NjY2NjY2NjY2NjY2NjY////' +
205
+ '/////////////////wAAAABMYXZjNTcuMTAAAAAAAAAAAAAAAAAkAkAAAAAAAAAJwOuMZun/+5RkAA8S' +
206
+ '/F23AGAaAi0AF0AAAAAInXsEAIRXyQ8D4OQgjEhE3cO7ujuHF0XCOu4G7xKbi3Funu7u7p9dw7unu7u7' +
207
+ 'p7u7u6fXcW7om7u7uiU3dxdT67u7p7uHdxelN3cW6fXcW7oXXd3eJTd3d0+u4t3iXdw4up70W4uiPruL' +
208
+ 'DzMw8Pz79Y99JfkyfPv5/h9uTJoy79Y99Y97q3vyZPJk0ZfrL6x73Vn+J35dKKS/STQyQ8CAiCPNuRAO' +
209
+ 'OqquAx+fzJeBKDAsgAMBuWcBsHKhjJTcCwIALyAvABbI0ZIcCmP8jHJe8gZAdVRp2TpnU/kUXV4iQuBA' +
210
+ 'AkAQgisLPvwQ2Jz7wIkIpQ8QOl/KFy75w+2HpTFnRqXLQo0fzlSYRe5Ce9yZMEzRM4xesu95Mo8QQsoM' +
211
+ 'H4gLg+fJqkmY3GZJE2kwGfMECJiAdIttoEa2yotfC7jsS2mjKgbzAfEMeiwZpGSUFCQwPKQiWXh0TnkN' +
212
+ 'or5SmrKvwHlX2zFxKxPCzRL/+5RkIwADvUxLawwb0GdF6Y1hJlgNNJk+DSRwyQwI6AD2JCiBmhaff0dz' +
213
+ 'CEBjgFABAcDNFc3YAEV4hQn0L/QvQnevom+n13eIjoTvABLrHg/L9RzdWXYonHbbbE2K0pX+gkL2g56R' +
214
+ 'iwrbuWwhoABzQoMKOAIGAfE4UKk6BhSIJpECBq0CEYmZKYIiAJt72H24dNou7y/Ee7a/3v+MgySemSTY' +
215
+ 'mnBAFwIAAGfCJ8/D9YfkwQEBcP38uA1d/EB1T5dZKEsgnuhwZirY5fIMRMdRn7U4OcN2m5NWeYdcPBwX' +
216
+ 'DBOsJF1DBYks62pAURqz1hGoGHH/QIoRC80tYAJ8g4f3MPD51sywAbhAn/X9P/75tvZww3gZ3pYPDx/+' +
217
+ 'ACO/7//ffHj/D/AAfATC4DYGFA3MRABo0lqWjBOl2yAda1C1BdhduXgm8FGnAQB/lDiEi6j9qw9EHigI' +
218
+ 'IOLB6F1eIPd+T6Agc4//lMo6+k3tdttJY2gArU7cN07m2FLSm4gCjyz/+5RECwACwSRZawkdLFGi2mVh' +
219
+ '5h4LfFdPVPGACViTavaeMAAV0UkkEsDhxxJwqF04on002mZah8w9+5ItfSAoyZa1dchnPpLmAEKrVMRA' +
220
+ '//sD8w0WsB4xiw4JqaZMB45TdpIuXXUPf8Bpa35p/jQIAOAuZkmUeJoM5W6L2gqqO6rTuHjUTDnhy4Qi' +
221
+ 'K348vtFysOizShoHbBpsPRYcSINCbiN4XOLPPAgq3dW2Ga7SlyiKXBV7W1RQl5BiiVGkwayJfEnPxgXk' +
222
+ 'QeZxxzyhTuLO2XFUDDstoc6CkM1J8QZAjUN3bM8580cRygNfmPAELGjIH0Z/0A+8csyH/4eHvgAf8APg' +
223
+ 'ABmZ98AARAADP////Dw8PHEmIpgGttpJQJsmZjq5nPQ8j5VqWW1evqdjP182PA6tHJZgkC5iSbEQkyJS' +
224
+ 'z/BvP3eucLKN0+Wiza4feKKFBqiAEBAMXyYni5NZc16CDl/QY9j6BAcWSmQYcIcoMHYoQNBiIBgIBUAz' +
225
+ 'QUMSnjj/+5RkCwADsFLffjEAAjrJe63JHACO6WtlnPMACKaCK1uMMADU5dI6JhW2cam98UlRmY4ihyKF' +
226
+ 'rNsgpZd5PYgBALnYofKEt82De0GbW1DLibvFDK+bSeOm8qKdqUFZ7uiK8XMPHyqm3pTxUvcunUfxXEo9' +
227
+ 'RNe5b/8vfCD3kzDN7vTtHyaIcntVDAYBAUBAAAAQBI2vguYNsHWm5AR3mZtZib8WAHFvz2Kf9//iYvlR' +
228
+ 'B/+n///////////+UH7XoIDMoJAEAMtj8JshJPRwklVqNSpYnalfE+VzNCAISCoxVHEpIo/WrTiMvP7V' +
229
+ 'TujOPnOglLbMLN/pq/d2Y4lRJIkSnPlUSJEjSKJqM41d88zWtMzP+fCOORmc9NeM+f1nnO//efM52/fG' +
230
+ '/ef385+5u+u1bRJkwU8FAkEItZpkRYeQYcAgZTEYlaZa2yROLeC0qdX73rZJJ/d2f6v6Or0u/+5FBYcn' +
231
+ 'g0MlCiQTR9GUU5LScmSuSlH00IWqXA6jlw4BEcD/+5REEAAi3RtU+eYbGF1E+lk9g0YJzLUgh7BlQVGT' +
232
+ 'ZJD0jKhhTNVilqrMzFRK+x/szcMKBWKep4NP1A0DR6RESkTp5Z1Q9Y8REgqMg1DpUBPleeqlRQcerBpM' +
233
+ 'jiURHVD4XwAALhAgbxxlxYD5OFkG8oQRPB2EpsxSCNVlgcYUqoAyiVJmaARlkwplICfPoUy/zWEzM2pc' +
234
+ 'NYzAQNJDSniEYecSEqxFEzQqEvUFGnvzwUfcRlpZ9T2LCR5QdDQDDhKICAjpJCagpRo9UQRPClZZlg6E' +
235
+ 'p9DMTkTl+okuhRIVIzAQEf9L+Mx/DUjqmqN6kX7M36lS4zgLyJV3iV6j3xF8kJduJawVw1nndAlBaLLg' +
236
+ 'JupwsTcLkxmJgFLgSzoCmHjSNGSqkGPCpnNqTXIwolf6qlVWN+q/su37HzgrES1pWGg3KnWh0FXCVniJ' +
237
+ '9K5b4iCrpLEuIcFTqwkVLFiqgaDqCCSMVWqxBAVCFOLVrVahm2ahUThUKJnmFCw15hD0Qhb/+5REEAhC' +
238
+ 'YSRCSQEb4FOGaBUMI6JIRYC0QIB2SQsgGpgwDghgIlS6FU8VBXDoiBp5Y9gtkVnhEhYBdJFQ7kQ3w1yp' +
239
+ '0NB2CoNPEttZ1/aeDUAAA26FEghWgEKNVAVWkFAQEmMK2Uwk/qI0hqUb/4epVIZH1ai6szf6kzH1f2ar' +
240
+ 'xYGS9FcOsN5UlJLQt///+oo0FRDTUQ0FBQr9f5LxXP+mEUfk0AIrf/5GRmQ0//mX//ZbLP5b5GrWSz+W' +
241
+ 'SkZMrWyyyy2GRqyggVRyMv////////st//sn/yyVDI1l8mVgoYGDCOqiqIQBxmvxWCggTpZZZD//aWfy' +
242
+ 'yWf/y/7KGDA0ssBggTof9k/+WS/8slQyMp/5Nfln8WAqGcUbULCrKxT9ISF+kKsxQWpMQU1FMy4xMDCq' +
243
+ 'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq' +
244
+ 'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo=';
245
+
246
+ const audio = new Audio(hitmarkerDataUrl);
247
+ audio.volume = 0.3;
248
+ audio.play().catch(() => {
249
+ // Fallback to synthetic sound if data URL fails
250
+ createSyntheticHitmarkerSound();
251
+ });
252
+ } catch {
253
+ // Fallback to synthetic sound
254
+ createSyntheticHitmarkerSound();
255
+ }
256
+ }
257
+
258
+ function createHitmarker(e: { clientX: number; clientY: number }) {
259
+ if (!containerRef) return;
260
+
261
+ const rect = containerRef.getBoundingClientRect();
262
+ const x = e.clientX - rect.left;
263
+ const y = e.clientY - rect.top;
264
+
265
+ const newHitmarker: Hitmarker = {
266
+ id: Date.now() + Math.random(),
267
+ x,
268
+ y,
269
+ };
270
+
271
+ hitmarkers = [...hitmarkers, newHitmarker];
272
+ playHitmarkerSound();
273
+
274
+ // Remove hitmarker after animation
275
+ setTimeout(() => {
276
+ hitmarkers = hitmarkers.filter(h => h.id !== newHitmarker.id);
277
+ }, 600);
278
+ }
279
+
280
+ // Mouse tracking for logo push-away effect
281
+ function handleMouseMove(e: MouseEvent) {
282
+ if (!containerRef) return;
283
+
284
+ const rect = containerRef.getBoundingClientRect();
285
+ const centerX = rect.left + rect.width / 2;
286
+ const centerY = rect.top + rect.height / 2;
287
+
288
+ const mouseX = e.clientX;
289
+ const mouseY = e.clientY;
290
+
291
+ const deltaX = mouseX - centerX;
292
+ const deltaY = mouseY - centerY;
293
+ const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
294
+
295
+ const maxDistance = logoSize * 1.5;
296
+ if (distance < maxDistance && distance > 0) {
297
+ const pushStrength = (maxDistance - distance) / maxDistance;
298
+ const pushDistance = 50 * pushStrength;
299
+
300
+ offset = {
301
+ x: -(deltaX / distance) * pushDistance,
302
+ y: -(deltaY / distance) * pushDistance,
303
+ };
304
+ isHovered = true;
305
+ } else {
306
+ offset = { x: 0, y: 0 };
307
+ isHovered = false;
308
+ }
309
+ }
310
+
311
+ function handleMouseLeave() {
312
+ offset = { x: 0, y: 0 };
313
+ isHovered = false;
314
+ }
315
+
316
+ function handleLogoClick(e: MouseEvent) {
317
+ e.stopPropagation();
318
+ createHitmarker({ clientX: e.clientX, clientY: e.clientY });
319
+ }
320
+
321
+ // Update logo size on container resize
322
+ $effect(() => {
323
+ if (!containerRef) return;
324
+
325
+ const updateLogoSize = () => {
326
+ if (containerRef) {
327
+ const minDimension = Math.min(containerRef.clientWidth, containerRef.clientHeight);
328
+ logoSize = minDimension * 0.2;
329
+ }
330
+ };
331
+
332
+ updateLogoSize();
333
+
334
+ const resizeObserver = new ResizeObserver(updateLogoSize);
335
+ resizeObserver.observe(containerRef);
336
+
337
+ return () => resizeObserver.disconnect();
338
+ });
339
+
340
+ // Start bubble animations on mount
341
+ onMount(() => {
342
+ bubbles.forEach((_, index) => {
343
+ setTimeout(() => animateBubble(index), index * 500);
344
+ });
345
+ });
346
+
347
+ // Cleanup on destroy
348
+ onDestroy(() => {
349
+ bubbles.forEach(bubble => {
350
+ if (bubble.timeoutId) clearTimeout(bubble.timeoutId);
351
+ });
352
+ });
353
+ </script>
354
+
355
+ <style>
356
+ @keyframes fadeInOut {
357
+ 0%, 100% { opacity: 0.6; }
358
+ 50% { opacity: 0.9; }
359
+ }
360
+
361
+ @keyframes logoPulse {
362
+ 0%, 100% {
363
+ opacity: 0.15;
364
+ transform: scale(1);
365
+ }
366
+ 50% {
367
+ opacity: 0.25;
368
+ transform: scale(1.05);
369
+ }
370
+ }
371
+
372
+ @keyframes floatUp {
373
+ 0% {
374
+ transform: translateY(100vh) rotate(0deg);
375
+ opacity: 0;
376
+ }
377
+ 10% { opacity: 0.6; }
378
+ 90% { opacity: 0.6; }
379
+ 100% {
380
+ transform: translateY(-100px) rotate(360deg);
381
+ opacity: 0;
382
+ }
383
+ }
384
+
385
+ @keyframes gradientShift {
386
+ 0%, 100% { background-position: 0% 50%; }
387
+ 50% { background-position: 100% 50%; }
388
+ }
389
+
390
+ @keyframes hitmarkerFade {
391
+ 0% {
392
+ opacity: 1;
393
+ transform: scale(0.5);
394
+ }
395
+ 20% {
396
+ opacity: 1;
397
+ transform: scale(1.2);
398
+ }
399
+ 100% {
400
+ opacity: 0;
401
+ transform: scale(1);
402
+ }
403
+ }
404
+
405
+ @keyframes hitmarkerFade45 {
406
+ 0% {
407
+ opacity: 1;
408
+ transform: translate(-50%, -50%) rotate(45deg) scale(0.5);
409
+ }
410
+ 20% {
411
+ opacity: 1;
412
+ transform: translate(-50%, -50%) rotate(45deg) scale(1.2);
413
+ }
414
+ 100% {
415
+ opacity: 0;
416
+ transform: translate(-50%, -50%) rotate(45deg) scale(1);
417
+ }
418
+ }
419
+
420
+ @keyframes hitmarkerFadeNeg45 {
421
+ 0% {
422
+ opacity: 1;
423
+ transform: translate(-50%, -50%) rotate(-45deg) scale(0.5);
424
+ }
425
+ 20% {
426
+ opacity: 1;
427
+ transform: translate(-50%, -50%) rotate(-45deg) scale(1.2);
428
+ }
429
+ 100% {
430
+ opacity: 0;
431
+ transform: translate(-50%, -50%) rotate(-45deg) scale(1);
432
+ }
433
+ }
434
+
435
+ .fw-player-root .loading-container {
436
+ position: relative;
437
+ width: 100%;
438
+ height: 100%;
439
+ min-height: 300px;
440
+ background: linear-gradient(135deg,
441
+ hsl(var(--tn-bg-dark, 235 21% 11%)) 0%,
442
+ hsl(var(--tn-bg, 233 23% 17%)) 25%,
443
+ hsl(var(--tn-bg-dark, 235 21% 11%)) 50%,
444
+ hsl(var(--tn-bg, 233 23% 17%)) 75%,
445
+ hsl(var(--tn-bg-dark, 235 21% 11%)) 100%
446
+ );
447
+ background-size: 400% 400%;
448
+ animation: gradientShift 16s ease-in-out infinite;
449
+ display: flex;
450
+ flex-direction: column;
451
+ align-items: center;
452
+ justify-content: center;
453
+ overflow: hidden;
454
+ border-radius: 0;
455
+ -moz-user-select: none;
456
+ user-select: none;
457
+ -webkit-user-select: none;
458
+ }
459
+
460
+ .fw-player-root .bubble {
461
+ position: absolute;
462
+ border-radius: 50%;
463
+ transition: opacity 1s ease-in-out;
464
+ pointer-events: none;
465
+ -webkit-user-select: none;
466
+ -moz-user-select: none;
467
+ user-select: none;
468
+ }
469
+
470
+ .fw-player-root .particle {
471
+ position: absolute;
472
+ border-radius: 50%;
473
+ opacity: 0;
474
+ animation: floatUp linear infinite;
475
+ pointer-events: none;
476
+ -webkit-user-select: none;
477
+ -moz-user-select: none;
478
+ user-select: none;
479
+ }
480
+
481
+ .fw-player-root .center-logo {
482
+ position: absolute;
483
+ top: 50%;
484
+ left: 50%;
485
+ display: flex;
486
+ align-items: center;
487
+ justify-content: center;
488
+ z-index: 10;
489
+ transition: transform 0.3s ease-out;
490
+ -webkit-user-select: none;
491
+ -moz-user-select: none;
492
+ user-select: none;
493
+ }
494
+
495
+ .fw-player-root .logo-pulse {
496
+ position: absolute;
497
+ border-radius: 50%;
498
+ background: rgba(122, 162, 247, 0.15);
499
+ animation: logoPulse 3s ease-in-out infinite;
500
+ -webkit-user-select: none;
501
+ -moz-user-select: none;
502
+ user-select: none;
503
+ pointer-events: none;
504
+ transition: transform 0.3s ease-out;
505
+ }
506
+
507
+ .fw-player-root .logo-pulse.hovered {
508
+ animation: logoPulse 1s ease-in-out infinite;
509
+ transform: scale(1.2);
510
+ }
511
+
512
+ .fw-player-root .logo-image {
513
+ position: relative;
514
+ z-index: 1;
515
+ filter: drop-shadow(0 4px 8px rgba(36, 40, 59, 0.3));
516
+ transition: all 0.3s ease-out;
517
+ -webkit-user-select: none;
518
+ -moz-user-select: none;
519
+ user-select: none;
520
+ -webkit-user-drag: none;
521
+ -webkit-touch-callout: none;
522
+ }
523
+
524
+ .fw-player-root .logo-image.hovered {
525
+ filter: drop-shadow(0 6px 12px rgba(36, 40, 59, 0.4)) brightness(1.1);
526
+ transform: scale(1.1);
527
+ cursor: pointer;
528
+ }
529
+
530
+ .fw-player-root .message {
531
+ position: absolute;
532
+ bottom: 20%;
533
+ left: 50%;
534
+ transform: translateX(-50%);
535
+ color: #a9b1d6;
536
+ font-size: 16px;
537
+ font-weight: 500;
538
+ text-align: center;
539
+ animation: fadeInOut 2s ease-in-out infinite;
540
+ text-shadow: 0 2px 4px rgba(36, 40, 59, 0.5);
541
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
542
+ -webkit-user-select: none;
543
+ -moz-user-select: none;
544
+ user-select: none;
545
+ pointer-events: none;
546
+ }
547
+
548
+ .fw-player-root .overlay-texture {
549
+ position: absolute;
550
+ top: 0;
551
+ left: 0;
552
+ right: 0;
553
+ bottom: 0;
554
+ background:
555
+ radial-gradient(circle at 20% 80%, rgba(122, 162, 247, 0.03) 0%, transparent 50%),
556
+ radial-gradient(circle at 80% 20%, rgba(187, 154, 247, 0.03) 0%, transparent 50%),
557
+ radial-gradient(circle at 40% 40%, rgba(158, 206, 106, 0.02) 0%, transparent 50%);
558
+ pointer-events: none;
559
+ -webkit-user-select: none;
560
+ -moz-user-select: none;
561
+ user-select: none;
562
+ }
563
+
564
+ .fw-player-root .hitmarker {
565
+ position: absolute;
566
+ transform: translate(-50%, -50%);
567
+ pointer-events: none;
568
+ z-index: 100;
569
+ width: 40px;
570
+ height: 40px;
571
+ }
572
+
573
+ .fw-player-root .hitmarker-line {
574
+ position: absolute;
575
+ width: 12px;
576
+ height: 3px;
577
+ background-color: #ffffff;
578
+ box-shadow: 0 0 8px rgba(255, 255, 255, 0.8);
579
+ border-radius: 1px;
580
+ }
581
+
582
+ .fw-player-root .hitmarker-line.tl {
583
+ top: 25%;
584
+ left: 25%;
585
+ animation: hitmarkerFade45 0.6s ease-out forwards;
586
+ }
587
+
588
+ .fw-player-root .hitmarker-line.tr {
589
+ top: 25%;
590
+ left: 75%;
591
+ animation: hitmarkerFadeNeg45 0.6s ease-out forwards;
592
+ }
593
+
594
+ .fw-player-root .hitmarker-line.bl {
595
+ top: 75%;
596
+ left: 25%;
597
+ animation: hitmarkerFadeNeg45 0.6s ease-out forwards;
598
+ }
599
+
600
+ .fw-player-root .hitmarker-line.br {
601
+ top: 75%;
602
+ left: 75%;
603
+ animation: hitmarkerFade45 0.6s ease-out forwards;
604
+ }
605
+ </style>
606
+
607
+ <div
608
+ bind:this={containerRef}
609
+ class="loading-container fw-player-root"
610
+ role="status"
611
+ aria-label="Loading"
612
+ onmousemove={handleMouseMove}
613
+ onmouseleave={handleMouseLeave}
614
+ >
615
+ <!-- Hitmarkers -->
616
+ {#each hitmarkers as hitmarker (hitmarker.id)}
617
+ <div class="hitmarker" style="left: {hitmarker.x}px; top: {hitmarker.y}px;">
618
+ <div class="hitmarker-line tl"></div>
619
+ <div class="hitmarker-line tr"></div>
620
+ <div class="hitmarker-line bl"></div>
621
+ <div class="hitmarker-line br"></div>
622
+ </div>
623
+ {/each}
624
+
625
+ <!-- Floating particles -->
626
+ {#each particles as particle, i}
627
+ <div
628
+ class="particle"
629
+ style="
630
+ left: {particle.left}%;
631
+ width: {particle.size}px;
632
+ height: {particle.size}px;
633
+ background: {particle.color};
634
+ animation-duration: {particle.duration}s;
635
+ animation-delay: {particle.delay}s;
636
+ "
637
+ />
638
+ {/each}
639
+
640
+ <!-- Animated bubbles -->
641
+ {#each bubbles as bubble, i}
642
+ <div
643
+ class="bubble"
644
+ style="
645
+ top: {bubble.position.top}%;
646
+ left: {bubble.position.left}%;
647
+ width: {bubble.size}px;
648
+ height: {bubble.size}px;
649
+ background: {bubble.color};
650
+ opacity: {bubble.opacity};
651
+ "
652
+ />
653
+ {/each}
654
+
655
+ <!-- Center logo with push-away effect -->
656
+ <div
657
+ class="center-logo"
658
+ style="transform: translate(-50%, -50%) translate({offset.x}px, {offset.y}px);"
659
+ >
660
+ <!-- Pulsing circle background -->
661
+ <div
662
+ class="logo-pulse"
663
+ class:hovered={isHovered}
664
+ style="width: {logoSize * 1.4}px; height: {logoSize * 1.4}px;"
665
+ />
666
+
667
+ <!-- Logo image -->
668
+ <img
669
+ src={effectiveLogoSrc}
670
+ alt="Logo"
671
+ class="logo-image"
672
+ class:hovered={isHovered}
673
+ style="width: {logoSize}px; height: {logoSize}px;"
674
+ onclick={handleLogoClick}
675
+ draggable="false"
676
+ />
677
+ </div>
678
+
679
+ <!-- Bouncing DVD Logo -->
680
+ <DvdLogo parentRef={containerRef} scale={0.08} />
681
+
682
+ <!-- Message -->
683
+ <div class="message">
684
+ {message}
685
+ </div>
686
+
687
+ <!-- Subtle overlay texture -->
688
+ <div class="overlay-texture" />
689
+ </div>
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ message?: string;
3
+ logoSrc?: string;
4
+ }
5
+ declare const LoadingScreen: import("svelte").Component<Props, {}, "">;
6
+ type LoadingScreen = ReturnType<typeof LoadingScreen>;
7
+ export default LoadingScreen;