Refine fullscreen player to look more like apple music
This commit is contained in:
parent
a812198a07
commit
0b1bb3cd11
5 changed files with 1003 additions and 158 deletions
156
index.html
156
index.html
|
|
@ -144,77 +144,99 @@
|
|||
<button id="toggle-fullscreen-lyrics-btn" class="fullscreen-lyrics-toggle" title="Toggle Lyrics">
|
||||
<use svg="!lucide/mic-vocal.svg" size="24" />
|
||||
</button>
|
||||
<button id="close-fullscreen-cover-btn" title="Close"><use svg="!lucide/x.svg" size="24" /></button>
|
||||
<div class="fullscreen-main-view">
|
||||
<img
|
||||
id="fullscreen-cover-image"
|
||||
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
|
||||
alt="Album Cover"
|
||||
/>
|
||||
<div class="fullscreen-top-actions">
|
||||
<button id="fs-visualizer-btn" class="fs-visualizer-btn" title="Disable Visualizer">
|
||||
<use svg="!lucide/audio-lines.svg" size="20" />
|
||||
</button>
|
||||
<button id="close-fullscreen-cover-btn" title="Close"><use svg="!lucide/x.svg" size="24" /></button>
|
||||
</div>
|
||||
<div class="fullscreen-shell">
|
||||
<div class="fullscreen-main-view">
|
||||
<div class="fullscreen-media-column">
|
||||
<div class="fullscreen-artwork-card">
|
||||
<img
|
||||
id="fullscreen-cover-image"
|
||||
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
|
||||
alt="Album Cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="fullscreen-track-info">
|
||||
<h2 id="fullscreen-track-title"></h2>
|
||||
<h3 id="fullscreen-track-artist"></h3>
|
||||
<div class="fullscreen-actions">
|
||||
<button id="fs-like-btn" class="btn-icon like-btn" title="Like">
|
||||
<use svg="!lucide/heart.svg" class="heart-icon" size="24" />
|
||||
</button>
|
||||
<button id="fs-add-playlist-btn" class="btn-icon" title="Add to Playlist">
|
||||
<use svg="!lucide/square-pen.svg" size="24" />
|
||||
</button>
|
||||
<button id="fs-download-btn" class="btn-icon" title="Download">
|
||||
<use svg="!lucide/download.svg" size="24" />
|
||||
</button>
|
||||
<button id="fs-cast-btn" class="btn-icon" title="Cast">
|
||||
<use svg="!lucide/cast.svg" size="24" />
|
||||
</button>
|
||||
<button id="fs-queue-btn" class="btn-icon" title="Queue">
|
||||
<use svg="!lucide/list.svg" size="24" />
|
||||
</button>
|
||||
</div>
|
||||
<div id="fullscreen-next-track" style="display: none">
|
||||
<span class="label">Up Next: </span>
|
||||
<span class="value"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fullscreen-controls">
|
||||
<div class="fullscreen-progress-container">
|
||||
<span id="fs-current-time">0:00</span>
|
||||
<div id="fs-progress-bar" class="progress-bar">
|
||||
<div id="fs-progress-fill" class="progress-fill"></div>
|
||||
<div class="fullscreen-track-info">
|
||||
<div class="fullscreen-track-text">
|
||||
<h2 id="fullscreen-track-title"></h2>
|
||||
<h3 id="fullscreen-track-artist"></h3>
|
||||
</div>
|
||||
<div class="fullscreen-actions">
|
||||
<button id="fs-like-btn" class="btn-icon like-btn" title="Like">
|
||||
<use svg="!lucide/heart.svg" class="heart-icon" size="24" />
|
||||
</button>
|
||||
<button id="fs-add-playlist-btn" class="btn-icon" title="Add to Playlist">
|
||||
<use svg="!lucide/square-pen.svg" size="24" />
|
||||
</button>
|
||||
<button id="fs-download-btn" class="btn-icon" title="Download">
|
||||
<use svg="!lucide/download.svg" size="24" />
|
||||
</button>
|
||||
<button id="fs-cast-btn" class="btn-icon" title="Cast">
|
||||
<use svg="!lucide/cast.svg" size="24" />
|
||||
</button>
|
||||
<button id="fs-queue-btn" class="btn-icon" title="Queue">
|
||||
<use svg="!lucide/list.svg" size="24" />
|
||||
</button>
|
||||
</div>
|
||||
<div id="fullscreen-next-track" style="display: none">
|
||||
<span class="label">Up Next</span>
|
||||
<span class="value"></span>
|
||||
</div>
|
||||
</div>
|
||||
<span id="fs-total-duration">0:00</span>
|
||||
</div>
|
||||
<div class="fullscreen-buttons">
|
||||
<button id="fs-shuffle-btn" title="Shuffle">
|
||||
<use svg="!lucide/shuffle.svg" size="24" />
|
||||
</button>
|
||||
<button id="fs-prev-btn" title="Previous">
|
||||
<use svg="!lucide/arrow-left-to-line.svg" size="24" />
|
||||
</button>
|
||||
<button id="fs-play-pause-btn" class="play-pause-btn" title="Play">
|
||||
<use svg="./images/play-large.svg" size="32" />
|
||||
</button>
|
||||
<button id="fs-next-btn" title="Next">
|
||||
<use svg="!lucide/arrow-right-to-line.svg" size="24" />
|
||||
</button>
|
||||
<button id="fs-repeat-btn" title="Repeat">
|
||||
<use svg="!lucide/repeat.svg" size="24" />
|
||||
</button>
|
||||
<button id="fs-quality-btn" class="fs-quality-btn" title="Quality" style="display: none">
|
||||
<use svg="!lucide/pencil-line.svg" size="20" />
|
||||
<span class="fs-quality-label">Auto</span>
|
||||
</button>
|
||||
<div id="fs-quality-menu" class="fs-quality-menu" style="display: none"></div>
|
||||
</div>
|
||||
<div class="fullscreen-volume-container">
|
||||
<button id="fs-volume-btn" class="fs-volume-btn" title="Mute">
|
||||
<use svg="!lucide/volume-1.svg" size="24" />
|
||||
</button>
|
||||
<div id="fs-volume-bar" class="fs-volume-bar">
|
||||
<div id="fs-volume-fill" class="fs-volume-fill"></div>
|
||||
|
||||
<div class="fullscreen-controls">
|
||||
<div class="fullscreen-progress-container">
|
||||
<span id="fs-current-time">0:00</span>
|
||||
<div id="fs-progress-bar" class="progress-bar">
|
||||
<div id="fs-progress-fill" class="progress-fill"></div>
|
||||
</div>
|
||||
<span id="fs-total-duration">0:00</span>
|
||||
</div>
|
||||
<div class="fullscreen-buttons">
|
||||
<button id="fs-shuffle-btn" title="Shuffle">
|
||||
<use svg="!lucide/shuffle.svg" size="24" />
|
||||
</button>
|
||||
<button id="fs-prev-btn" title="Previous">
|
||||
<use svg="!lucide/arrow-left-to-line.svg" size="24" />
|
||||
</button>
|
||||
<button id="fs-play-pause-btn" class="play-pause-btn" title="Play">
|
||||
<use svg="./images/play-large.svg" size="32" />
|
||||
</button>
|
||||
<button id="fs-next-btn" title="Next">
|
||||
<use svg="!lucide/arrow-right-to-line.svg" size="24" />
|
||||
</button>
|
||||
<button id="fs-repeat-btn" title="Repeat">
|
||||
<use svg="!lucide/repeat.svg" size="24" />
|
||||
</button>
|
||||
<button id="fs-quality-btn" class="fs-quality-btn" title="Quality" style="display: none">
|
||||
<use svg="!lucide/pencil-line.svg" size="20" />
|
||||
<span class="fs-quality-label">Auto</span>
|
||||
</button>
|
||||
<div id="fs-quality-menu" class="fs-quality-menu" style="display: none"></div>
|
||||
</div>
|
||||
<div class="fullscreen-volume-container">
|
||||
<button id="fs-volume-btn" class="fs-volume-btn" title="Mute">
|
||||
<use svg="!lucide/volume-1.svg" size="24" />
|
||||
</button>
|
||||
<div id="fs-volume-bar" class="fs-volume-bar">
|
||||
<div id="fs-volume-fill" class="fs-volume-fill"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside id="fullscreen-lyrics-pane" class="fullscreen-lyrics-pane">
|
||||
<div class="fullscreen-lyrics-shell">
|
||||
<div id="fullscreen-lyrics-content" class="fullscreen-lyrics-content">
|
||||
<div class="fullscreen-lyrics-empty">Lyrics appear here.</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ export { default as SVG_CLOCK } from '!lucide/clock.svg?svg&icon';
|
|||
export { default as SVG_CLOSE } from '!lucide/x.svg?svg&icon';
|
||||
export { default as SVG_DISC } from '!lucide/disc.svg?svg&icon';
|
||||
export { default as SVG_DOWNLOAD } from '!lucide/download.svg?svg&icon';
|
||||
export { default as SVG_EYE } from '!lucide/eye.svg?svg&icon';
|
||||
export { default as SVG_EYE_OFF } from '!lucide/eye-off.svg?svg&icon';
|
||||
export { default as SVG_EQUAL } from '!lucide/equal.svg?svg&icon';
|
||||
export { default as SVG_FACEBOOK } from '../images/facebook.svg?svg&icon';
|
||||
export { default as SVG_FOLDER_PLUS } from '!lucide/folder-plus.svg?svg&icon';
|
||||
|
|
|
|||
69
js/lyrics.js
69
js/lyrics.js
|
|
@ -964,6 +964,74 @@ themeObserver.observe(document.documentElement, {
|
|||
attributeFilter: ['data-theme', 'style'],
|
||||
});
|
||||
|
||||
function applyFullscreenLyricsShadowTweaks(amLyrics, container) {
|
||||
if (!amLyrics || container?.id !== 'fullscreen-lyrics-content') return;
|
||||
|
||||
const injectStyle = () => {
|
||||
const root = amLyrics.shadowRoot;
|
||||
if (!root) return false;
|
||||
|
||||
let styleEl = root.getElementById('monochrome-fullscreen-lyrics-tweaks');
|
||||
if (!styleEl) {
|
||||
styleEl = document.createElement('style');
|
||||
styleEl.id = 'monochrome-fullscreen-lyrics-tweaks';
|
||||
root.appendChild(styleEl);
|
||||
}
|
||||
|
||||
styleEl.textContent = `
|
||||
.lyrics-container {
|
||||
scrollbar-width: none !important;
|
||||
-ms-overflow-style: none !important;
|
||||
}
|
||||
|
||||
.lyrics-container::-webkit-scrollbar {
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
display: none !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.lyrics-line {
|
||||
transition:
|
||||
opacity 0.42s ease,
|
||||
transform 0.55s cubic-bezier(0.22, 1, 0.36, 1) var(--lyrics-line-delay, 0ms),
|
||||
filter 0.48s cubic-bezier(0.22, 1, 0.36, 1) !important;
|
||||
}
|
||||
|
||||
.lyrics-line-container {
|
||||
transition:
|
||||
transform 0.72s cubic-bezier(0.22, 1, 0.36, 1),
|
||||
background-color 0.3s ease,
|
||||
color 0.3s ease !important;
|
||||
}
|
||||
|
||||
.lyrics-line.active .lyrics-line-container,
|
||||
.lyrics-line.pre-active .lyrics-line-container {
|
||||
transition:
|
||||
transform 0.56s cubic-bezier(0.22, 1, 0.36, 1),
|
||||
background-color 0.22s ease,
|
||||
color 0.22s ease !important;
|
||||
}
|
||||
`;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if (injectStyle()) return;
|
||||
|
||||
let attempts = 0;
|
||||
const maxAttempts = 24;
|
||||
const tryInject = () => {
|
||||
if (injectStyle()) return;
|
||||
attempts += 1;
|
||||
if (attempts < maxAttempts) {
|
||||
requestAnimationFrame(tryInject);
|
||||
}
|
||||
};
|
||||
|
||||
requestAnimationFrame(tryInject);
|
||||
}
|
||||
|
||||
async function renderLyricsComponent(container, track, audioPlayer, lyricsManager) {
|
||||
container.innerHTML = '<div class="lyrics-loading">Loading lyrics...</div>';
|
||||
|
||||
|
|
@ -1006,6 +1074,7 @@ async function renderLyricsComponent(container, track, audioPlayer, lyricsManage
|
|||
amLyrics.style.width = '100%';
|
||||
|
||||
container.appendChild(amLyrics);
|
||||
applyFullscreenLyricsShadowTweaks(amLyrics, container);
|
||||
|
||||
lyricsManager.setupLyricsObserver(amLyrics);
|
||||
|
||||
|
|
|
|||
313
js/ui.js
313
js/ui.js
|
|
@ -15,7 +15,7 @@ import {
|
|||
escapeHtml,
|
||||
getShareUrl,
|
||||
} from './utils.js';
|
||||
import { openLyricsPanel } from './lyrics.js';
|
||||
import { openLyricsPanel, renderLyricsInFullscreen, clearFullscreenLyricsSync } from './lyrics.js';
|
||||
import {
|
||||
recentActivityManager,
|
||||
backgroundSettings,
|
||||
|
|
@ -27,9 +27,6 @@ import {
|
|||
contentBlockingSettings,
|
||||
settingsUiState,
|
||||
fullscreenCoverNoRoundSettings,
|
||||
fullscreenCoverVanillaTiltSettings,
|
||||
fullscreenCoverTiltDistanceSettings,
|
||||
fullscreenCoverTiltSpeedSettings,
|
||||
} from './storage.js';
|
||||
import { db } from './db.js';
|
||||
import { getVibrantColorFromImage } from './vibrant-color.js';
|
||||
|
|
@ -61,6 +58,8 @@ import {
|
|||
SVG_HEART,
|
||||
SVG_VOLUME,
|
||||
SVG_MUTE,
|
||||
SVG_EYE,
|
||||
SVG_EYE_OFF,
|
||||
SVG_HEART_FILLED,
|
||||
SVG_CLOSE,
|
||||
SVG_SORT,
|
||||
|
|
@ -89,6 +88,11 @@ import {
|
|||
SVG_CHECKBOX,
|
||||
} from './icons.js';
|
||||
|
||||
const setFullscreenUIToggleIcon = (button, visualizerOnlyMode) => {
|
||||
if (!button) return;
|
||||
button.innerHTML = visualizerOnlyMode ? SVG_EYE(24) : SVG_EYE_OFF(24);
|
||||
};
|
||||
|
||||
function sortTracks(tracks, sortType) {
|
||||
if (sortType === 'custom') return [...tracks];
|
||||
const sorted = [...tracks];
|
||||
|
|
@ -151,6 +155,8 @@ export class UIRenderer {
|
|||
this.renderLock = false;
|
||||
this.lastRecommendedTracks = [];
|
||||
this.currentArtistId = null;
|
||||
this.fullscreenLyricsVisible = true;
|
||||
this.fullscreenPlaybackStateCleanup = null;
|
||||
|
||||
// Listen for dynamic color reset events
|
||||
window.addEventListener('reset-dynamic-color', () => {
|
||||
|
|
@ -177,20 +183,8 @@ export class UIRenderer {
|
|||
} else {
|
||||
overlay.classList.remove('fullscreen-cover-no-round');
|
||||
}
|
||||
if (coverImage) {
|
||||
if (fullscreenCoverVanillaTiltSettings.isEnabled() && window.VanillaTilt) {
|
||||
if (coverImage.vanillaTilt) {
|
||||
coverImage.vanillaTilt.destroy();
|
||||
}
|
||||
window.VanillaTilt.init(coverImage, {
|
||||
max: fullscreenCoverTiltDistanceSettings.getValue(),
|
||||
speed: fullscreenCoverTiltSpeedSettings.getValue(),
|
||||
glare: true,
|
||||
'max-glare': 0.3,
|
||||
});
|
||||
} else if (coverImage.vanillaTilt) {
|
||||
coverImage.vanillaTilt.destroy();
|
||||
}
|
||||
if (coverImage?.vanillaTilt) {
|
||||
coverImage.vanillaTilt.destroy();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -1117,6 +1111,23 @@ export class UIRenderer {
|
|||
root.style.removeProperty('--track-hover-bg');
|
||||
}
|
||||
|
||||
getFullscreenQualityBadgeHTML(track) {
|
||||
const nowPlayingTitle = document.querySelector('.now-playing-bar .title');
|
||||
if (nowPlayingTitle && this.player?.currentTrack?.id === track?.id) {
|
||||
const badges = Array.from(nowPlayingTitle.querySelectorAll('.shaka-quality-badge, .quality-badge'));
|
||||
const liveBadge = badges.find((badge) => getComputedStyle(badge).display !== 'none') || badges[0];
|
||||
if (liveBadge) {
|
||||
const badgeClone = liveBadge.cloneNode(true);
|
||||
if (badgeClone instanceof HTMLElement) {
|
||||
badgeClone.style.removeProperty('display');
|
||||
}
|
||||
return badgeClone.outerHTML;
|
||||
}
|
||||
}
|
||||
|
||||
return createQualityBadgeHTML(track);
|
||||
}
|
||||
|
||||
async updateFullscreenMetadata(track, nextTrack) {
|
||||
if (!track) return;
|
||||
const overlay = document.getElementById('fullscreen-cover-overlay');
|
||||
|
|
@ -1214,7 +1225,7 @@ export class UIRenderer {
|
|||
await this.extractAndApplyColor(this.api.getCoverUrl(track.album?.cover, '80'));
|
||||
}
|
||||
|
||||
const qualityBadge = createQualityBadgeHTML(track);
|
||||
const qualityBadge = this.getFullscreenQualityBadgeHTML(track);
|
||||
title.innerHTML = `${escapeHtml(track.title)} ${qualityBadge}`;
|
||||
artist.textContent = getTrackArtists(track);
|
||||
|
||||
|
|
@ -1228,11 +1239,14 @@ export class UIRenderer {
|
|||
|
||||
async showFullscreenCover(track, nextTrack, lyricsManager, activeElement) {
|
||||
if (!track) return;
|
||||
this.fullscreenVisualizerSuppressed = true;
|
||||
if (window.location.hash !== '#fullscreen') {
|
||||
window.history.pushState({ fullscreen: true }, '', '#fullscreen');
|
||||
}
|
||||
const overlay = document.getElementById('fullscreen-cover-overlay');
|
||||
const nextTrackEl = document.getElementById('fullscreen-next-track');
|
||||
const lyricsPane = document.getElementById('fullscreen-lyrics-pane');
|
||||
const lyricsContent = document.getElementById('fullscreen-lyrics-content');
|
||||
const lyricsToggleBtn = document.getElementById('toggle-fullscreen-lyrics-btn');
|
||||
|
||||
await this.updateFullscreenMetadata(track, nextTrack);
|
||||
|
|
@ -1245,27 +1259,33 @@ export class UIRenderer {
|
|||
nextTrackEl.classList.remove('animate-in');
|
||||
}
|
||||
|
||||
if (lyricsManager && activeElement) {
|
||||
lyricsToggleBtn.style.display = 'flex';
|
||||
lyricsToggleBtn.classList.remove('active');
|
||||
|
||||
const toggleLyrics = () => {
|
||||
openLyricsPanel(track, activeElement, lyricsManager);
|
||||
lyricsToggleBtn.classList.toggle('active');
|
||||
};
|
||||
|
||||
const newToggleBtn = lyricsToggleBtn.cloneNode(true);
|
||||
lyricsToggleBtn.parentNode.replaceChild(newToggleBtn, lyricsToggleBtn);
|
||||
newToggleBtn.addEventListener('click', toggleLyrics);
|
||||
const canRenderLyrics = Boolean(lyricsManager && activeElement && lyricsPane && lyricsContent && track.type !== 'video');
|
||||
if (canRenderLyrics) {
|
||||
lyricsToggleBtn.style.display = 'none';
|
||||
overlay.classList.remove('lyrics-unavailable');
|
||||
clearFullscreenLyricsSync(lyricsContent);
|
||||
await renderLyricsInFullscreen(track, activeElement, lyricsManager, lyricsContent);
|
||||
} else {
|
||||
lyricsToggleBtn.style.display = 'none';
|
||||
overlay.classList.add('lyrics-unavailable');
|
||||
if (lyricsContent) {
|
||||
clearFullscreenLyricsSync(lyricsContent);
|
||||
lyricsContent.innerHTML = '<div class="fullscreen-lyrics-empty">Lyrics are not available for this track.</div>';
|
||||
}
|
||||
}
|
||||
|
||||
const playerBar = document.querySelector('.now-playing-bar');
|
||||
if (playerBar) playerBar.style.display = 'none';
|
||||
if (sidePanelManager.isActive('lyrics') || sidePanelManager.isActive('queue')) {
|
||||
sidePanelManager.close();
|
||||
}
|
||||
const mainContent = document.querySelector('.main-content');
|
||||
if (mainContent instanceof HTMLElement) {
|
||||
this.fullscreenMainContentOverflow = mainContent.style.overflowY;
|
||||
mainContent.style.overflowY = 'hidden';
|
||||
}
|
||||
|
||||
this.setupFullscreenControls();
|
||||
|
||||
overlay.style.display = 'flex';
|
||||
|
||||
if (fullscreenCoverNoRoundSettings.isEnabled()) {
|
||||
|
|
@ -1275,75 +1295,41 @@ export class UIRenderer {
|
|||
}
|
||||
|
||||
const coverImage = document.getElementById('fullscreen-cover-image');
|
||||
if (fullscreenCoverVanillaTiltSettings.isEnabled() && coverImage && window.VanillaTilt) {
|
||||
window.VanillaTilt.init(coverImage, {
|
||||
max: fullscreenCoverTiltDistanceSettings.getValue(),
|
||||
speed: fullscreenCoverTiltSpeedSettings.getValue(),
|
||||
glare: true,
|
||||
'max-glare': 0.3,
|
||||
});
|
||||
if (coverImage?.vanillaTilt) {
|
||||
coverImage.vanillaTilt.destroy();
|
||||
}
|
||||
|
||||
const startVisualizer = async () => {
|
||||
if (!visualizerSettings.isEnabled()) {
|
||||
if (this.visualizer) this.visualizer.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.visualizer && activeElement) {
|
||||
const canvas = document.getElementById('visualizer-canvas');
|
||||
if (canvas) {
|
||||
this.visualizer = new Visualizer(canvas, activeElement);
|
||||
await this.visualizer.initPresets();
|
||||
}
|
||||
}
|
||||
if (this.visualizer) {
|
||||
await this.visualizer.start();
|
||||
}
|
||||
|
||||
// Add visualizer-active class for enhanced drop shadow
|
||||
overlay.classList.add('visualizer-active');
|
||||
};
|
||||
|
||||
// Setup UI toggle button
|
||||
this.setupUIToggleButton(overlay);
|
||||
|
||||
if (localStorage.getItem('epilepsy-warning-dismissed') === 'true') {
|
||||
await startVisualizer();
|
||||
} else {
|
||||
const modal = document.getElementById('epilepsy-warning-modal');
|
||||
if (modal) {
|
||||
modal.classList.add('active');
|
||||
|
||||
const acceptBtn = document.getElementById('epilepsy-accept-btn');
|
||||
const cancelBtn = document.getElementById('epilepsy-cancel-btn');
|
||||
|
||||
acceptBtn.onclick = async () => {
|
||||
modal.classList.remove('active');
|
||||
localStorage.setItem('epilepsy-warning-dismissed', 'true');
|
||||
await startVisualizer();
|
||||
};
|
||||
cancelBtn.onclick = () => {
|
||||
modal.classList.remove('active');
|
||||
this.closeFullscreenCover();
|
||||
};
|
||||
} else {
|
||||
await startVisualizer();
|
||||
}
|
||||
}
|
||||
this.setupControlsAutoHide(overlay);
|
||||
await this.refreshFullscreenVisualizerState(activeElement);
|
||||
}
|
||||
|
||||
closeFullscreenCover() {
|
||||
const overlay = document.getElementById('fullscreen-cover-overlay');
|
||||
const coverImage = document.getElementById('fullscreen-cover-image');
|
||||
const lyricsContent = document.getElementById('fullscreen-lyrics-content');
|
||||
if (coverImage && coverImage.vanillaTilt) {
|
||||
coverImage.vanillaTilt.destroy();
|
||||
}
|
||||
if (lyricsContent) {
|
||||
clearFullscreenLyricsSync(lyricsContent);
|
||||
lyricsContent.innerHTML = '<div class="fullscreen-lyrics-empty">Lyrics appear here.</div>';
|
||||
}
|
||||
overlay.style.display = 'none';
|
||||
overlay.classList.remove('visualizer-active', 'ui-hidden', 'fullscreen-cover-no-round');
|
||||
overlay.classList.remove('visualizer-active', 'ui-hidden', 'fullscreen-cover-no-round', 'fullscreen-paused');
|
||||
|
||||
const playerBar = document.querySelector('.now-playing-bar');
|
||||
if (playerBar) playerBar.style.removeProperty('display');
|
||||
const mainContent = document.querySelector('.main-content');
|
||||
if (mainContent instanceof HTMLElement) {
|
||||
if (typeof this.fullscreenMainContentOverflow === 'string' && this.fullscreenMainContentOverflow.length > 0) {
|
||||
mainContent.style.overflowY = this.fullscreenMainContentOverflow;
|
||||
} else {
|
||||
mainContent.style.removeProperty('overflow-y');
|
||||
}
|
||||
this.fullscreenMainContentOverflow = null;
|
||||
}
|
||||
|
||||
if (this.player?.currentTrack?.type === 'video') {
|
||||
const coverContainer = document.querySelector('.now-playing-bar .track-info');
|
||||
|
|
@ -1375,6 +1361,7 @@ export class UIRenderer {
|
|||
if (this.visualizer) {
|
||||
this.visualizer.stop();
|
||||
}
|
||||
this.fullscreenVisualizerSuppressed = false;
|
||||
|
||||
// Clear UI toggle button timers
|
||||
if (this.uiToggleMouseTimer) {
|
||||
|
|
@ -1383,13 +1370,115 @@ export class UIRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
async startFullscreenVisualizer(activeElement, overlay) {
|
||||
if (!activeElement) return;
|
||||
|
||||
if (!this.visualizer) {
|
||||
const canvas = document.getElementById('visualizer-canvas');
|
||||
if (canvas) {
|
||||
this.visualizer = new Visualizer(canvas, activeElement);
|
||||
await this.visualizer.initPresets();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.visualizer) {
|
||||
await this.visualizer.start();
|
||||
overlay.classList.add('visualizer-active');
|
||||
}
|
||||
}
|
||||
|
||||
async ensureVisualizerPermission(activeElement, overlay, { closeOnCancel = false } = {}) {
|
||||
if (localStorage.getItem('epilepsy-warning-dismissed') === 'true') {
|
||||
await this.startFullscreenVisualizer(activeElement, overlay);
|
||||
return true;
|
||||
}
|
||||
|
||||
const modal = document.getElementById('epilepsy-warning-modal');
|
||||
if (!modal) {
|
||||
await this.startFullscreenVisualizer(activeElement, overlay);
|
||||
return true;
|
||||
}
|
||||
|
||||
return await new Promise((resolve) => {
|
||||
modal.classList.add('active');
|
||||
|
||||
const acceptBtn = document.getElementById('epilepsy-accept-btn');
|
||||
const cancelBtn = document.getElementById('epilepsy-cancel-btn');
|
||||
|
||||
acceptBtn.onclick = async () => {
|
||||
modal.classList.remove('active');
|
||||
localStorage.setItem('epilepsy-warning-dismissed', 'true');
|
||||
await this.startFullscreenVisualizer(activeElement, overlay);
|
||||
resolve(true);
|
||||
};
|
||||
|
||||
cancelBtn.onclick = () => {
|
||||
modal.classList.remove('active');
|
||||
if (closeOnCancel) {
|
||||
this.closeFullscreenCover();
|
||||
}
|
||||
resolve(false);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async refreshFullscreenVisualizerState(activeElement, { closeOnCancel = false } = {}) {
|
||||
const overlay = document.getElementById('fullscreen-cover-overlay');
|
||||
const visualizerBtn = document.getElementById('fs-visualizer-btn');
|
||||
const toggleBtn = document.getElementById('toggle-ui-btn');
|
||||
const isVideoTrack = this.player?.currentTrack?.type === 'video';
|
||||
const enabled = visualizerSettings.isEnabled() && !isVideoTrack && !this.fullscreenVisualizerSuppressed;
|
||||
|
||||
if (!overlay) return;
|
||||
|
||||
if (visualizerBtn) {
|
||||
visualizerBtn.style.display = isVideoTrack ? 'none' : 'flex';
|
||||
visualizerBtn.classList.toggle('active', enabled);
|
||||
visualizerBtn.title = enabled ? 'Disable Visualizer' : 'Use Visualizer';
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
overlay.classList.remove('visualizer-active');
|
||||
overlay.classList.remove('ui-hidden');
|
||||
if (this.visualizer) {
|
||||
this.visualizer.stop();
|
||||
}
|
||||
if (toggleBtn) {
|
||||
toggleBtn.classList.remove('active', 'visible');
|
||||
toggleBtn.title = 'Hide UI';
|
||||
setFullscreenUIToggleIcon(toggleBtn, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const allowed = await this.ensureVisualizerPermission(activeElement, overlay, { closeOnCancel });
|
||||
if (!allowed) {
|
||||
this.fullscreenVisualizerSuppressed = true;
|
||||
overlay.classList.remove('visualizer-active');
|
||||
if (this.visualizer) {
|
||||
this.visualizer.stop();
|
||||
}
|
||||
if (visualizerBtn) {
|
||||
visualizerBtn.classList.remove('active');
|
||||
visualizerBtn.title = 'Use Visualizer';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupUIToggleButton(overlay) {
|
||||
const toggleBtn = document.getElementById('toggle-ui-btn');
|
||||
if (!toggleBtn) return;
|
||||
|
||||
const updateToggleButtonIcon = () => {
|
||||
const visualizerOnlyMode =
|
||||
overlay.classList.contains('ui-hidden') && overlay.classList.contains('visualizer-active');
|
||||
setFullscreenUIToggleIcon(toggleBtn, visualizerOnlyMode);
|
||||
};
|
||||
|
||||
let isUIHidden = overlay.classList.contains('ui-hidden');
|
||||
toggleBtn.classList.toggle('active', isUIHidden);
|
||||
toggleBtn.title = isUIHidden ? 'Show UI' : 'Hide UI';
|
||||
updateToggleButtonIcon();
|
||||
|
||||
// Show button
|
||||
const showButton = () => {
|
||||
|
|
@ -1408,12 +1497,39 @@ export class UIRenderer {
|
|||
showButton();
|
||||
}
|
||||
|
||||
const toggleUI = (e) => {
|
||||
const toggleUI = async (e) => {
|
||||
if (e) e.stopPropagation();
|
||||
if (!overlay.classList.contains('visualizer-active')) {
|
||||
const isVideoTrack = this.player?.currentTrack?.type === 'video';
|
||||
if (isVideoTrack) {
|
||||
overlay.classList.remove('ui-hidden');
|
||||
isUIHidden = false;
|
||||
toggleBtn.classList.remove('active');
|
||||
toggleBtn.title = 'Hide UI';
|
||||
updateToggleButtonIcon();
|
||||
showButton();
|
||||
return;
|
||||
}
|
||||
|
||||
this.fullscreenVisualizerSuppressed = false;
|
||||
visualizerSettings.setEnabled(true);
|
||||
await this.refreshFullscreenVisualizerState(this.player?.activeElement);
|
||||
|
||||
if (!overlay.classList.contains('visualizer-active')) {
|
||||
overlay.classList.remove('ui-hidden');
|
||||
isUIHidden = false;
|
||||
toggleBtn.classList.remove('active');
|
||||
toggleBtn.title = 'Hide UI';
|
||||
updateToggleButtonIcon();
|
||||
showButton();
|
||||
return;
|
||||
}
|
||||
}
|
||||
isUIHidden = !isUIHidden;
|
||||
overlay.classList.toggle('ui-hidden', isUIHidden);
|
||||
toggleBtn.classList.toggle('active', isUIHidden);
|
||||
toggleBtn.title = isUIHidden ? 'Show UI' : 'Hide UI';
|
||||
updateToggleButtonIcon();
|
||||
|
||||
if (isUIHidden) {
|
||||
hideButton();
|
||||
|
|
@ -1458,12 +1574,21 @@ export class UIRenderer {
|
|||
};
|
||||
}
|
||||
|
||||
setupControlsAutoHide(overlay) {
|
||||
if (this.controlsIdleCleanup) this.controlsIdleCleanup();
|
||||
overlay.classList.remove('controls-idle');
|
||||
|
||||
this.controlsIdleCleanup = () => {
|
||||
overlay.classList.remove('controls-idle');
|
||||
};
|
||||
}
|
||||
setupFullscreenControls() {
|
||||
const playBtn = document.getElementById('fs-play-pause-btn');
|
||||
const prevBtn = document.getElementById('fs-prev-btn');
|
||||
const nextBtn = document.getElementById('fs-next-btn');
|
||||
const shuffleBtn = document.getElementById('fs-shuffle-btn');
|
||||
const repeatBtn = document.getElementById('fs-repeat-btn');
|
||||
const visualizerBtn = document.getElementById('fs-visualizer-btn');
|
||||
const progressBar = document.getElementById('fs-progress-bar');
|
||||
const progressFill = document.getElementById('fs-progress-fill');
|
||||
const currentTimeEl = document.getElementById('fs-current-time');
|
||||
|
|
@ -1524,6 +1649,22 @@ export class UIRenderer {
|
|||
}
|
||||
};
|
||||
|
||||
if (visualizerBtn) {
|
||||
visualizerBtn.onclick = async () => {
|
||||
if (this.fullscreenVisualizerSuppressed) {
|
||||
this.fullscreenVisualizerSuppressed = false;
|
||||
visualizerSettings.setEnabled(true);
|
||||
} else if (visualizerSettings.isEnabled()) {
|
||||
visualizerSettings.setEnabled(false);
|
||||
this.fullscreenVisualizerSuppressed = false;
|
||||
} else {
|
||||
this.fullscreenVisualizerSuppressed = false;
|
||||
visualizerSettings.setEnabled(true);
|
||||
}
|
||||
await this.refreshFullscreenVisualizerState(this.player.activeElement);
|
||||
};
|
||||
}
|
||||
|
||||
// Progress bar with drag support
|
||||
let isFsSeeking = false;
|
||||
let wasFsPlaying = false;
|
||||
|
|
|
|||
621
styles.css
621
styles.css
|
|
@ -970,6 +970,7 @@ ul {
|
|||
display: grid;
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
min-height: 0;
|
||||
grid-template:
|
||||
'sidebar main' 1fr
|
||||
'player player' auto / 210px 1fr;
|
||||
|
|
@ -977,6 +978,7 @@ ul {
|
|||
|
||||
.sidebar {
|
||||
grid-area: sidebar;
|
||||
min-height: 0;
|
||||
background-color: var(--background);
|
||||
border-right: 1px solid var(--border);
|
||||
padding: 1.25rem;
|
||||
|
|
@ -1023,7 +1025,10 @@ ul {
|
|||
|
||||
.main-content {
|
||||
grid-area: main;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding: var(--spacing-xl);
|
||||
scroll-behavior: smooth;
|
||||
position: relative;
|
||||
|
|
@ -3934,7 +3939,26 @@ input:checked + .slider::before {
|
|||
filter: var(--cover-filter);
|
||||
z-index: -1;
|
||||
background-image: var(--bg-image);
|
||||
transition: background-image var(--transition);
|
||||
transition:
|
||||
background-image var(--transition),
|
||||
filter 0.65s ease,
|
||||
opacity 0.65s ease;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
radial-gradient(circle at 20% 22%, rgb(var(--highlight-rgb) / 0.28), transparent 36%),
|
||||
radial-gradient(circle at 82% 18%, rgb(255 255 255 / 0.09), transparent 28%),
|
||||
linear-gradient(135deg, rgb(10 13 18 / 0.48), rgb(10 13 18 / 0.2) 38%, rgb(var(--highlight-rgb) / 0.12) 100%);
|
||||
opacity: 0.36;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
transition:
|
||||
opacity 0.65s ease,
|
||||
background 0.65s ease;
|
||||
}
|
||||
|
||||
#visualizer-container {
|
||||
|
|
@ -3944,7 +3968,13 @@ input:checked + .slider::before {
|
|||
height: 100%;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s ease;
|
||||
filter: blur(14px) saturate(0.84) brightness(0.8);
|
||||
transform: scale(1.04);
|
||||
opacity: 0.82;
|
||||
transition:
|
||||
opacity 0.65s ease,
|
||||
filter 0.65s ease,
|
||||
transform 0.65s ease;
|
||||
}
|
||||
|
||||
#visualizer-canvas {
|
||||
|
|
@ -3963,6 +3993,7 @@ input:checked + .slider::before {
|
|||
height: 100%;
|
||||
position: relative;
|
||||
padding: 1rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* UI Toggle Button for Visualizer Mode - Rightmost position */
|
||||
|
|
@ -4079,11 +4110,27 @@ input:checked + .slider::before {
|
|||
/* When UI is hidden, only toggle button stays visible at right edge (when .visible class is added) */
|
||||
|
||||
#fullscreen-cover-overlay.ui-hidden .fullscreen-lyrics-toggle,
|
||||
#fullscreen-cover-overlay.ui-hidden #close-fullscreen-cover-btn {
|
||||
#fullscreen-cover-overlay.ui-hidden .fullscreen-top-actions {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay.ui-hidden::before,
|
||||
#fullscreen-cover-overlay.ui-hidden::after {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay.ui-hidden #visualizer-container {
|
||||
filter: none;
|
||||
transform: none;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
body:has(#fullscreen-cover-overlay.ui-hidden.inline-lyrics) #side-panel[data-view='lyrics'] {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
#fullscreen-cover-overlay:not(.ui-hidden) .fullscreen-main-view,
|
||||
#fullscreen-cover-overlay:not(.ui-hidden) .fullscreen-controls,
|
||||
#fullscreen-cover-overlay:not(.ui-hidden) #fullscreen-next-track {
|
||||
|
|
@ -4092,6 +4139,59 @@ input:checked + .slider::before {
|
|||
transition: opacity 0.5s ease;
|
||||
}
|
||||
|
||||
/* Auto-hide controls on idle */
|
||||
#fullscreen-cover-overlay.controls-idle .fullscreen-track-info,
|
||||
#fullscreen-cover-overlay.controls-idle .fullscreen-controls,
|
||||
#fullscreen-cover-overlay.controls-idle #fullscreen-next-track,
|
||||
#fullscreen-cover-overlay.controls-idle #toggle-ui-btn,
|
||||
#fullscreen-cover-overlay.controls-idle .fullscreen-lyrics-toggle,
|
||||
#fullscreen-cover-overlay.controls-idle .fullscreen-top-actions {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition:
|
||||
opacity 0.6s ease,
|
||||
transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay.controls-idle #fullscreen-cover-image {
|
||||
transform: translateY(4rem);
|
||||
transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay:not(.controls-idle) #fullscreen-cover-image {
|
||||
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay.controls-idle .fullscreen-controls {
|
||||
transform: translateY(1.5rem);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay.controls-idle .fullscreen-track-info {
|
||||
transform: translateY(0.5rem);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay.controls-idle #toggle-ui-btn,
|
||||
#fullscreen-cover-overlay.controls-idle .fullscreen-lyrics-toggle,
|
||||
#fullscreen-cover-overlay.controls-idle .fullscreen-top-actions {
|
||||
transform: translateY(-0.5rem);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay:not(.controls-idle) .fullscreen-track-info,
|
||||
#fullscreen-cover-overlay:not(.controls-idle) .fullscreen-controls,
|
||||
#fullscreen-cover-overlay:not(.controls-idle) #fullscreen-next-track,
|
||||
#fullscreen-cover-overlay:not(.controls-idle) #toggle-ui-btn,
|
||||
#fullscreen-cover-overlay:not(.controls-idle) .fullscreen-lyrics-toggle,
|
||||
#fullscreen-cover-overlay:not(.controls-idle) .fullscreen-top-actions {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
transition:
|
||||
opacity 0.4s ease,
|
||||
transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay.controls-idle {
|
||||
cursor: none;
|
||||
}
|
||||
#fullscreen-cover-image {
|
||||
max-width: 55vw;
|
||||
max-height: 45vh;
|
||||
|
|
@ -4946,7 +5046,7 @@ input:checked + .slider::before {
|
|||
|
||||
#download-notifications {
|
||||
position: fixed;
|
||||
bottom: 120px;
|
||||
bottom: calc(max(env(safe-area-inset-bottom), 0px) + 12px);
|
||||
right: 20px;
|
||||
z-index: 20000;
|
||||
max-width: 350px;
|
||||
|
|
@ -6774,7 +6874,7 @@ img[src=''] {
|
|||
}
|
||||
|
||||
#download-notifications {
|
||||
bottom: 10px;
|
||||
bottom: calc(max(env(safe-area-inset-bottom), 0px) + 10px);
|
||||
right: 10px;
|
||||
left: 10px;
|
||||
max-width: none;
|
||||
|
|
@ -9936,3 +10036,514 @@ body:has(#side-panel.active) #close-fullscreen-cover-btn {
|
|||
.contrib {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* Fullscreen layout rebuild on PR 378 base */
|
||||
#fullscreen-cover-overlay .fullscreen-shell {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: center;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-main-view {
|
||||
width: min(1240px, 100%);
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(360px, 430px) minmax(420px, 1fr);
|
||||
gap: clamp(1.5rem, 3vw, 3rem);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: clamp(4rem, 7vh, 5rem) clamp(2rem, 4vw, 3rem) clamp(3rem, 6vh, 4rem) clamp(4rem, 7vw, 6.25rem);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-media-column,
|
||||
#fullscreen-cover-overlay .fullscreen-lyrics-pane {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-media-column {
|
||||
width: min(420px, 100%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.95rem;
|
||||
justify-self: center;
|
||||
transform: translateX(clamp(0.75rem, 1.2vw, 1.4rem));
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-artwork-card {
|
||||
width: min(420px, 100%);
|
||||
aspect-ratio: 1 / 1;
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 28px 80px rgba(0, 0, 0, 0.26);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay #fullscreen-cover-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
max-width: none;
|
||||
max-height: none;
|
||||
object-fit: cover;
|
||||
border-radius: 18px;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-track-info {
|
||||
width: min(420px, 100%);
|
||||
display: block;
|
||||
text-align: left;
|
||||
max-width: none;
|
||||
padding: 0.15rem 0 0;
|
||||
background: none;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
backdrop-filter: none;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-track-text {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay #fullscreen-track-title {
|
||||
margin: 0;
|
||||
font-size: clamp(1.15rem, 1.5vw, 1.42rem);
|
||||
line-height: 1.08;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay #fullscreen-track-artist {
|
||||
margin: 0.12rem 0 0;
|
||||
font-size: 0.94rem;
|
||||
color: rgb(255 255 255 / 0.74);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay #toggle-fullscreen-lyrics-btn,
|
||||
#fullscreen-cover-overlay .fullscreen-lyrics-toggle {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-actions {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.9rem;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-actions .btn-icon {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
padding: 0;
|
||||
border-radius: 999px;
|
||||
color: rgb(255 255 255 / 0.74);
|
||||
background: transparent;
|
||||
transition:
|
||||
color 0.2s ease,
|
||||
background-color 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-actions .btn-icon:hover {
|
||||
color: rgb(255 255 255 / 0.96);
|
||||
background: rgb(255 255 255 / 0.08);
|
||||
transform: scale(1.03);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay #fullscreen-next-track {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
margin-top: 0.85rem;
|
||||
color: rgb(255 255 255 / 0.56);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay #fullscreen-next-track .label {
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay #fullscreen-next-track .value {
|
||||
font-size: 0.84rem;
|
||||
color: rgb(255 255 255 / 0.74);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-top-actions {
|
||||
position: absolute;
|
||||
top: 1.25rem;
|
||||
left: calc(1.5rem + env(safe-area-inset-left));
|
||||
right: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
z-index: 12;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-top-actions button {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
border-radius: 999px;
|
||||
padding: 0;
|
||||
background: rgb(9 12 18 / 0.34);
|
||||
color: rgb(255 255 255 / 0.72);
|
||||
backdrop-filter: blur(10px);
|
||||
transition:
|
||||
color 0.2s ease,
|
||||
background-color 0.2s ease,
|
||||
opacity 0.2s ease;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-top-actions #fs-visualizer-btn {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-top-actions #fs-visualizer-btn,
|
||||
#fullscreen-cover-overlay .fullscreen-top-actions #close-fullscreen-cover-btn {
|
||||
position: static;
|
||||
top: auto;
|
||||
right: auto;
|
||||
left: auto;
|
||||
bottom: auto;
|
||||
margin: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-top-actions #close-fullscreen-cover-btn {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay #toggle-ui-btn {
|
||||
top: 1.25rem;
|
||||
left: calc(80px + 2.3rem + env(safe-area-inset-left));
|
||||
right: auto;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: rgb(9 12 18 / 0.34);
|
||||
color: rgb(255 255 255 / 0.72);
|
||||
backdrop-filter: blur(10px);
|
||||
opacity: 1;
|
||||
z-index: 12;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-controls {
|
||||
width: min(420px, 100%);
|
||||
margin-top: 0;
|
||||
align-items: center;
|
||||
gap: 0.85rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-buttons {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-buttons button {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
color: rgb(255 255 255 / 0.72);
|
||||
border-radius: 999px;
|
||||
padding: 0;
|
||||
transition:
|
||||
color 0.2s ease,
|
||||
transform 0.2s ease,
|
||||
background-color 0.2s ease,
|
||||
opacity 0.2s ease;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-buttons button:hover {
|
||||
color: rgb(255 255 255 / 0.94);
|
||||
background: rgb(255 255 255 / 0.08);
|
||||
transform: scale(1.04);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-buttons button.active {
|
||||
color: rgb(var(--highlight-rgb) / 0.98);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-buttons #fs-play-pause-btn {
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
background: rgb(255 255 255 / 0.96);
|
||||
color: rgb(11 15 21 / 0.92);
|
||||
box-shadow: 0 12px 28px rgb(0 0 0 / 0.2);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-buttons #fs-play-pause-btn:hover {
|
||||
background: rgb(255 255 255 / 1);
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-volume-container {
|
||||
width: 238px;
|
||||
max-width: 100%;
|
||||
align-self: center;
|
||||
justify-content: center;
|
||||
margin-top: 0.2rem;
|
||||
margin-inline: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fs-visualizer-btn,
|
||||
#fullscreen-cover-overlay .fs-volume-btn {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
padding: 0;
|
||||
color: rgb(255 255 255 / 0.62);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fs-visualizer-btn:hover,
|
||||
#fullscreen-cover-overlay .fs-volume-btn:hover {
|
||||
background: transparent;
|
||||
color: rgb(255 255 255 / 0.9);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fs-visualizer-btn.active {
|
||||
color: rgb(var(--highlight-rgb) / 0.96);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fs-volume-btn {
|
||||
position: absolute;
|
||||
left: -2.5rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fs-volume-btn:hover {
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fs-volume-bar {
|
||||
width: 238px;
|
||||
height: 4px;
|
||||
background: rgb(255 255 255 / 0.24);
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fs-volume-bar:hover {
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fs-volume-fill,
|
||||
#fullscreen-cover-overlay .fullscreen-progress-container .progress-fill {
|
||||
background: rgb(255 255 255 / 0.92);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-progress-container {
|
||||
color: rgb(255 255 255 / 0.62);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-progress-container .progress-bar {
|
||||
height: 4px;
|
||||
background: rgb(255 255 255 / 0.2);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-progress-container .progress-bar:hover {
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-progress-container .progress-bar:hover .progress-fill,
|
||||
#fullscreen-cover-overlay .fs-volume-bar:hover .fs-volume-fill {
|
||||
background: rgb(var(--highlight-rgb) / 0.94);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-progress-container .progress-bar:hover .progress-fill::after,
|
||||
#fullscreen-cover-overlay .fullscreen-progress-container .progress-bar:active .progress-fill::after,
|
||||
#fullscreen-cover-overlay .fs-volume-bar:hover .fs-volume-fill::after,
|
||||
#fullscreen-cover-overlay .fs-volume-bar:active .fs-volume-fill::after {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
box-shadow: 0 4px 12px rgb(0 0 0 / 0.28);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-lyrics-pane {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: flex-start;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-lyrics-shell,
|
||||
#fullscreen-cover-overlay .fullscreen-lyrics-content,
|
||||
#fullscreen-cover-overlay .fullscreen-lyrics-content am-lyrics {
|
||||
background: transparent !important;
|
||||
border: 0 !important;
|
||||
box-shadow: none !important;
|
||||
outline: none !important;
|
||||
backdrop-filter: none !important;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-lyrics-shell {
|
||||
width: min(860px, 100%);
|
||||
min-height: 0;
|
||||
margin-left: clamp(4rem, 8vw, 8rem);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-lyrics-content {
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
padding-left: clamp(2.5rem, 4vw, 4rem);
|
||||
mask-image: none;
|
||||
overflow: visible;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-lyrics-content::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-lyrics-content am-lyrics {
|
||||
--am-lyrics-highlight-color: #f6f4ef;
|
||||
--lyrics-scroll-padding-top: 18%;
|
||||
--lyplus-blur-amount: 0.16em;
|
||||
--lyplus-blur-amount-near: 0.085em;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
font-family:
|
||||
'SF Pro Display',
|
||||
Inter,
|
||||
sans-serif;
|
||||
--lyplus-font-size-base: clamp(34px, 3vw, 52px);
|
||||
--lyplus-padding-line: 8px;
|
||||
--lyplus-text-color: rgba(246, 244, 239, 0.08);
|
||||
--lyplus-active-color: #f6f4ef;
|
||||
line-height: 1.32;
|
||||
letter-spacing: -0.04em;
|
||||
font-weight: 600;
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-lyrics-content::after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-lyrics-empty,
|
||||
#fullscreen-cover-overlay .fullscreen-lyrics-content .lyrics-loading,
|
||||
#fullscreen-cover-overlay .fullscreen-lyrics-content .lyrics-error {
|
||||
padding: clamp(5rem, 14vh, 7rem) 0 0 clamp(2rem, 5vw, 4.5rem);
|
||||
background: none;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay.lyrics-unavailable .fullscreen-lyrics-pane {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
#fullscreen-cover-overlay .fullscreen-main-view {
|
||||
grid-template-columns: 1fr;
|
||||
width: min(760px, 100%);
|
||||
gap: 1rem;
|
||||
align-items: start;
|
||||
padding:
|
||||
calc(4.5rem + env(safe-area-inset-top))
|
||||
clamp(1rem, 4vw, 1.5rem)
|
||||
calc(1.5rem + env(safe-area-inset-bottom))
|
||||
clamp(1rem, 4vw, 1.5rem);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-media-column {
|
||||
justify-self: center;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-lyrics-pane {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
#fullscreen-cover-overlay .fullscreen-cover-content {
|
||||
padding: 0.75rem 0.75rem calc(0.75rem + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-top-actions {
|
||||
top: calc(0.75rem + env(safe-area-inset-top));
|
||||
left: calc(1rem + env(safe-area-inset-left));
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-top-actions button,
|
||||
#fullscreen-cover-overlay #toggle-ui-btn {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background: rgb(9 12 18 / 0.5);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay #toggle-ui-btn {
|
||||
top: calc(0.75rem + env(safe-area-inset-top));
|
||||
left: calc(88px + 1.25rem + env(safe-area-inset-left));
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-main-view {
|
||||
width: 100%;
|
||||
gap: 0.85rem;
|
||||
padding:
|
||||
calc(4.25rem + env(safe-area-inset-top))
|
||||
0.75rem
|
||||
calc(1.5rem + env(safe-area-inset-bottom))
|
||||
0.75rem;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-track-info,
|
||||
#fullscreen-cover-overlay .fullscreen-controls,
|
||||
#fullscreen-cover-overlay .fullscreen-media-column {
|
||||
width: min(100%, 460px);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-actions {
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.45rem;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-actions .btn-icon {
|
||||
background: rgb(255 255 255 / 0.06);
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-progress-container {
|
||||
gap: 0.65rem;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-buttons {
|
||||
gap: 0.2rem;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-buttons button {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-buttons #fs-play-pause-btn {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fullscreen-volume-container {
|
||||
width: min(220px, calc(100% - 2.75rem));
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fs-volume-btn {
|
||||
left: -2.25rem;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay .fs-volume-bar {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue