@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.
Files changed (88) hide show
  1. package/dist/DevModePanel.svelte +266 -127
  2. package/dist/DevModePanel.svelte.d.ts +1 -1
  3. package/dist/DvdLogo.svelte +17 -21
  4. package/dist/Icons.svelte +5 -3
  5. package/dist/Icons.svelte.d.ts +6 -19
  6. package/dist/IdleScreen.svelte +277 -186
  7. package/dist/IdleScreen.svelte.d.ts +1 -1
  8. package/dist/LoadingScreen.svelte +190 -162
  9. package/dist/Player.svelte +244 -111
  10. package/dist/Player.svelte.d.ts +1 -1
  11. package/dist/PlayerControls.svelte +263 -168
  12. package/dist/PlayerControls.svelte.d.ts +1 -1
  13. package/dist/SeekBar.svelte +61 -35
  14. package/dist/SkipIndicator.svelte +4 -4
  15. package/dist/SkipIndicator.svelte.d.ts +1 -1
  16. package/dist/SpeedIndicator.svelte +1 -1
  17. package/dist/StatsPanel.svelte +76 -57
  18. package/dist/StatsPanel.svelte.d.ts +1 -1
  19. package/dist/StreamStateOverlay.svelte +143 -107
  20. package/dist/StreamStateOverlay.svelte.d.ts +1 -1
  21. package/dist/SubtitleRenderer.svelte +46 -43
  22. package/dist/ThumbnailOverlay.svelte +22 -19
  23. package/dist/TitleOverlay.svelte +6 -11
  24. package/dist/components/VolumeIcons.svelte +12 -6
  25. package/dist/global.d.ts +3 -3
  26. package/dist/icons/FullscreenExitIcon.svelte +1 -5
  27. package/dist/icons/FullscreenIcon.svelte +1 -5
  28. package/dist/icons/PauseIcon.svelte +1 -5
  29. package/dist/icons/PictureInPictureIcon.svelte +12 -6
  30. package/dist/icons/PlayIcon.svelte +1 -5
  31. package/dist/icons/SeekToLiveIcon.svelte +1 -5
  32. package/dist/icons/SettingsIcon.svelte +1 -5
  33. package/dist/icons/SkipBackIcon.svelte +1 -5
  34. package/dist/icons/SkipForwardIcon.svelte +1 -5
  35. package/dist/icons/StatsIcon.svelte +1 -5
  36. package/dist/icons/VolumeOffIcon.svelte +1 -5
  37. package/dist/icons/VolumeUpIcon.svelte +1 -5
  38. package/dist/icons/index.d.ts +12 -12
  39. package/dist/icons/index.js +12 -12
  40. package/dist/index.d.ts +24 -24
  41. package/dist/index.js +21 -21
  42. package/dist/stores/index.d.ts +6 -6
  43. package/dist/stores/index.js +6 -6
  44. package/dist/stores/playbackQuality.d.ts +2 -2
  45. package/dist/stores/playbackQuality.js +7 -7
  46. package/dist/stores/playerContext.d.ts +2 -2
  47. package/dist/stores/playerContext.js +17 -17
  48. package/dist/stores/playerController.d.ts +13 -4
  49. package/dist/stores/playerController.js +80 -56
  50. package/dist/stores/playerSelection.d.ts +2 -2
  51. package/dist/stores/playerSelection.js +7 -7
  52. package/dist/stores/streamState.d.ts +2 -2
  53. package/dist/stores/streamState.js +56 -56
  54. package/dist/stores/viewerEndpoints.d.ts +3 -3
  55. package/dist/stores/viewerEndpoints.js +21 -21
  56. package/dist/types.d.ts +1 -1
  57. package/dist/ui/Badge.svelte +9 -10
  58. package/dist/ui/Badge.svelte.d.ts +8 -29
  59. package/dist/ui/Button.svelte +16 -16
  60. package/dist/ui/Button.svelte.d.ts +8 -29
  61. package/dist/ui/Slider.svelte +21 -55
  62. package/dist/ui/badge.js +1 -1
  63. package/dist/ui/button.js +2 -2
  64. package/dist/ui/context-menu/ContextMenuCheckboxItem.svelte +5 -7
  65. package/dist/ui/context-menu/ContextMenuCheckboxItem.svelte.d.ts +6 -27
  66. package/dist/ui/context-menu/ContextMenuContent.svelte +2 -9
  67. package/dist/ui/context-menu/ContextMenuItem.svelte +1 -5
  68. package/dist/ui/context-menu/ContextMenuLabel.svelte +1 -5
  69. package/dist/ui/context-menu/ContextMenuRadioItem.svelte +5 -7
  70. package/dist/ui/context-menu/ContextMenuRadioItem.svelte.d.ts +6 -27
  71. package/dist/ui/context-menu/ContextMenuSeparator.svelte +2 -8
  72. package/dist/ui/context-menu/ContextMenuShortcut.svelte +2 -12
  73. package/dist/ui/context-menu/ContextMenuSubContent.svelte +1 -5
  74. package/package.json +15 -7
  75. package/src/DevModePanel.svelte +1 -0
  76. package/src/Icons.svelte +5 -3
  77. package/src/IdleScreen.svelte +21 -14
  78. package/src/LoadingScreen.svelte +20 -13
  79. package/src/Player.svelte +48 -2
  80. package/src/PlayerControls.svelte +36 -17
  81. package/src/SeekBar.svelte +33 -0
  82. package/src/StreamStateOverlay.svelte +2 -2
  83. package/src/stores/playerController.ts +39 -1
  84. package/src/stores/viewerEndpoints.ts +1 -1
  85. package/src/ui/Badge.svelte +7 -4
  86. package/src/ui/Button.svelte +13 -13
  87. package/src/ui/context-menu/ContextMenuCheckboxItem.svelte +4 -2
  88. package/src/ui/context-menu/ContextMenuRadioItem.svelte +4 -2
