- Neue Farbpalette: #0a0d0b Hintergrund, #111511 Surface, #1ed760 Akzent - Inter-Font via Google Fonts (400/500/600) - CSS Grid Layout: 240px Sidebar + 1fr Content + 88px Player Bar - Sidebar: neue Nav-Items mit Padding, Uppercase-Labels, grünes Logo-Icon - CoverImage: 2-Buchstaben-Kürzel mit 6 Cover-Placeholder-Farben - BookCard: Play-Overlay (34px), 3px Progress-Bar am Kartenrand - MiniPlayer: Player Bar mit 3-Spalten-Grid, 4px Progress-Track - Alle Pages und Komponenten auf neue Tokenfarben aktualisiert - BookDetail: Fehlermeldung wenn kein Match gefunden Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
83 lines
2.9 KiB
TypeScript
83 lines
2.9 KiB
TypeScript
import React from 'react'
|
|
import { Play, Pause, X } from 'lucide-react'
|
|
import { usePlayerStore } from '../../store/playerStore'
|
|
import CoverImage from '../common/CoverImage'
|
|
import { coverUrl } from '../../api/items'
|
|
|
|
export default function MiniPlayer() {
|
|
const { item, currentTime, duration, isPlaying, setPlaying, stop, setExpanded } = usePlayerStore()
|
|
if (!item) return null
|
|
|
|
const meta = item.media?.metadata || {}
|
|
const title = meta.title || item.relPath || ''
|
|
const author = meta.authors?.[0]?.name || ''
|
|
const pct = duration > 0 ? (currentTime / duration) * 100 : 0
|
|
|
|
return (
|
|
<div className="h-full flex flex-col bg-surface border-t border-divider">
|
|
{/* Progress track */}
|
|
<div className="bg-muted2 relative" style={{ height: '4px', borderRadius: '2px' }}>
|
|
<div
|
|
className="h-full bg-ink"
|
|
style={{ width: `${pct}%`, borderRadius: '2px', transition: 'width 0.5s linear' }}
|
|
/>
|
|
</div>
|
|
|
|
{/* 3-column grid */}
|
|
<div
|
|
className="flex-1 grid items-center"
|
|
style={{ gridTemplateColumns: '1fr auto 1fr', padding: '0 24px', gap: '16px' }}
|
|
>
|
|
{/* Left: cover + title */}
|
|
<div
|
|
className="flex items-center gap-3 cursor-pointer min-w-0"
|
|
onClick={() => setExpanded(true)}
|
|
>
|
|
<div className="w-10 h-10 flex-shrink-0 overflow-hidden" style={{ borderRadius: '6px' }}>
|
|
<CoverImage
|
|
src={item.media?.coverPath ? coverUrl(item.id) : null}
|
|
alt={title}
|
|
className="w-full h-full"
|
|
/>
|
|
</div>
|
|
<div className="min-w-0">
|
|
<p className="text-ink truncate" style={{ fontSize: '13px', fontWeight: 500 }}>{title}</p>
|
|
{author && <p className="text-muted truncate" style={{ fontSize: '12px' }}>{author}</p>}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Center: play/pause */}
|
|
<button
|
|
className="flex items-center justify-center flex-shrink-0"
|
|
style={{
|
|
width: 40,
|
|
height: 40,
|
|
borderRadius: '50%',
|
|
background: '#e4ede5',
|
|
color: '#000',
|
|
transition: 'transform 0.1s',
|
|
}}
|
|
onMouseEnter={(e) => { e.currentTarget.style.transform = 'scale(1.06)'; e.currentTarget.style.background = '#fff' }}
|
|
onMouseLeave={(e) => { e.currentTarget.style.transform = 'scale(1)'; e.currentTarget.style.background = '#e4ede5' }}
|
|
onClick={() => setPlaying(!isPlaying)}
|
|
>
|
|
{isPlaying
|
|
? <Pause size={18} fill="currentColor" />
|
|
: <Play size={18} fill="currentColor" />
|
|
}
|
|
</button>
|
|
|
|
{/* Right: close */}
|
|
<div className="flex items-center justify-end">
|
|
<button
|
|
className="text-muted hover:text-ink transition-colors p-1.5"
|
|
onClick={stop}
|
|
>
|
|
<X size={18} />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|