@livepeer-frameworks/player-svelte 0.1.1 → 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.
- package/dist/DevModePanel.svelte +266 -127
- package/dist/DevModePanel.svelte.d.ts +1 -1
- package/dist/DvdLogo.svelte +17 -21
- package/dist/Icons.svelte +5 -3
- package/dist/Icons.svelte.d.ts +6 -19
- package/dist/IdleScreen.svelte +277 -186
- package/dist/IdleScreen.svelte.d.ts +1 -1
- package/dist/LoadingScreen.svelte +190 -162
- package/dist/Player.svelte +244 -111
- package/dist/Player.svelte.d.ts +1 -1
- package/dist/PlayerControls.svelte +263 -168
- package/dist/PlayerControls.svelte.d.ts +1 -1
- package/dist/SeekBar.svelte +61 -35
- package/dist/SkipIndicator.svelte +4 -4
- package/dist/SkipIndicator.svelte.d.ts +1 -1
- package/dist/SpeedIndicator.svelte +1 -1
- package/dist/StatsPanel.svelte +76 -57
- package/dist/StatsPanel.svelte.d.ts +1 -1
- package/dist/StreamStateOverlay.svelte +143 -107
- package/dist/StreamStateOverlay.svelte.d.ts +1 -1
- package/dist/SubtitleRenderer.svelte +46 -43
- package/dist/ThumbnailOverlay.svelte +22 -19
- package/dist/TitleOverlay.svelte +6 -11
- package/dist/components/VolumeIcons.svelte +12 -6
- package/dist/global.d.ts +3 -3
- package/dist/icons/FullscreenExitIcon.svelte +1 -5
- package/dist/icons/FullscreenIcon.svelte +1 -5
- package/dist/icons/PauseIcon.svelte +1 -5
- package/dist/icons/PictureInPictureIcon.svelte +12 -6
- package/dist/icons/PlayIcon.svelte +1 -5
- package/dist/icons/SeekToLiveIcon.svelte +1 -5
- package/dist/icons/SettingsIcon.svelte +1 -5
- package/dist/icons/SkipBackIcon.svelte +1 -5
- package/dist/icons/SkipForwardIcon.svelte +1 -5
- package/dist/icons/StatsIcon.svelte +1 -5
- package/dist/icons/VolumeOffIcon.svelte +1 -5
- package/dist/icons/VolumeUpIcon.svelte +1 -5
- package/dist/icons/index.d.ts +12 -12
- package/dist/icons/index.js +12 -12
- package/dist/index.d.ts +24 -24
- package/dist/index.js +21 -21
- package/dist/stores/index.d.ts +6 -6
- package/dist/stores/index.js +6 -6
- package/dist/stores/playbackQuality.d.ts +2 -2
- package/dist/stores/playbackQuality.js +7 -7
- package/dist/stores/playerContext.d.ts +2 -2
- package/dist/stores/playerContext.js +17 -17
- package/dist/stores/playerController.d.ts +13 -4
- package/dist/stores/playerController.js +80 -56
- package/dist/stores/playerSelection.d.ts +2 -2
- package/dist/stores/playerSelection.js +7 -7
- package/dist/stores/streamState.d.ts +2 -2
- package/dist/stores/streamState.js +56 -56
- package/dist/stores/viewerEndpoints.d.ts +3 -3
- package/dist/stores/viewerEndpoints.js +21 -21
- package/dist/types.d.ts +1 -1
- package/dist/ui/Badge.svelte +9 -10
- package/dist/ui/Badge.svelte.d.ts +8 -29
- package/dist/ui/Button.svelte +16 -16
- package/dist/ui/Button.svelte.d.ts +8 -29
- package/dist/ui/Slider.svelte +21 -55
- package/dist/ui/badge.js +1 -1
- package/dist/ui/button.js +2 -2
- package/dist/ui/context-menu/ContextMenuCheckboxItem.svelte +5 -7
- package/dist/ui/context-menu/ContextMenuCheckboxItem.svelte.d.ts +6 -27
- package/dist/ui/context-menu/ContextMenuContent.svelte +2 -9
- package/dist/ui/context-menu/ContextMenuItem.svelte +1 -5
- package/dist/ui/context-menu/ContextMenuLabel.svelte +1 -5
- package/dist/ui/context-menu/ContextMenuRadioItem.svelte +5 -7
- package/dist/ui/context-menu/ContextMenuRadioItem.svelte.d.ts +6 -27
- package/dist/ui/context-menu/ContextMenuSeparator.svelte +2 -8
- package/dist/ui/context-menu/ContextMenuShortcut.svelte +2 -12
- package/dist/ui/context-menu/ContextMenuSubContent.svelte +1 -5
- package/package.json +15 -7
- package/src/DevModePanel.svelte +1 -0
- package/src/Icons.svelte +5 -3
- package/src/IdleScreen.svelte +21 -14
- package/src/LoadingScreen.svelte +20 -13
- package/src/Player.svelte +48 -2
- package/src/PlayerControls.svelte +36 -17
- package/src/SeekBar.svelte +33 -0
- package/src/StreamStateOverlay.svelte +2 -2
- package/src/stores/playerController.ts +39 -1
- package/src/stores/viewerEndpoints.ts +1 -1
- package/src/ui/Badge.svelte +7 -4
- package/src/ui/Button.svelte +13 -13
- package/src/ui/context-menu/ContextMenuCheckboxItem.svelte +4 -2
- package/src/ui/context-menu/ContextMenuRadioItem.svelte +4 -2
package/dist/IdleScreen.svelte
CHANGED
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
- Status overlay at bottom
|
|
14
14
|
-->
|
|
15
15
|
<script lang="ts">
|
|
16
|
-
import { onMount, onDestroy } from
|
|
17
|
-
import type { StreamStatus } from
|
|
18
|
-
import DvdLogo from
|
|
19
|
-
import logomarkAsset from
|
|
16
|
+
import { onMount, onDestroy } from "svelte";
|
|
17
|
+
import type { StreamStatus } from "@livepeer-frameworks/player-core";
|
|
18
|
+
import DvdLogo from "./DvdLogo.svelte";
|
|
19
|
+
import logomarkAsset from "./assets/logomark.svg";
|
|
20
20
|
|
|
21
21
|
interface Props {
|
|
22
22
|
status?: StreamStatus;
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
let {
|
|
30
|
-
status =
|
|
31
|
-
message =
|
|
30
|
+
status = "OFFLINE",
|
|
31
|
+
message = "Waiting for stream...",
|
|
32
32
|
percentage = undefined,
|
|
33
33
|
error = undefined,
|
|
34
34
|
onRetry = undefined,
|
|
@@ -52,26 +52,26 @@
|
|
|
52
52
|
|
|
53
53
|
// Tokyo Night inspired pastel colors for bubbles
|
|
54
54
|
const bubbleColors = [
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
"rgba(122, 162, 247, 0.2)", // Terminal Blue
|
|
56
|
+
"rgba(187, 154, 247, 0.2)", // Terminal Magenta
|
|
57
|
+
"rgba(158, 206, 106, 0.2)", // Strings/CSS classes
|
|
58
|
+
"rgba(115, 218, 202, 0.2)", // Terminal Green
|
|
59
|
+
"rgba(125, 207, 255, 0.2)", // Terminal Cyan
|
|
60
|
+
"rgba(247, 118, 142, 0.2)", // Keywords/Terminal Red
|
|
61
|
+
"rgba(224, 175, 104, 0.2)", // Terminal Yellow
|
|
62
|
+
"rgba(42, 195, 222, 0.2)", // Language functions
|
|
63
63
|
];
|
|
64
64
|
|
|
65
65
|
// Particle colors
|
|
66
66
|
const particleColors = [
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
67
|
+
"#7aa2f7", // Terminal Blue
|
|
68
|
+
"#bb9af7", // Terminal Magenta
|
|
69
|
+
"#9ece6a", // Strings/CSS classes
|
|
70
|
+
"#73daca", // Terminal Green
|
|
71
|
+
"#7dcfff", // Terminal Cyan
|
|
72
|
+
"#f7768e", // Keywords/Terminal Red
|
|
73
|
+
"#e0af68", // Terminal Yellow
|
|
74
|
+
"#2ac3de", // Language functions
|
|
75
75
|
];
|
|
76
76
|
|
|
77
77
|
// Generate random particles (matching React's 12 particles)
|
|
@@ -135,7 +135,11 @@
|
|
|
135
135
|
|
|
136
136
|
const oscillator1 = audioContext.createOscillator();
|
|
137
137
|
const oscillator2 = audioContext.createOscillator();
|
|
138
|
-
const noiseBuffer = audioContext.createBuffer(
|
|
138
|
+
const noiseBuffer = audioContext.createBuffer(
|
|
139
|
+
1,
|
|
140
|
+
audioContext.sampleRate * 0.1,
|
|
141
|
+
audioContext.sampleRate
|
|
142
|
+
);
|
|
139
143
|
const noiseSource = audioContext.createBufferSource();
|
|
140
144
|
|
|
141
145
|
// Generate white noise for the initial "crack"
|
|
@@ -166,8 +170,8 @@
|
|
|
166
170
|
oscillator2.frequency.setValueAtTime(3600, audioContext.currentTime);
|
|
167
171
|
oscillator2.frequency.exponentialRampToValueAtTime(1800, audioContext.currentTime + 0.04);
|
|
168
172
|
|
|
169
|
-
oscillator1.type =
|
|
170
|
-
oscillator2.type =
|
|
173
|
+
oscillator1.type = "triangle";
|
|
174
|
+
oscillator2.type = "sine";
|
|
171
175
|
|
|
172
176
|
// Sharp attack, quick decay
|
|
173
177
|
gainNode1.gain.setValueAtTime(0, audioContext.currentTime);
|
|
@@ -202,15 +206,16 @@
|
|
|
202
206
|
|
|
203
207
|
function playHitmarkerSound() {
|
|
204
208
|
try {
|
|
205
|
-
const hitmarkerDataUrl =
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
209
|
+
const hitmarkerDataUrl =
|
|
210
|
+
"data:audio/mpeg;base64,SUQzBAAAAAAANFRDT04AAAAHAAADT3RoZXIAVFNTRQAAAA8AAANMYXZmNTcuODMuMTAwAAAAAAAAAAAA" +
|
|
211
|
+
"AAD/+1QAAAAAAAAAAAAAAAAAAAAA" +
|
|
212
|
+
"AAAAAAAAAAAAAAAAAAAAAABJbmZvAAAADwAAAAYAAAnAADs7Ozs7Ozs7Ozs7Ozs7OztiYmJiYmJiYmJi" +
|
|
213
|
+
"YmJiYmJiYomJiYmJiYmJiYmJiYmJiYmxsbGxsbGxsbGxsbGxsbGxsdjY2NjY2NjY2NjY2NjY2NjY////" +
|
|
214
|
+
"/////////////////wAAAABMYXZjNTcuMTAAAAAAAAAAAAAAAAAkAkAAAAAAAAAJwOuMZun/+5RkAA8S" +
|
|
215
|
+
"/F23AGAaAi0AF0AAAAAInXsEAIRXyQ8D4OQgjEhE3cO7ujuHF0XCOu4G7xKbi3Funu7u7p9dw7unu7u7" +
|
|
216
|
+
"p7u7u6fXcW7om7u7uiU3dxdT67u7p7uHdxelN3cW6fXcW7oXXd3eJTd3d0+u4t3iXdw4up70W4uiPruL" +
|
|
217
|
+
"DzMw8Pz79Y99JfkyfPv5/h9uTJoy79Y99Y97q3vyZPJk0ZfrL6x73Vn+J35dKKS/STQyQ8CAiCPNuRAO" +
|
|
218
|
+
"OqquAx+fzJeBKDAsgAMBuWcBsHKhjJTcCwIALyAvABbI0ZIcCmP8jHJe8gZAdVRp2TpnU/kUXV4iQuBA";
|
|
214
219
|
|
|
215
220
|
const audio = new Audio(hitmarkerDataUrl);
|
|
216
221
|
audio.volume = 0.3;
|
|
@@ -239,7 +244,7 @@
|
|
|
239
244
|
playHitmarkerSound();
|
|
240
245
|
|
|
241
246
|
setTimeout(() => {
|
|
242
|
-
hitmarkers = hitmarkers.filter(h => h.id !== newHitmarker.id);
|
|
247
|
+
hitmarkers = hitmarkers.filter((h) => h.id !== newHitmarker.id);
|
|
243
248
|
}, 600);
|
|
244
249
|
}
|
|
245
250
|
|
|
@@ -287,25 +292,36 @@
|
|
|
287
292
|
// Status helpers
|
|
288
293
|
function getStatusLabel(s?: StreamStatus): string {
|
|
289
294
|
switch (s) {
|
|
290
|
-
case
|
|
291
|
-
|
|
292
|
-
case
|
|
293
|
-
|
|
294
|
-
case
|
|
295
|
-
|
|
296
|
-
case
|
|
297
|
-
|
|
298
|
-
|
|
295
|
+
case "ONLINE":
|
|
296
|
+
return "ONLINE";
|
|
297
|
+
case "OFFLINE":
|
|
298
|
+
return "OFFLINE";
|
|
299
|
+
case "INITIALIZING":
|
|
300
|
+
return "STARTING";
|
|
301
|
+
case "BOOTING":
|
|
302
|
+
return "STARTING";
|
|
303
|
+
case "WAITING_FOR_DATA":
|
|
304
|
+
return "WAITING";
|
|
305
|
+
case "SHUTTING_DOWN":
|
|
306
|
+
return "ENDING";
|
|
307
|
+
case "ERROR":
|
|
308
|
+
return "ERROR";
|
|
309
|
+
case "INVALID":
|
|
310
|
+
return "ERROR";
|
|
311
|
+
default:
|
|
312
|
+
return "CONNECTING";
|
|
299
313
|
}
|
|
300
314
|
}
|
|
301
315
|
|
|
302
316
|
let _statusLabel = $derived(getStatusLabel(status));
|
|
303
|
-
let showRetry = $derived((status ===
|
|
304
|
-
let showProgress = $derived(status ===
|
|
317
|
+
let showRetry = $derived((status === "ERROR" || status === "INVALID") && onRetry);
|
|
318
|
+
let showProgress = $derived(status === "INITIALIZING" && percentage !== undefined);
|
|
305
319
|
let displayMessage = $derived(error || message);
|
|
306
|
-
let isLoading = $derived(
|
|
307
|
-
|
|
308
|
-
|
|
320
|
+
let isLoading = $derived(
|
|
321
|
+
status === "INITIALIZING" || status === "BOOTING" || status === "WAITING_FOR_DATA" || !status
|
|
322
|
+
);
|
|
323
|
+
let isError = $derived(status === "ERROR" || status === "INVALID");
|
|
324
|
+
let isOffline = $derived(status === "OFFLINE");
|
|
309
325
|
|
|
310
326
|
// Update logo size on container resize
|
|
311
327
|
$effect(() => {
|
|
@@ -335,20 +351,199 @@
|
|
|
335
351
|
|
|
336
352
|
// Cleanup on destroy
|
|
337
353
|
onDestroy(() => {
|
|
338
|
-
bubbles.forEach(bubble => {
|
|
354
|
+
bubbles.forEach((bubble) => {
|
|
339
355
|
if (bubble.timeoutId) clearTimeout(bubble.timeoutId);
|
|
340
356
|
});
|
|
341
357
|
});
|
|
342
358
|
</script>
|
|
343
359
|
|
|
360
|
+
<div
|
|
361
|
+
bind:this={containerRef}
|
|
362
|
+
class="idle-container fw-player-root"
|
|
363
|
+
role="status"
|
|
364
|
+
aria-label="Stream status"
|
|
365
|
+
onmousemove={handleMouseMove}
|
|
366
|
+
onmouseleave={handleMouseLeave}
|
|
367
|
+
>
|
|
368
|
+
<!-- Hitmarkers -->
|
|
369
|
+
{#each hitmarkers as hitmarker (hitmarker.id)}
|
|
370
|
+
<div class="hitmarker" style="left: {hitmarker.x}px; top: {hitmarker.y}px;">
|
|
371
|
+
<div class="hitmarker-line tl"></div>
|
|
372
|
+
<div class="hitmarker-line tr"></div>
|
|
373
|
+
<div class="hitmarker-line bl"></div>
|
|
374
|
+
<div class="hitmarker-line br"></div>
|
|
375
|
+
</div>
|
|
376
|
+
{/each}
|
|
377
|
+
|
|
378
|
+
<!-- Floating particles -->
|
|
379
|
+
{#each particles as particle, _i}
|
|
380
|
+
<div
|
|
381
|
+
class="particle"
|
|
382
|
+
style="
|
|
383
|
+
left: {particle.left}%;
|
|
384
|
+
width: {particle.size}px;
|
|
385
|
+
height: {particle.size}px;
|
|
386
|
+
background: {particle.color};
|
|
387
|
+
animation-duration: {particle.duration}s;
|
|
388
|
+
animation-delay: {particle.delay}s;
|
|
389
|
+
"
|
|
390
|
+
></div>
|
|
391
|
+
{/each}
|
|
392
|
+
|
|
393
|
+
<!-- Animated bubbles -->
|
|
394
|
+
{#each bubbles as bubble, _i}
|
|
395
|
+
<div
|
|
396
|
+
class="bubble"
|
|
397
|
+
style="
|
|
398
|
+
top: {bubble.position.top}%;
|
|
399
|
+
left: {bubble.position.left}%;
|
|
400
|
+
width: {bubble.size}px;
|
|
401
|
+
height: {bubble.size}px;
|
|
402
|
+
background: {bubble.color};
|
|
403
|
+
opacity: {bubble.opacity};
|
|
404
|
+
"
|
|
405
|
+
></div>
|
|
406
|
+
{/each}
|
|
407
|
+
|
|
408
|
+
<!-- Center logo with push-away effect -->
|
|
409
|
+
<div
|
|
410
|
+
class="center-logo"
|
|
411
|
+
style="transform: translate(-50%, -50%) translate({offset.x}px, {offset.y}px);"
|
|
412
|
+
>
|
|
413
|
+
<!-- Pulsing circle background -->
|
|
414
|
+
<div
|
|
415
|
+
class="logo-pulse"
|
|
416
|
+
class:hovered={isHovered}
|
|
417
|
+
style="width: {logoSize * 1.4}px; height: {logoSize * 1.4}px;"
|
|
418
|
+
></div>
|
|
419
|
+
|
|
420
|
+
<!-- Logo image -->
|
|
421
|
+
<button type="button" class="logo-button" onclick={handleLogoClick} aria-label="Logo">
|
|
422
|
+
<img
|
|
423
|
+
src={logomarkAsset}
|
|
424
|
+
alt=""
|
|
425
|
+
class="logo-image"
|
|
426
|
+
class:hovered={isHovered}
|
|
427
|
+
style="width: {logoSize}px; height: {logoSize}px;"
|
|
428
|
+
draggable="false"
|
|
429
|
+
/>
|
|
430
|
+
</button>
|
|
431
|
+
</div>
|
|
432
|
+
|
|
433
|
+
<!-- Bouncing DVD Logo -->
|
|
434
|
+
<DvdLogo parentRef={containerRef} scale={0.08} />
|
|
435
|
+
|
|
436
|
+
<!-- Status overlay at bottom -->
|
|
437
|
+
<div class="status-overlay">
|
|
438
|
+
<div class="status-indicator">
|
|
439
|
+
<!-- Status icon -->
|
|
440
|
+
{#if isLoading}
|
|
441
|
+
<svg
|
|
442
|
+
class="status-icon spinning"
|
|
443
|
+
fill="none"
|
|
444
|
+
viewBox="0 0 24 24"
|
|
445
|
+
style="color: hsl(var(--tn-yellow, 40 95% 64%));"
|
|
446
|
+
>
|
|
447
|
+
<circle
|
|
448
|
+
style="opacity: 0.25;"
|
|
449
|
+
cx="12"
|
|
450
|
+
cy="12"
|
|
451
|
+
r="10"
|
|
452
|
+
stroke="currentColor"
|
|
453
|
+
stroke-width="4"
|
|
454
|
+
/>
|
|
455
|
+
<path
|
|
456
|
+
style="opacity: 0.75;"
|
|
457
|
+
fill="currentColor"
|
|
458
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
459
|
+
/>
|
|
460
|
+
</svg>
|
|
461
|
+
{:else if isOffline}
|
|
462
|
+
<svg
|
|
463
|
+
class="status-icon"
|
|
464
|
+
fill="none"
|
|
465
|
+
viewBox="0 0 24 24"
|
|
466
|
+
stroke="currentColor"
|
|
467
|
+
style="color: hsl(var(--tn-red, 348 100% 72%));"
|
|
468
|
+
>
|
|
469
|
+
<path
|
|
470
|
+
stroke-linecap="round"
|
|
471
|
+
stroke-linejoin="round"
|
|
472
|
+
stroke-width="2"
|
|
473
|
+
d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a4.978 4.978 0 01-1.414-2.83m-1.414 5.658a9 9 0 01-2.167-9.238m7.824 2.167a1 1 0 111.414 1.414m-1.414-1.414L3 3m8.293 8.293l1.414 1.414"
|
|
474
|
+
/>
|
|
475
|
+
</svg>
|
|
476
|
+
{:else if isError}
|
|
477
|
+
<svg
|
|
478
|
+
class="status-icon"
|
|
479
|
+
fill="none"
|
|
480
|
+
viewBox="0 0 24 24"
|
|
481
|
+
stroke="currentColor"
|
|
482
|
+
style="color: hsl(var(--tn-red, 348 100% 72%));"
|
|
483
|
+
>
|
|
484
|
+
<path
|
|
485
|
+
stroke-linecap="round"
|
|
486
|
+
stroke-linejoin="round"
|
|
487
|
+
stroke-width="2"
|
|
488
|
+
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
|
489
|
+
/>
|
|
490
|
+
</svg>
|
|
491
|
+
{:else}
|
|
492
|
+
<svg
|
|
493
|
+
class="status-icon spinning"
|
|
494
|
+
fill="none"
|
|
495
|
+
viewBox="0 0 24 24"
|
|
496
|
+
style="color: hsl(var(--tn-cyan, 193 100% 75%));"
|
|
497
|
+
>
|
|
498
|
+
<circle
|
|
499
|
+
style="opacity: 0.25;"
|
|
500
|
+
cx="12"
|
|
501
|
+
cy="12"
|
|
502
|
+
r="10"
|
|
503
|
+
stroke="currentColor"
|
|
504
|
+
stroke-width="4"
|
|
505
|
+
/>
|
|
506
|
+
<path
|
|
507
|
+
style="opacity: 0.75;"
|
|
508
|
+
fill="currentColor"
|
|
509
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
510
|
+
/>
|
|
511
|
+
</svg>
|
|
512
|
+
{/if}
|
|
513
|
+
<span>{displayMessage}</span>
|
|
514
|
+
</div>
|
|
515
|
+
|
|
516
|
+
<!-- Progress bar -->
|
|
517
|
+
{#if showProgress}
|
|
518
|
+
<div class="progress-bar">
|
|
519
|
+
<div class="progress-fill" style="width: {Math.min(100, percentage ?? 0)};"></div>
|
|
520
|
+
</div>
|
|
521
|
+
{/if}
|
|
522
|
+
|
|
523
|
+
<!-- Retry button -->
|
|
524
|
+
{#if showRetry}
|
|
525
|
+
<button type="button" class="retry-button" onclick={onRetry}> Retry </button>
|
|
526
|
+
{/if}
|
|
527
|
+
</div>
|
|
528
|
+
|
|
529
|
+
<!-- Subtle overlay texture -->
|
|
530
|
+
<div class="overlay-texture"></div>
|
|
531
|
+
</div>
|
|
532
|
+
|
|
344
533
|
<style>
|
|
345
534
|
@keyframes fadeInOut {
|
|
346
|
-
0%,
|
|
347
|
-
|
|
535
|
+
0%,
|
|
536
|
+
100% {
|
|
537
|
+
opacity: 0.6;
|
|
538
|
+
}
|
|
539
|
+
50% {
|
|
540
|
+
opacity: 0.9;
|
|
541
|
+
}
|
|
348
542
|
}
|
|
349
543
|
|
|
350
544
|
@keyframes logoPulse {
|
|
351
|
-
0%,
|
|
545
|
+
0%,
|
|
546
|
+
100% {
|
|
352
547
|
opacity: 0.15;
|
|
353
548
|
transform: scale(1);
|
|
354
549
|
}
|
|
@@ -363,8 +558,12 @@
|
|
|
363
558
|
transform: translateY(100vh) rotate(0deg);
|
|
364
559
|
opacity: 0;
|
|
365
560
|
}
|
|
366
|
-
10% {
|
|
367
|
-
|
|
561
|
+
10% {
|
|
562
|
+
opacity: 0.6;
|
|
563
|
+
}
|
|
564
|
+
90% {
|
|
565
|
+
opacity: 0.6;
|
|
566
|
+
}
|
|
368
567
|
100% {
|
|
369
568
|
transform: translateY(-100px) rotate(360deg);
|
|
370
569
|
opacity: 0;
|
|
@@ -372,8 +571,13 @@
|
|
|
372
571
|
}
|
|
373
572
|
|
|
374
573
|
@keyframes gradientShift {
|
|
375
|
-
0%,
|
|
376
|
-
|
|
574
|
+
0%,
|
|
575
|
+
100% {
|
|
576
|
+
background-position: 0% 50%;
|
|
577
|
+
}
|
|
578
|
+
50% {
|
|
579
|
+
background-position: 100% 50%;
|
|
580
|
+
}
|
|
377
581
|
}
|
|
378
582
|
|
|
379
583
|
@keyframes hitmarkerFade45 {
|
|
@@ -407,15 +611,20 @@
|
|
|
407
611
|
}
|
|
408
612
|
|
|
409
613
|
@keyframes spin {
|
|
410
|
-
from {
|
|
411
|
-
|
|
614
|
+
from {
|
|
615
|
+
transform: rotate(0deg);
|
|
616
|
+
}
|
|
617
|
+
to {
|
|
618
|
+
transform: rotate(360deg);
|
|
619
|
+
}
|
|
412
620
|
}
|
|
413
621
|
|
|
414
622
|
.fw-player-root .idle-container {
|
|
415
623
|
position: absolute;
|
|
416
624
|
inset: 0;
|
|
417
625
|
z-index: 5;
|
|
418
|
-
background: linear-gradient(
|
|
626
|
+
background: linear-gradient(
|
|
627
|
+
135deg,
|
|
419
628
|
hsl(var(--tn-bg-dark, 235 21% 11%)) 0%,
|
|
420
629
|
hsl(var(--tn-bg, 233 23% 17%)) 25%,
|
|
421
630
|
hsl(var(--tn-bg-dark, 235 21% 11%)) 50%,
|
|
@@ -487,6 +696,12 @@
|
|
|
487
696
|
transform: scale(1.2);
|
|
488
697
|
}
|
|
489
698
|
|
|
699
|
+
.fw-player-root .logo-button {
|
|
700
|
+
all: unset;
|
|
701
|
+
cursor: pointer;
|
|
702
|
+
display: block;
|
|
703
|
+
}
|
|
704
|
+
|
|
490
705
|
.fw-player-root .logo-image {
|
|
491
706
|
position: relative;
|
|
492
707
|
z-index: 1;
|
|
@@ -583,7 +798,7 @@
|
|
|
583
798
|
gap: 8px;
|
|
584
799
|
color: #787c99;
|
|
585
800
|
font-size: 13px;
|
|
586
|
-
font-family: -apple-system, BlinkMacSystemFont,
|
|
801
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
587
802
|
}
|
|
588
803
|
|
|
589
804
|
.fw-player-root .status-icon {
|
|
@@ -619,134 +834,10 @@
|
|
|
619
834
|
font-weight: 500;
|
|
620
835
|
cursor: pointer;
|
|
621
836
|
transition: all 0.2s ease;
|
|
622
|
-
font-family: -apple-system, BlinkMacSystemFont,
|
|
837
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
623
838
|
}
|
|
624
839
|
|
|
625
840
|
.fw-player-root .retry-button:hover {
|
|
626
841
|
background: rgba(122, 162, 247, 0.1);
|
|
627
842
|
}
|
|
628
843
|
</style>
|
|
629
|
-
|
|
630
|
-
<div
|
|
631
|
-
bind:this={containerRef}
|
|
632
|
-
class="idle-container fw-player-root"
|
|
633
|
-
role="status"
|
|
634
|
-
aria-label="Stream status"
|
|
635
|
-
onmousemove={handleMouseMove}
|
|
636
|
-
onmouseleave={handleMouseLeave}
|
|
637
|
-
>
|
|
638
|
-
<!-- Hitmarkers -->
|
|
639
|
-
{#each hitmarkers as hitmarker (hitmarker.id)}
|
|
640
|
-
<div class="hitmarker" style="left: {hitmarker.x}px; top: {hitmarker.y}px;">
|
|
641
|
-
<div class="hitmarker-line tl"></div>
|
|
642
|
-
<div class="hitmarker-line tr"></div>
|
|
643
|
-
<div class="hitmarker-line bl"></div>
|
|
644
|
-
<div class="hitmarker-line br"></div>
|
|
645
|
-
</div>
|
|
646
|
-
{/each}
|
|
647
|
-
|
|
648
|
-
<!-- Floating particles -->
|
|
649
|
-
{#each particles as particle, _i}
|
|
650
|
-
<div
|
|
651
|
-
class="particle"
|
|
652
|
-
style="
|
|
653
|
-
left: {particle.left}%;
|
|
654
|
-
width: {particle.size}px;
|
|
655
|
-
height: {particle.size}px;
|
|
656
|
-
background: {particle.color};
|
|
657
|
-
animation-duration: {particle.duration}s;
|
|
658
|
-
animation-delay: {particle.delay}s;
|
|
659
|
-
"
|
|
660
|
-
/>
|
|
661
|
-
{/each}
|
|
662
|
-
|
|
663
|
-
<!-- Animated bubbles -->
|
|
664
|
-
{#each bubbles as bubble, _i}
|
|
665
|
-
<div
|
|
666
|
-
class="bubble"
|
|
667
|
-
style="
|
|
668
|
-
top: {bubble.position.top}%;
|
|
669
|
-
left: {bubble.position.left}%;
|
|
670
|
-
width: {bubble.size}px;
|
|
671
|
-
height: {bubble.size}px;
|
|
672
|
-
background: {bubble.color};
|
|
673
|
-
opacity: {bubble.opacity};
|
|
674
|
-
"
|
|
675
|
-
/>
|
|
676
|
-
{/each}
|
|
677
|
-
|
|
678
|
-
<!-- Center logo with push-away effect -->
|
|
679
|
-
<div
|
|
680
|
-
class="center-logo"
|
|
681
|
-
style="transform: translate(-50%, -50%) translate({offset.x}px, {offset.y}px);"
|
|
682
|
-
>
|
|
683
|
-
<!-- Pulsing circle background -->
|
|
684
|
-
<div
|
|
685
|
-
class="logo-pulse"
|
|
686
|
-
class:hovered={isHovered}
|
|
687
|
-
style="width: {logoSize * 1.4}px; height: {logoSize * 1.4}px;"
|
|
688
|
-
/>
|
|
689
|
-
|
|
690
|
-
<!-- Logo image -->
|
|
691
|
-
<img
|
|
692
|
-
src={logomarkAsset}
|
|
693
|
-
alt="Logo"
|
|
694
|
-
class="logo-image"
|
|
695
|
-
class:hovered={isHovered}
|
|
696
|
-
style="width: {logoSize}px; height: {logoSize}px;"
|
|
697
|
-
onclick={handleLogoClick}
|
|
698
|
-
draggable="false"
|
|
699
|
-
/>
|
|
700
|
-
</div>
|
|
701
|
-
|
|
702
|
-
<!-- Bouncing DVD Logo -->
|
|
703
|
-
<DvdLogo parentRef={containerRef} scale={0.08} />
|
|
704
|
-
|
|
705
|
-
<!-- Status overlay at bottom -->
|
|
706
|
-
<div class="status-overlay">
|
|
707
|
-
<div class="status-indicator">
|
|
708
|
-
<!-- Status icon -->
|
|
709
|
-
{#if isLoading}
|
|
710
|
-
<svg class="status-icon spinning" fill="none" viewBox="0 0 24 24" style="color: hsl(var(--tn-yellow, 40 95% 64%));">
|
|
711
|
-
<circle style="opacity: 0.25;" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
|
|
712
|
-
<path style="opacity: 0.75;" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
|
|
713
|
-
</svg>
|
|
714
|
-
{:else if isOffline}
|
|
715
|
-
<svg class="status-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor" style="color: hsl(var(--tn-red, 348 100% 72%));">
|
|
716
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a4.978 4.978 0 01-1.414-2.83m-1.414 5.658a9 9 0 01-2.167-9.238m7.824 2.167a1 1 0 111.414 1.414m-1.414-1.414L3 3m8.293 8.293l1.414 1.414" />
|
|
717
|
-
</svg>
|
|
718
|
-
{:else if isError}
|
|
719
|
-
<svg class="status-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor" style="color: hsl(var(--tn-red, 348 100% 72%));">
|
|
720
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
|
721
|
-
</svg>
|
|
722
|
-
{:else}
|
|
723
|
-
<svg class="status-icon spinning" fill="none" viewBox="0 0 24 24" style="color: hsl(var(--tn-cyan, 193 100% 75%));">
|
|
724
|
-
<circle style="opacity: 0.25;" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
|
|
725
|
-
<path style="opacity: 0.75;" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
|
|
726
|
-
</svg>
|
|
727
|
-
{/if}
|
|
728
|
-
<span>{displayMessage}</span>
|
|
729
|
-
</div>
|
|
730
|
-
|
|
731
|
-
<!-- Progress bar -->
|
|
732
|
-
{#if showProgress}
|
|
733
|
-
<div class="progress-bar">
|
|
734
|
-
<div class="progress-fill" style="width: {Math.min(100, percentage ?? 0)}%;" />
|
|
735
|
-
</div>
|
|
736
|
-
{/if}
|
|
737
|
-
|
|
738
|
-
<!-- Retry button -->
|
|
739
|
-
{#if showRetry}
|
|
740
|
-
<button
|
|
741
|
-
type="button"
|
|
742
|
-
class="retry-button"
|
|
743
|
-
onclick={onRetry}
|
|
744
|
-
>
|
|
745
|
-
Retry
|
|
746
|
-
</button>
|
|
747
|
-
{/if}
|
|
748
|
-
</div>
|
|
749
|
-
|
|
750
|
-
<!-- Subtle overlay texture -->
|
|
751
|
-
<div class="overlay-texture" />
|
|
752
|
-
</div>
|