feat: Emberwake, Crucible phases, Linux agent, musical dashboard, e2e
Emberwake spread/waterhole UI, campaign DB, spread handler, spread-kit web publisher, and SPREAD_TECHNIQUES doc. Crucible Phase A-C: expanded ops, port-forward matrix, remote dir browser, crucible help/tests. Linux agent hardening: credential vault, persistence audit, firewall/defender deploy, SMB spread status, CPU stats, screenshots/crypt/file-ops split. Docker compose and agent/server images with e2e validation script and docs. Musical dashboard: ambient music player, hover SFX, SoundContext/AmbientMusicContext, steampunk polish. Public builds API, dropper handler updates, SessionGate and fleet UX. README and PROBLEMS.md refresh.
This commit is contained in:
48
server/web/src/components/GlobalMusicPlayer.tsx
Normal file
48
server/web/src/components/GlobalMusicPlayer.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { useAmbientMusic } from '../context/AmbientMusicContext';
|
||||
import './GlobalMusicPlayer.css';
|
||||
|
||||
export default function GlobalMusicPlayer() {
|
||||
const { enabled, playing, volume, setVolume, togglePlay } = useAmbientMusic();
|
||||
|
||||
return (
|
||||
<div
|
||||
className="global-music-player"
|
||||
data-sfx="off"
|
||||
role="region"
|
||||
aria-label="Background music controls"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="global-music-player__play"
|
||||
onClick={togglePlay}
|
||||
aria-label={playing ? 'Pause background music' : 'Play background music'}
|
||||
title={playing ? 'Pause music' : enabled ? 'Play music' : 'Enable & play music'}
|
||||
>
|
||||
{playing ? (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<rect x="6" y="5" width="4" height="14" rx="1" fill="currentColor" />
|
||||
<rect x="14" y="5" width="4" height="14" rx="1" fill="currentColor" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M8 5v14l11-7z" fill="currentColor" />
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
<label className="global-music-player__vol" title="Music volume">
|
||||
<span className="sr-only">Music volume</span>
|
||||
<input
|
||||
type="range"
|
||||
min={0}
|
||||
max={100}
|
||||
step={5}
|
||||
value={Math.round(volume * 100)}
|
||||
onChange={(e) => setVolume(parseInt(e.target.value, 10) / 100)}
|
||||
aria-valuenow={Math.round(volume * 100)}
|
||||
aria-valuemin={0}
|
||||
aria-valuemax={100}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user