// HERO PLAYER — inline audio player placeholder
const { useState: useStatePl, useRef: useRefPl, useEffect: useEffectPl } = React;
// When you're ready, drop mp3s into assets/preview/ and fill this queue.
// Set `src: "assets/preview/your-file.mp3"` on any track to activate it.
const QUEUE = [
{ title: "Neon Memories", album: "Digital Blade", src: "assets/preview/neon-memories.mp3", cover: "assets/albums/03-digital-blade.png" },
];
function HeroPlayer() {
const [ix, setIx] = useStatePl(0);
const [playing, setPlaying] = useStatePl(false);
const [prog, setProg] = useStatePl(0);
const [dur, setDur] = useStatePl(0);
const [cur, setCur] = useStatePl(0);
const [vol, setVol] = useStatePl(0.8);
const [muted, setMuted] = useStatePl(false);
const audioRef = useRefPl(null);
const track = QUEUE[ix];
const ready = !!track.src;
useEffectPl(() => {
const a = audioRef.current;
if (!a) return;
setPlaying(false);
setProg(0); setCur(0);
if (ready) { a.load(); }
}, [ix, ready]);
useEffectPl(() => {
const a = audioRef.current;
if (!a) return;
a.volume = muted ? 0 : vol;
}, [vol, muted]);
const toggle = () => {
if (!ready) return;
const a = audioRef.current;
if (!a) return;
if (a.paused) { a.play(); setPlaying(true); }
else { a.pause(); setPlaying(false); }
};
const prev = () => setIx((ix - 1 + QUEUE.length) % QUEUE.length);
const next = () => setIx((ix + 1) % QUEUE.length);
const onTime = () => {
const a = audioRef.current; if (!a) return;
setCur(a.currentTime); setDur(a.duration || 0);
setProg(a.duration ? (a.currentTime / a.duration) * 100 : 0);
};
const seek = (e) => {
if (!ready) return;
const a = audioRef.current; if (!a || !a.duration) return;
const r = e.currentTarget.getBoundingClientRect();
const p = (e.clientX - r.left) / r.width;
a.currentTime = p * a.duration;
};
const fmt = (s) => {
if (!isFinite(s) || s < 0) s = 0;
const m = Math.floor(s / 60);
const sec = Math.floor(s % 60);
return `${String(m).padStart(2,"0")}:${String(sec).padStart(2,"0")}`;
};
// Animated equalizer bars (purely visual)
const bars = Array.from({length: 40});
return (
{ready ? (playing ? "NOW PLAYING" : "READY") : "PLAYER // STANDBY"}
TRK {String(ix+1).padStart(2,"0")}/{String(QUEUE.length).padStart(2,"0")}
{/* Spinning disc */}
{track.album.toUpperCase()}
{track.title}
DJ GAIA
{/* Equalizer */}
{bars.map((_, i) => {
const env = Math.sin((i / bars.length) * Math.PI);
const base = 8 + env * 70;
return ;
})}
{/* Progress */}
{fmt(cur)}
{ready ? fmt(dur) : "--:--"}
{/* Controls */}
{/* Volume */}
{ setVol(parseFloat(e.target.value)); setMuted(false); }}
style={pl.volSlider} title="Volume"
/>
{String(Math.round((muted ? 0 : vol) * 100)).padStart(2, "0")}
{/* Queue */}
{QUEUE.map((t, i) => (
))}
);
}
const pl = {
wrap: {
marginTop: 8,
padding: 20,
position: "relative",
},
head: {
display: "flex", justifyContent: "space-between", alignItems: "center",
marginBottom: 14,
},
main: {
display: "grid", gridTemplateColumns: "90px 1fr", gap: 18, alignItems: "flex-start",
},
disc: {
width: 90, height: 90, position: "relative",
},
info: { minWidth: 0 },
eq: {
marginTop: 12,
display: "flex", alignItems: "flex-end", gap: 2,
height: 32,
borderBottom: "1px solid var(--panel-stroke)",
paddingBottom: 2,
},
track: {
marginTop: 12,
height: 4,
background: "oklch(0.2 0.06 300)",
position: "relative",
transition: "opacity .2s",
},
trackFill: {
height: "100%",
background: "linear-gradient(90deg, var(--magenta), var(--cyan))",
boxShadow: "0 0 8px var(--magenta)",
transition: "width .1s linear",
},
trackDot: {
position: "absolute", top: -3, width: 10, height: 10, borderRadius: "50%",
background: "var(--magenta)", boxShadow: "0 0 10px var(--magenta)",
transition: "left .1s linear",
},
times: {
display: "flex", justifyContent: "space-between", marginTop: 4,
},
ctrls: {
display: "flex", alignItems: "center", gap: 8, marginTop: 10,
},
btn: {
width: 32, height: 32,
border: "1px solid var(--panel-stroke)",
background: "oklch(0.12 0.04 300 / 0.6)",
color: "var(--ink)",
cursor: "pointer",
fontFamily: "Orbitron, sans-serif",
fontSize: 10,
},
play: {
width: 44, height: 32,
background: "var(--magenta)",
color: "var(--bg-0)",
border: "none",
fontSize: 12, fontWeight: 900,
},
queue: {
marginTop: 14,
borderTop: "1px solid var(--panel-stroke)",
paddingTop: 12,
display: "flex", flexDirection: "column", gap: 2,
},
qItem: {
display: "flex", alignItems: "center", gap: 10,
padding: "6px 10px",
border: "1px solid transparent",
background: "transparent",
fontFamily: "inherit",
fontSize: 12,
textAlign: "left",
cursor: "pointer",
transition: "all .15s",
},
volWrap: {
display: "flex", alignItems: "center", gap: 8,
padding: "0 4px 0 0",
},
volSlider: {
width: 90,
height: 4,
appearance: "none",
WebkitAppearance: "none",
background: "oklch(0.2 0.06 300)",
outline: "none",
cursor: "pointer",
accentColor: "var(--magenta)",
},
};
// Equalizer keyframes
const _plStyle = document.createElement("style");
_plStyle.textContent = `@keyframes eqbar { from { transform: scaleY(0.3); } to { transform: scaleY(1); } }`;
document.head.appendChild(_plStyle);
window.HeroPlayer = HeroPlayer;