@@ -13,10 +13,10 @@
13
13
  - Status overlay at bottom
14
14
  -->
15
15
  <script lang="ts">
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';
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 = 'OFFLINE',
31
- message = 'Waiting for stream...',
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
- '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
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
- '#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
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(1, audioContext.sampleRate * 0.1, audioContext.sampleRate);
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 = 'triangle';
170
- oscillator2.type = 'sine';
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 = 'data:audio/mpeg;base64,SUQzBAAAAAAANFRDT04AAAAHAAADT3RoZXIAVFNTRQAAAA8AAANMYXZmNTcuODMuMTAwAAAAAAAAAAAA' +
206
- 'AAD/+1QAAAAAAAAAAAAAAAAAAAAA' +
207
- 'AAAAAAAAAAAAAAAAAAAAAABJbmZvAAAADwAAAAYAAAnAADs7Ozs7Ozs7Ozs7Ozs7OztiYmJiYmJiYmJi' +
208
- 'YmJiYmJiYomJiYmJiYmJiYmJiYmJiYmxsbGxsbGxsbGxsbGxsbGxsdjY2NjY2NjY2NjY2NjY2NjY////' +
209
- '/////////////////wAAAABMYXZjNTcuMTAAAAAAAAAAAAAAAAAkAkAAAAAAAAAJwOuMZun/+5RkAA8S' +
210
- '/F23AGAaAi0AF0AAAAAInXsEAIRXyQ8D4OQgjEhE3cO7ujuHF0XCOu4G7xKbi3Funu7u7p9dw7unu7u7' +
211
- 'p7u7u6fXcW7om7u7uiU3dxdT67u7p7uHdxelN3cW6fXcW7oXXd3eJTd3d0+u4t3iXdw4up70W4uiPruL' +
212
- 'DzMw8Pz79Y99JfkyfPv5/h9uTJoy79Y99Y97q3vyZPJk0ZfrL6x73Vn+J35dKKS/STQyQ8CAiCPNuRAO' +
213
- 'OqquAx+fzJeBKDAsgAMBuWcBsHKhjJTcCwIALyAvABbI0ZIcCmP8jHJe8gZAdVRp2TpnU/kUXV4iQuBA';
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 'ONLINE': return 'ONLINE';
291
- case 'OFFLINE': return 'OFFLINE';
292
- case 'INITIALIZING': return 'STARTING';
293
- case 'BOOTING': return 'STARTING';
294
- case 'WAITING_FOR_DATA': return 'WAITING';
295
- case 'SHUTTING_DOWN': return 'ENDING';
296
- case 'ERROR': return 'ERROR';
297
- case 'INVALID': return 'ERROR';
298
- default: return 'CONNECTING';
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 === 'ERROR' || status === 'INVALID') && onRetry);
304
- let showProgress = $derived(status === 'INITIALIZING' && percentage !== undefined);
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(status === 'INITIALIZING' || status === 'BOOTING' || status === 'WAITING_FOR_DATA' || !status);
307
- let isError = $derived(status === 'ERROR' || status === 'INVALID');
308
- let isOffline = $derived(status === 'OFFLINE');
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%, 100% { opacity: 0.6; }
347
- 50% { opacity: 0.9; }
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%, 100% {
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% { opacity: 0.6; }
367
- 90% { opacity: 0.6; }
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%, 100% { background-position: 0% 50%; }
376
- 50% { background-position: 100% 50%; }
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 { transform: rotate(0deg); }
411
- to { transform: rotate(360deg); }
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(135deg,
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, 'Segoe UI', Roboto, sans-serif;
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, 'Segoe UI', Roboto, sans-serif;
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>
@@ -1,4 +1,4 @@
1
- import type { StreamStatus } from '@livepeer-frameworks/player-core';
1
+ import type { StreamStatus } from "@livepeer-frameworks/player-core";
2
2
  interface Props {
3
3
  status?: StreamStatus;
4
4
  message?: string;