Improve web UI
This commit is contained in:
@@ -47,11 +47,23 @@ body {
|
||||
flex-shrink: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
.header-logo {
|
||||
display: flex; align-items: center; gap: 0.5rem;
|
||||
font-size: 1.15rem; font-weight: 700; color: var(--accent);
|
||||
}
|
||||
.header-logo svg { width: 22px; height: 22px; }
|
||||
.header-logo { display: flex; align-items: center; gap: 0.75rem; font-weight: 700; font-size: 1.1rem; }
|
||||
.header-logo svg { width: 22px; height: 22px; color: var(--primary); }
|
||||
.header-version {
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-muted);
|
||||
text-decoration: none;
|
||||
background: rgba(255,255,255,0.05);
|
||||
padding: 0.1rem 0.4rem;
|
||||
border-radius: 4px;
|
||||
margin-left: 0.25rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.header-version:hover {
|
||||
color: var(--primary);
|
||||
background: rgba(124, 106, 247, 0.15);
|
||||
}
|
||||
.btn-logout {
|
||||
font-size: 0.78rem; color: var(--text-muted); background: none;
|
||||
border: 1px solid var(--border); border-radius: 6px;
|
||||
@@ -339,6 +351,37 @@ body {
|
||||
}
|
||||
.toast.show { opacity: 1; transform: translateY(0); }
|
||||
|
||||
/* ─── Cover Preview ─── */
|
||||
.cover-preview-overlay {
|
||||
position: fixed;
|
||||
bottom: 80px;
|
||||
left: 1.5rem;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.7);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
transform: translateY(10px) scale(0.95);
|
||||
pointer-events: none;
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
z-index: 50;
|
||||
}
|
||||
.cover-preview-overlay.show {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
.cover-preview-overlay img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
/* ─── Responsive (Mobile) ─── */
|
||||
@media (max-width: 768px) {
|
||||
.btn-menu { display: inline-block; }
|
||||
@@ -375,6 +418,7 @@ body {
|
||||
<path d="M12 18V6l9-3v3"/>
|
||||
</svg>
|
||||
Furumi Player
|
||||
<a href="https://gt.hexor.cy/ab/furumi-ng" class="header-version" target="_blank">v<!-- VERSION_PLACEHOLDER --></a>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 1rem;">
|
||||
<span style="font-size: 0.8rem; color: var(--text-dim);">
|
||||
@@ -421,7 +465,8 @@ body {
|
||||
<div class="player-bar">
|
||||
<!-- Now playing -->
|
||||
<div class="np-info">
|
||||
<div class="np-cover" id="npCover">🎵</div>
|
||||
<div class="np-cover" id="npCover" onmouseenter="showCoverPreview(true)" onmouseleave="showCoverPreview(false)">🎵</div>
|
||||
<div class="cover-preview-overlay" id="coverPreview"></div>
|
||||
<div class="np-text">
|
||||
<div class="np-title" id="npTitle">Nothing playing</div>
|
||||
<div class="np-artist" id="npArtist">—</div>
|
||||
@@ -673,14 +718,49 @@ function playIndex(i) {
|
||||
}
|
||||
|
||||
function updateNowPlaying(track) {
|
||||
document.getElementById('npTitle').textContent = track.meta?.title || displayName(track.name);
|
||||
document.getElementById('npArtist').textContent = track.meta?.artist || '—';
|
||||
if (track.meta?.cover_base64) {
|
||||
document.getElementById('npCover').innerHTML = `<img src="${track.meta.cover_base64}" alt="cover">`;
|
||||
const title = track.meta?.title || displayName(track.name);
|
||||
const artist = track.meta?.artist || '—';
|
||||
const coverBase64 = track.meta?.cover_base64;
|
||||
|
||||
document.getElementById('npTitle').textContent = title;
|
||||
document.getElementById('npArtist').textContent = artist;
|
||||
if (coverBase64) {
|
||||
document.getElementById('npCover').innerHTML = `<img src="${coverBase64}" alt="cover">`;
|
||||
} else {
|
||||
document.getElementById('npCover').textContent = '🎵';
|
||||
}
|
||||
document.title = (track.meta?.title || displayName(track.name)) + ' — Furumi Player';
|
||||
document.title = title + ' — Furumi Player';
|
||||
|
||||
// Sync URL
|
||||
history.replaceState(null, "", "?t=" + encodeURIComponent(track.path));
|
||||
|
||||
// Update OS Media Session (Hardware keys & OS Player UI)
|
||||
if ('mediaSession' in navigator) {
|
||||
navigator.mediaSession.metadata = new MediaMetadata({
|
||||
title: title,
|
||||
artist: artist !== '—' ? artist : '',
|
||||
album: track.meta?.album || 'Furumi',
|
||||
artwork: coverBase64 ? [{ src: coverBase64, sizes: '512x512' }] : []
|
||||
});
|
||||
}
|
||||
|
||||
// Update Preview
|
||||
const preview = document.getElementById('coverPreview');
|
||||
if (coverBase64) {
|
||||
preview.innerHTML = `<img src="${coverBase64}" alt="cover full">`;
|
||||
} else {
|
||||
preview.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
function showCoverPreview(show) {
|
||||
const preview = document.getElementById('coverPreview');
|
||||
const hasImage = preview.querySelector('img');
|
||||
if (show && hasImage) {
|
||||
preview.classList.add('show');
|
||||
} else {
|
||||
preview.classList.remove('show');
|
||||
}
|
||||
}
|
||||
|
||||
async function loadMeta(track) {
|
||||
@@ -961,6 +1041,29 @@ function logout() {
|
||||
}
|
||||
|
||||
// ─── Init ─────────────────────────────────────────────────────────────────────
|
||||
if ('mediaSession' in navigator) {
|
||||
navigator.mediaSession.setActionHandler('play', togglePlay);
|
||||
navigator.mediaSession.setActionHandler('pause', togglePlay);
|
||||
navigator.mediaSession.setActionHandler('previoustrack', prevTrack);
|
||||
navigator.mediaSession.setActionHandler('nexttrack', nextTrack);
|
||||
navigator.mediaSession.setActionHandler('seekto', (details) => {
|
||||
if (details.fastSeek && 'fastSeek' in audio) {
|
||||
audio.fastSeek(details.seekTime);
|
||||
return;
|
||||
}
|
||||
audio.currentTime = details.seekTime;
|
||||
});
|
||||
}
|
||||
|
||||
// Deep linking on load
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const trackPath = urlParams.get('t');
|
||||
if (trackPath) {
|
||||
const parts = trackPath.split('/');
|
||||
const name = parts[parts.length - 1];
|
||||
addToQueue(trackPath, name, true);
|
||||
}
|
||||
|
||||
navigate('');
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user