diff --git a/web/src/App.tsx b/web/src/App.tsx index e281b3a..c88c4c8 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -216,12 +216,12 @@ export default function App() { try { await playSound(s.name, guildId, channelId, volume, s.relativePath); setLastPlayed(s.name); - setTimeout(() => setLastPlayed(''), 4000); } catch (e: any) { notify(e?.message || 'Play fehlgeschlagen', 'error'); } } async function handleStop() { if (!selected) return; + setLastPlayed(''); try { await fetch(`/api/stop?guildId=${encodeURIComponent(guildId)}`, { method: 'POST' }); } catch { } } @@ -421,6 +421,34 @@ export default function App() {
+
+ { + const newVol = volume > 0 ? 0 : 0.5; + setVolume(newVol); + if (guildId) setVolumeLive(guildId, newVol).catch(() => {}); + }} + > + {volume === 0 ? 'volume_off' : volume < 0.5 ? 'volume_down' : 'volume_up'} + + { + const v = parseFloat(e.target.value); + setVolume(v); + if (guildId) try { await setVolumeLive(guildId, v); } catch { } + }} + style={{ '--vol': `${Math.round(volume * 100)}%` } as React.CSSProperties} + /> + {Math.round(volume * 100)}% +
+
-
- { - const newVol = volume > 0 ? 0 : 0.5; - setVolume(newVol); - if (guildId) setVolumeLive(guildId, newVol).catch(() => {}); - }} - > - {volume === 0 ? 'volume_off' : volume < 0.5 ? 'volume_down' : 'volume_up'} - - { - const v = parseFloat(e.target.value); - setVolume(v); - if (guildId) try { await setVolumeLive(guildId, v); } catch { } - }} - style={{ '--vol': `${Math.round(volume * 100)}%` } as React.CSSProperties} - /> - {Math.round(volume * 100)}% -
{/* ═══ CONTEXT MENU ═══ */} diff --git a/web/src/styles.css b/web/src/styles.css index 92185be..b715f20 100644 --- a/web/src/styles.css +++ b/web/src/styles.css @@ -1025,57 +1025,64 @@ input, select { .np-wave-bar:nth-child(3) { height: 6px; animation-delay: 240ms; } .np-wave-bar:nth-child(4) { height: 11px; animation-delay: 80ms; } -/* ── Volume ── */ -.volume-section { +/* ── Volume Control (Toolbar) ── */ +.volume-control { display: flex; align-items: center; - gap: 8px; - margin-left: auto; + gap: 6px; + padding: 4px 10px; + border-radius: 20px; + background: var(--bg-tertiary); + border: 1px solid rgba(255, 255, 255, .06); } -.volume-icon { - font-size: 18px; - color: var(--text-muted); +.vol-icon { + font-size: 16px; + color: var(--text-faint); cursor: pointer; transition: color var(--transition); + user-select: none; } -.volume-icon:hover { +.vol-icon:hover { color: var(--text-normal); } -.volume-slider { +.vol-slider { -webkit-appearance: none; appearance: none; - width: 90px; + width: 80px; height: 3px; border-radius: 2px; - background: linear-gradient(to right, var(--accent) 0%, var(--accent) var(--vol, 80%), var(--bg-tertiary) var(--vol, 80%)); + background: linear-gradient(to right, var(--accent) 0%, var(--accent) var(--vol, 80%), var(--bg-modifier-selected) var(--vol, 80%)); outline: none; cursor: pointer; } -.volume-slider::-webkit-slider-thumb { +.vol-slider::-webkit-slider-thumb { -webkit-appearance: none; width: 12px; height: 12px; border-radius: 50%; - background: var(--white); - box-shadow: 0 1px 4px rgba(0, 0, 0, .3); + background: var(--accent); cursor: pointer; + transition: transform var(--transition); } -.volume-slider::-moz-range-thumb { +.vol-slider::-webkit-slider-thumb:hover { + transform: scale(1.3); +} + +.vol-slider::-moz-range-thumb { width: 12px; height: 12px; border-radius: 50%; - background: var(--white); - box-shadow: 0 1px 4px rgba(0, 0, 0, .3); + background: var(--accent); border: none; cursor: pointer; } -.volume-pct { +.vol-pct { font-size: 11px; color: var(--text-faint); min-width: 28px;