Feat: Now-Playing serverseitig syncen + in Topbar verschieben
Backend:
- nowPlaying Map trackt aktuell gespielten Sound pro Guild
- SSE broadcast { type: 'nowplaying' } bei play und stop
- nowplaying im SSE-Snapshot für neue Clients
- playFilePath Helper broadcastet ebenfalls (Party Mode)
Frontend:
- SSE-Handler für nowplaying Events (sync über alle Clients)
- Now-Playing als Pill-Badge in der Topbar (rechts, neben Channel)
- Bottombar komplett entfernt
- Fade-in Animation und accent-farbige Pill
- --accent-rgb CSS Variable für alle Themes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4661c366fb
commit
f90401a009
3 changed files with 58 additions and 46 deletions
|
|
@ -154,12 +154,20 @@ export default function App() {
|
|||
const g = selectedRef.current?.split(':')[0];
|
||||
if (g && typeof vols[g] === 'number') setVolume(vols[g]);
|
||||
} catch { }
|
||||
try {
|
||||
const np = msg?.nowplaying || {};
|
||||
const g = selectedRef.current?.split(':')[0];
|
||||
if (g && typeof np[g] === 'string') setLastPlayed(np[g]);
|
||||
} catch { }
|
||||
} else if (msg?.type === 'channel') {
|
||||
const g = selectedRef.current?.split(':')[0];
|
||||
if (msg.guildId === g) setSelected(`${msg.guildId}:${msg.channelId}`);
|
||||
} else if (msg?.type === 'volume') {
|
||||
const g = selectedRef.current?.split(':')[0];
|
||||
if (msg.guildId === g && typeof msg.volume === 'number') setVolume(msg.volume);
|
||||
} else if (msg?.type === 'nowplaying') {
|
||||
const g = selectedRef.current?.split(':')[0];
|
||||
if (msg.guildId === g) setLastPlayed(msg.name || '');
|
||||
}
|
||||
});
|
||||
return () => { try { unsub(); } catch { } };
|
||||
|
|
@ -362,6 +370,15 @@ export default function App() {
|
|||
</div>
|
||||
|
||||
<div className="topbar-right">
|
||||
{lastPlayed && (
|
||||
<div className="now-playing">
|
||||
<div className="np-waves active">
|
||||
<div className="np-wave-bar" /><div className="np-wave-bar" />
|
||||
<div className="np-wave-bar" /><div className="np-wave-bar" />
|
||||
</div>
|
||||
<span className="np-name">{lastPlayed}</span>
|
||||
</div>
|
||||
)}
|
||||
{selected && (
|
||||
<div className="connection">
|
||||
<span className="conn-dot" />
|
||||
|
|
@ -594,18 +611,6 @@ export default function App() {
|
|||
)}
|
||||
</main>
|
||||
|
||||
{/* ═══ BOTTOM BAR ═══ */}
|
||||
<div className="bottombar">
|
||||
<div className="now-playing">
|
||||
<div className={`np-waves ${lastPlayed ? 'active' : ''}`}>
|
||||
<div className="np-wave-bar" /><div className="np-wave-bar" />
|
||||
<div className="np-wave-bar" /><div className="np-wave-bar" />
|
||||
</div>
|
||||
<span className="np-label">Spielt:</span>
|
||||
<span className="np-name">{lastPlayed || '—'}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ═══ CONTEXT MENU ═══ */}
|
||||
{ctxMenu && (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
--text-faint: #6d6f78;
|
||||
|
||||
--accent: #5865f2;
|
||||
--accent-rgb: 88, 101, 242;
|
||||
--accent-hover: #4752c4;
|
||||
--accent-glow: rgba(88, 101, 242, .45);
|
||||
|
||||
|
|
@ -53,6 +54,7 @@
|
|||
--bg-secondary: #241f35;
|
||||
--bg-tertiary: #2e2845;
|
||||
--accent: #9b59b6;
|
||||
--accent-rgb: 155, 89, 182;
|
||||
--accent-hover: #8e44ad;
|
||||
--accent-glow: rgba(155, 89, 182, .45);
|
||||
}
|
||||
|
|
@ -64,6 +66,7 @@
|
|||
--bg-secondary: #1c2e22;
|
||||
--bg-tertiary: #253a2c;
|
||||
--accent: #2ecc71;
|
||||
--accent-rgb: 46, 204, 113;
|
||||
--accent-hover: #27ae60;
|
||||
--accent-glow: rgba(46, 204, 113, .4);
|
||||
}
|
||||
|
|
@ -75,6 +78,7 @@
|
|||
--bg-secondary: #2f201c;
|
||||
--bg-tertiary: #3d2a24;
|
||||
--accent: #e67e22;
|
||||
--accent-rgb: 230, 126, 34;
|
||||
--accent-hover: #d35400;
|
||||
--accent-glow: rgba(230, 126, 34, .4);
|
||||
}
|
||||
|
|
@ -86,6 +90,7 @@
|
|||
--bg-secondary: #162a42;
|
||||
--bg-tertiary: #1e3652;
|
||||
--accent: #3498db;
|
||||
--accent-rgb: 52, 152, 219;
|
||||
--accent-hover: #2980b9;
|
||||
--accent-glow: rgba(52, 152, 219, .4);
|
||||
}
|
||||
|
|
@ -962,36 +967,25 @@ input, select {
|
|||
max-width: 260px;
|
||||
}
|
||||
|
||||
/* ────────────────────────────────────────────
|
||||
Bottom Bar
|
||||
──────────────────────────────────────────── */
|
||||
.bottombar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
padding: 0 20px;
|
||||
height: 48px;
|
||||
background: var(--bg-secondary);
|
||||
border-top: 1px solid rgba(0, 0, 0, .24);
|
||||
z-index: 10;
|
||||
flex-shrink: 0;
|
||||
transition: background .4s ease;
|
||||
}
|
||||
|
||||
/* ── Now Playing (Topbar) ── */
|
||||
.now-playing {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
gap: 6px;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
background: rgba(var(--accent-rgb, 88, 101, 242), .12);
|
||||
border: 1px solid rgba(var(--accent-rgb, 88, 101, 242), .2);
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
max-width: 200px;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
animation: np-fade-in 300ms ease;
|
||||
}
|
||||
|
||||
.np-label {
|
||||
color: var(--text-faint);
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
@keyframes np-fade-in {
|
||||
from { opacity: 0; transform: translateX(10px); }
|
||||
to { opacity: 1; transform: translateX(0); }
|
||||
}
|
||||
|
||||
.np-name {
|
||||
|
|
@ -1006,7 +1000,8 @@ input, select {
|
|||
display: none;
|
||||
gap: 1.5px;
|
||||
align-items: flex-end;
|
||||
height: 14px;
|
||||
height: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.np-waves.active {
|
||||
|
|
@ -1014,16 +1009,16 @@ input, select {
|
|||
}
|
||||
|
||||
.np-wave-bar {
|
||||
width: 2.5px;
|
||||
width: 2px;
|
||||
background: var(--accent);
|
||||
border-radius: 1px;
|
||||
animation: wave 500ms ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.np-wave-bar:nth-child(1) { height: 4px; animation-delay: 0ms; }
|
||||
.np-wave-bar:nth-child(2) { height: 9px; animation-delay: 120ms; }
|
||||
.np-wave-bar:nth-child(3) { height: 6px; animation-delay: 240ms; }
|
||||
.np-wave-bar:nth-child(4) { height: 11px; animation-delay: 80ms; }
|
||||
.np-wave-bar:nth-child(1) { height: 3px; animation-delay: 0ms; }
|
||||
.np-wave-bar:nth-child(2) { height: 8px; animation-delay: 120ms; }
|
||||
.np-wave-bar:nth-child(3) { height: 5px; animation-delay: 240ms; }
|
||||
.np-wave-bar:nth-child(4) { height: 10px; animation-delay: 80ms; }
|
||||
|
||||
/* ── Volume Control (Toolbar) ── */
|
||||
.volume-control {
|
||||
|
|
@ -1398,10 +1393,6 @@ input, select {
|
|||
font-size: 11px;
|
||||
}
|
||||
|
||||
.bottombar {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.tb-btn span:not(.tb-icon) {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -1416,6 +1407,10 @@ input, select {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.now-playing {
|
||||
max-width: 120px;
|
||||
}
|
||||
|
||||
.toolbar .tb-btn {
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue