Files
boWave/dashboard/src/components/audio/NowPlaying.jsx
denshooter e236e1f673 Phase 4: Glassmorphic design refresh across all components
- Enhanced all major components with glassmorphic styling:
  * EngineData gauges: frosted glass panels with animations
  * InstrumentPanel: glassmorphic waypoint boxes and data tables
  * NowPlaying: glassmorphic album art container and controls
  * All panels: smooth fade-in animations on mount

- Updated visual elements:
  * Consistent use of backdrop-filter blur effect
  * Semi-transparent borders with 0.1-0.2 opacity
  * Smooth animations (slideInUp, slideInDown, fadeIn)
  * Better font weights and hierarchy
  * Improved contrast and readability

- Color scheme refinements:
  * Highlight backgrounds use RGBA with proper opacity
  * Better use of accent colors for emphasis
  * Consistent border styling with transparency
  * Support for light/dark mode throughout

- Animation improvements:
  * All cards and panels animate on mount
  * Tab transitions are smooth and snappy
  * Hover effects with scale and shadow changes
  * Cubic-bezier timing functions for natural feel

- Build optimization:
  * Still 70 modules, same bundle size
  * CSS is well-organized and maintainable
  * No breaking changes to component APIs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 15:03:35 +01:00

98 lines
4.0 KiB
JavaScript

import { usePlayer } from '../../hooks/usePlayer.js'
function formatTime(ms) {
if (!ms) return '0:00'
const s = Math.floor(ms / 1000)
return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, '0')}`
}
export default function NowPlaying({ compact = false }) {
const { currentTrack, state, position, play, pause, next, previous, connected } = usePlayer()
if (!connected) {
return (
<div style={styles.container}>
<span style={{ color: 'var(--muted)', fontSize: 13 }}>Audio not connected</span>
</div>
)
}
const progress = currentTrack?.duration
? Math.min(100, (position / currentTrack.duration) * 100)
: 0
return (
<div style={{ ...styles.container, ...(compact ? styles.compact : {}) }}>
{/* Cover placeholder */}
<div style={styles.cover}>
{currentTrack?.coverUrl
? <img src={currentTrack.coverUrl} alt="cover" style={styles.coverImg} />
: <span style={styles.coverIcon}></span>}
</div>
<div style={styles.info}>
<div style={styles.title}>{currentTrack?.title || 'Nothing playing'}</div>
<div style={styles.artist}>{currentTrack?.artist || ''}</div>
{!compact && <div style={styles.album}>{currentTrack?.album || ''}</div>}
{/* Progress bar */}
{currentTrack && (
<div style={styles.progressRow}>
<span style={styles.timeText}>{formatTime(position)}</span>
<div style={styles.progressBg}>
<div style={{ ...styles.progressFill, width: `${progress}%` }} />
</div>
<span style={styles.timeText}>{formatTime(currentTrack.duration)}</span>
</div>
)}
{/* Controls */}
<div style={styles.controls}>
<button style={styles.btn} onClick={previous}></button>
<button style={{ ...styles.btn, ...styles.playBtn }}
onClick={state === 'playing' ? pause : play}>
{state === 'playing' ? '⏸' : '▶'}
</button>
<button style={styles.btn} onClick={next}></button>
</div>
</div>
</div>
)
}
const styles = {
container: {
display: 'flex', gap: 16, padding: 16,
background: 'var(--glass-bg)',
backdropFilter: 'blur(var(--glass-blur))',
WebkitBackdropFilter: 'blur(var(--glass-blur))',
borderRadius: 'var(--radius-lg)',
border: '1px solid rgba(255, 255, 255, 0.1)',
alignItems: 'center',
animation: 'slideInDown 0.3s ease-out',
transition: 'all 0.2s',
},
compact: { padding: '10px 14px' },
cover: {
width: 64, height: 64, flexShrink: 0,
background: 'rgba(255, 255, 255, 0.08)',
borderRadius: 'var(--radius)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
overflow: 'hidden',
border: '1px solid rgba(255, 255, 255, 0.1)',
},
coverImg: { width: '100%', height: '100%', objectFit: 'cover' },
coverIcon: { fontSize: 28, color: 'var(--muted)' },
info: { flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 6 },
title: { fontWeight: 600, fontSize: 14, color: 'var(--text)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' },
artist: { fontSize: 12, color: 'var(--muted)', fontWeight: 500 },
album: { fontSize: 11, color: 'var(--muted)', opacity: 0.7 },
progressRow: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 4 },
progressBg: { flex: 1, height: 3, background: 'rgba(255, 255, 255, 0.1)', borderRadius: 2, overflow: 'hidden' },
progressFill: { height: '100%', background: 'var(--accent)', borderRadius: 2, transition: 'width 1s linear' },
timeText: { fontSize: 10, color: 'var(--muted)', fontFamily: 'var(--font-mono)', minWidth: 30, fontWeight: 500 },
controls: { display: 'flex', gap: 6, marginTop: 4 },
btn: { width: 36, height: 36, fontSize: 14, background: 'rgba(255, 255, 255, 0.08)', color: 'var(--text)', minWidth: 36, border: '1px solid rgba(255, 255, 255, 0.1)', borderRadius: 'var(--radius)', transition: 'all 0.2s' },
playBtn: { background: 'var(--accent)', color: 'white', fontWeight: 700, border: 'none' },
}