feat: SSE live fight spectating with spectator count

Enable real-time fight spectating for all live fights (not just human
fights). Multiple spectators can watch simultaneously via SSE. Spectator
count is tracked per-fight and broadcast with every SSE event.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-08 19:46:39 +00:00
co-authored by Claude Opus 4.6
parent a540320901
commit 610e799605
18 changed files with 1555 additions and 397 deletions
+23 -16
View File
@@ -643,9 +643,9 @@ async function _doReplay() {
<div v-if="announcementVisible"
class="absolute inset-0 flex items-center justify-center pointer-events-none z-20">
<div class="announce-text-wrapper">
<p class="font-funky text-2xl sm:text-5xl lg:text-7xl tracking-widest announce-text uppercase announce-chromatic"
<p class="font-funky text-3xl sm:text-6xl lg:text-8xl tracking-widest announce-text-3d uppercase announce-chromatic"
:data-text="announcement"
:style="{ color: announcementColor, textShadow: `0 0 20px ${announcementColor}, 0 0 40px ${announcementColor}, 0 0 80px ${announcementColor}40, 0 0 120px ${announcementColor}20` }">
:style="{ '--announce-color': announcementColor }">
{{ announcement }}
</p>
</div>
@@ -720,20 +720,24 @@ async function _doReplay() {
position: relative;
}
.announce-text {
animation: announce-pulse 0.4s ease-in-out infinite alternate, announce-hue 2s linear infinite;
-webkit-text-stroke: 1px rgba(0,0,0,0.3);
/* Colourful 3D extruded text */
.announce-text-3d {
color: var(--announce-color, #ffffff);
paint-order: stroke fill;
-webkit-text-stroke: 2px rgba(0,0,0,0.6);
text-shadow:
2px 2px 0 #1a0020,
4px 4px 0 #2a0040,
6px 6px 0 #3a0060,
8px 8px 0 #4d0080,
0 0 20px var(--announce-color, #ffffff),
0 0 60px var(--announce-color, #ffffff);
will-change: transform;
animation: announce-3d-pulse 0.35s ease-in-out infinite alternate;
}
@keyframes announce-pulse {
from { transform: scale(1) rotate(-1deg); }
to { transform: scale(1.08) rotate(1deg); }
}
@keyframes announce-hue {
0% { filter: hue-rotate(0deg) brightness(1); }
25% { filter: hue-rotate(15deg) brightness(1.1); }
50% { filter: hue-rotate(0deg) brightness(1.2); }
75% { filter: hue-rotate(-15deg) brightness(1.1); }
100% { filter: hue-rotate(0deg) brightness(1); }
@keyframes announce-3d-pulse {
from { transform: scale(1) rotate(-0.5deg); }
to { transform: scale(1.06) rotate(0.5deg); }
}
/* Chromatic aberration on announcements */
@@ -748,8 +752,11 @@ async function _doReplay() {
left: 0;
right: 0;
text-align: center;
opacity: 0.5;
opacity: 0.35;
pointer-events: none;
text-shadow: none;
-webkit-text-stroke: 0;
will-change: transform;
}
.announce-chromatic::before {
color: #ff2d7b;