Merge branch 'main' into fix/multi-artist
This commit is contained in:
commit
132d00263e
8 changed files with 95 additions and 45 deletions
BIN
assets/appicon.png
Normal file
BIN
assets/appicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.2 KiB |
|
|
@ -357,7 +357,7 @@
|
||||||
|
|
||||||
<footer class="now-playing-bar">
|
<footer class="now-playing-bar">
|
||||||
<div class="track-info">
|
<div class="track-info">
|
||||||
<img src="https://resources.tidal.com/images/03b50002/d6f9/40e7/8b2e/0e9093cba2ac/80x80.jpg" alt="Current Track Cover" class="cover">
|
<img src="./assets/appicon.png" alt="Current Track Cover" class="cover">
|
||||||
<div class="details">
|
<div class="details">
|
||||||
<div class="title">Select a song</div>
|
<div class="title">Select a song</div>
|
||||||
<div class="artist"></div>
|
<div class="artist"></div>
|
||||||
|
|
|
||||||
73
js/app.js
73
js/app.js
|
|
@ -8,7 +8,7 @@ import {
|
||||||
SVG_VOLUME, SVG_MUTE, formatTime, trackDataStore,
|
SVG_VOLUME, SVG_MUTE, formatTime, trackDataStore,
|
||||||
buildTrackFilename, RATE_LIMIT_ERROR_MESSAGE, debounce,
|
buildTrackFilename, RATE_LIMIT_ERROR_MESSAGE, debounce,
|
||||||
sanitizeForFilename,
|
sanitizeForFilename,
|
||||||
getTrackArtists
|
getTrackTitle
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
|
|
||||||
const downloadTasks = new Map();
|
const downloadTasks = new Map();
|
||||||
|
|
@ -39,13 +39,15 @@ function addDownloadTask(trackId, track, filename, api) {
|
||||||
const taskEl = document.createElement('div');
|
const taskEl = document.createElement('div');
|
||||||
taskEl.className = 'download-task';
|
taskEl.className = 'download-task';
|
||||||
taskEl.dataset.trackId = trackId;
|
taskEl.dataset.trackId = trackId;
|
||||||
|
|
||||||
|
const trackTitle = getTrackTitle(track);
|
||||||
|
|
||||||
taskEl.innerHTML = `
|
taskEl.innerHTML = `
|
||||||
<div style="display: flex; align-items: start; gap: 0.75rem;">
|
<div style="display: flex; align-items: start; gap: 0.75rem;">
|
||||||
<img src="${api.getCoverUrl(track.album?.cover, '80')}"
|
<img src="${api.getCoverUrl(track.album?.cover, '80')}"
|
||||||
style="width: 40px; height: 40px; border-radius: 4px; flex-shrink: 0;">
|
style="width: 40px; height: 40px; border-radius: 4px; flex-shrink: 0;">
|
||||||
<div style="flex: 1; min-width: 0;">
|
<div style="flex: 1; min-width: 0;">
|
||||||
<div style="font-weight: 500; font-size: 0.9rem; margin-bottom: 0.25rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${track.title}</div>
|
<div style="font-weight: 500; font-size: 0.9rem; margin-bottom: 0.25rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${trackTitle}</div>
|
||||||
<div style="font-size: 0.8rem; color: var(--muted-foreground); margin-bottom: 0.5rem;">${track.artist?.name || 'Unknown'}</div>
|
<div style="font-size: 0.8rem; color: var(--muted-foreground); margin-bottom: 0.5rem;">${track.artist?.name || 'Unknown'}</div>
|
||||||
<div class="download-progress-bar" style="height: 4px; background: var(--secondary); border-radius: 2px; overflow: hidden;">
|
<div class="download-progress-bar" style="height: 4px; background: var(--secondary); border-radius: 2px; overflow: hidden;">
|
||||||
<div class="download-progress-fill" style="width: 0%; height: 100%; background: var(--highlight); transition: width 0.2s;"></div>
|
<div class="download-progress-fill" style="width: 0%; height: 100%; background: var(--highlight); transition: width 0.2s;"></div>
|
||||||
|
|
@ -185,8 +187,9 @@ async function downloadAlbumAsZip(album, tracks, api, quality) {
|
||||||
for (let i = 0; i < tracks.length; i++) {
|
for (let i = 0; i < tracks.length; i++) {
|
||||||
const track = tracks[i];
|
const track = tracks[i];
|
||||||
const filename = buildTrackFilename(track, quality);
|
const filename = buildTrackFilename(track, quality);
|
||||||
|
const trackTitle = getTrackTitle(track);
|
||||||
|
|
||||||
updateBulkDownloadProgress(notification, i, tracks.length, track.title);
|
updateBulkDownloadProgress(notification, i, tracks.length, trackTitle);
|
||||||
|
|
||||||
const blob = await downloadTrackBlob(track, quality, api);
|
const blob = await downloadTrackBlob(track, quality, api);
|
||||||
zip.file(`${folderName}/${filename}`, blob);
|
zip.file(`${folderName}/${filename}`, blob);
|
||||||
|
|
@ -396,6 +399,48 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
const lastfmToggleSetting = document.getElementById('lastfm-toggle-setting');
|
const lastfmToggleSetting = document.getElementById('lastfm-toggle-setting');
|
||||||
|
|
||||||
window.loadHomeFeed = loadHomeFeed;
|
window.loadHomeFeed = loadHomeFeed;
|
||||||
|
function positionContextMenu(menu, x, y, preferLeft = false) {
|
||||||
|
menu.style.display = 'block';
|
||||||
|
menu.style.visibility = 'hidden';
|
||||||
|
|
||||||
|
const menuRect = menu.getBoundingClientRect();
|
||||||
|
const viewportWidth = window.innerWidth;
|
||||||
|
const viewportHeight = window.innerHeight;
|
||||||
|
|
||||||
|
let finalX = x;
|
||||||
|
let finalY = y;
|
||||||
|
|
||||||
|
if (preferLeft || (x + menuRect.width > viewportWidth)) {
|
||||||
|
finalX = x - menuRect.width;
|
||||||
|
if (finalX < 0) {
|
||||||
|
finalX = Math.min(x, viewportWidth - menuRect.width - 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finalX < 10) {
|
||||||
|
finalX = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finalX + menuRect.width > viewportWidth - 10) {
|
||||||
|
finalX = viewportWidth - menuRect.width - 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y + menuRect.height > viewportHeight) {
|
||||||
|
finalY = Math.max(10, y - menuRect.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finalY + menuRect.height > viewportHeight - 10) {
|
||||||
|
finalY = viewportHeight - menuRect.height - 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finalY < 10) {
|
||||||
|
finalY = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.style.left = `${finalX}px`;
|
||||||
|
menu.style.top = `${finalY}px`;
|
||||||
|
menu.style.visibility = 'visible';
|
||||||
|
}
|
||||||
|
|
||||||
function updateLastFMUI() {
|
function updateLastFMUI() {
|
||||||
if (scrobbler.isAuthenticated()) {
|
if (scrobbler.isAuthenticated()) {
|
||||||
|
|
@ -736,9 +781,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
|
||||||
const html = currentQueue.map((track, index) => {
|
const html = currentQueue.map((track, index) => {
|
||||||
const isPlaying = index === player.currentQueueIndex;
|
const isPlaying = index === player.currentQueueIndex;
|
||||||
const trackArtists = getTrackArtists(track, {
|
const trackTitle = getTrackTitle(track);
|
||||||
fallback: "Unknown"
|
|
||||||
});
|
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="queue-track-item ${isPlaying ? 'playing' : ''}" data-queue-index="${index}" data-track-id="${track.id}" draggable="true">
|
<div class="queue-track-item ${isPlaying ? 'playing' : ''}" data-queue-index="${index}" data-track-id="${track.id}" draggable="true">
|
||||||
|
|
@ -752,8 +795,8 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
<img src="${api.getCoverUrl(track.album?.cover, '80')}"
|
<img src="${api.getCoverUrl(track.album?.cover, '80')}"
|
||||||
class="track-item-cover" loading="lazy">
|
class="track-item-cover" loading="lazy">
|
||||||
<div class="track-item-details">
|
<div class="track-item-details">
|
||||||
<div class="title">${track.title}</div>
|
<div class="title">${trackTitle}</div>
|
||||||
<div class="artist">${trackArtists}</div>
|
<div class="artist">${track.artist?.name || 'Unknown'}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="track-item-duration">${formatTime(track.duration)}</div>
|
<div class="track-item-duration">${formatTime(track.duration)}</div>
|
||||||
|
|
@ -812,11 +855,11 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
|
||||||
function showQueueTrackMenu(e, trackIndex) {
|
function showQueueTrackMenu(e, trackIndex) {
|
||||||
const menu = document.getElementById('queue-track-menu');
|
const menu = document.getElementById('queue-track-menu');
|
||||||
menu.style.top = `${e.pageY}px`;
|
|
||||||
menu.style.left = `${e.pageX}px`;
|
|
||||||
menu.classList.add('show');
|
menu.classList.add('show');
|
||||||
menu.dataset.trackIndex = trackIndex;
|
menu.dataset.trackIndex = trackIndex;
|
||||||
|
|
||||||
|
positionContextMenu(menu, e.pageX, e.pageY, true);
|
||||||
|
|
||||||
document.addEventListener('click', hideQueueTrackMenu);
|
document.addEventListener('click', hideQueueTrackMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -849,9 +892,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
contextTrack = trackDataStore.get(trackItem);
|
contextTrack = trackDataStore.get(trackItem);
|
||||||
if (contextTrack) {
|
if (contextTrack) {
|
||||||
const rect = menuBtn.getBoundingClientRect();
|
const rect = menuBtn.getBoundingClientRect();
|
||||||
contextMenu.style.top = `${rect.bottom + 5}px`;
|
positionContextMenu(contextMenu, rect.left, rect.bottom + 5, true);
|
||||||
contextMenu.style.left = `${rect.left}px`;
|
|
||||||
contextMenu.style.display = 'block';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
@ -881,9 +922,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
contextTrack = trackDataStore.get(trackItem);
|
contextTrack = trackDataStore.get(trackItem);
|
||||||
|
|
||||||
if (contextTrack) {
|
if (contextTrack) {
|
||||||
contextMenu.style.top = `${e.pageY}px`;
|
positionContextMenu(contextMenu, e.pageX, e.pageY, true);
|
||||||
contextMenu.style.left = `${e.pageX}px`;
|
|
||||||
contextMenu.style.display = 'block';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -1164,4 +1203,4 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
deferredPrompt = e;
|
deferredPrompt = e;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
20
js/player.js
20
js/player.js
|
|
@ -1,5 +1,5 @@
|
||||||
//player.js
|
//player.js
|
||||||
import { REPEAT_MODE, formatTime, getTrackArtists } from './utils.js';
|
import { REPEAT_MODE, formatTime, getTrackTitle } from './utils.js';
|
||||||
|
|
||||||
export class Player {
|
export class Player {
|
||||||
constructor(audioElement, api, quality = 'LOSSLESS') {
|
constructor(audioElement, api, quality = 'LOSSLESS') {
|
||||||
|
|
@ -99,6 +99,7 @@ export class Player {
|
||||||
|
|
||||||
for (const { track, index } of tracksToPreload) {
|
for (const { track, index } of tracksToPreload) {
|
||||||
if (this.preloadCache.has(track.id)) continue;
|
if (this.preloadCache.has(track.id)) continue;
|
||||||
|
const trackTitle = getTrackTitle(track);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const streamUrl = await this.api.getStreamUrl(track.id, this.quality);
|
const streamUrl = await this.api.getStreamUrl(track.id, this.quality);
|
||||||
|
|
@ -113,7 +114,7 @@ export class Player {
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.name !== 'AbortError') {
|
if (error.name !== 'AbortError') {
|
||||||
console.debug('Failed to get stream URL for preload:', track.title);
|
console.debug('Failed to get stream URL for preload:', trackTitle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -128,13 +129,13 @@ async playTrackFromQueue() {
|
||||||
const track = currentQueue[this.currentQueueIndex];
|
const track = currentQueue[this.currentQueueIndex];
|
||||||
this.currentTrack = track;
|
this.currentTrack = track;
|
||||||
|
|
||||||
const trackArtists = getTrackArtists(track);
|
const trackTitle = getTrackTitle(track);
|
||||||
|
|
||||||
document.querySelector('.now-playing-bar .cover').src =
|
document.querySelector('.now-playing-bar .cover').src =
|
||||||
this.api.getCoverUrl(track.album?.cover, '1280');
|
this.api.getCoverUrl(track.album?.cover, '1280');
|
||||||
document.querySelector('.now-playing-bar .title').textContent = track.title;
|
document.querySelector('.now-playing-bar .title').textContent = trackTitle;
|
||||||
document.querySelector('.now-playing-bar .artist').textContent = trackArtists;
|
document.querySelector('.now-playing-bar .artist').textContent = track.artist?.name || 'Unknown Artist';
|
||||||
document.title = `${track.title} • ${track.artist?.name || 'Unknown'}`;
|
document.title = `${trackTitle} • ${track.artist?.name || 'Unknown'}`;
|
||||||
|
|
||||||
this.updatePlayingTrackIndicator();
|
this.updatePlayingTrackIndicator();
|
||||||
this.updateMediaSession(track);
|
this.updateMediaSession(track);
|
||||||
|
|
@ -183,8 +184,8 @@ async playTrackFromQueue() {
|
||||||
this.setupCrossfadeListener();
|
this.setupCrossfadeListener();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Could not play track: ${track.title}`, error);
|
console.error(`Could not play track: ${trackTitle}`, error);
|
||||||
document.querySelector('.now-playing-bar .title').textContent = `Error: ${track.title}`;
|
document.querySelector('.now-playing-bar .title').textContent = `Error: ${trackTitle}`;
|
||||||
document.querySelector('.now-playing-bar .artist').textContent = error.message || 'Could not load track';
|
document.querySelector('.now-playing-bar .artist').textContent = error.message || 'Could not load track';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -412,6 +413,7 @@ async playTrackFromQueue() {
|
||||||
const artwork = [];
|
const artwork = [];
|
||||||
const sizes = ['1280'];
|
const sizes = ['1280'];
|
||||||
const coverId = track.album?.cover;
|
const coverId = track.album?.cover;
|
||||||
|
const trackTitle = getTrackTitle(track);
|
||||||
|
|
||||||
if (coverId) {
|
if (coverId) {
|
||||||
sizes.forEach(size => {
|
sizes.forEach(size => {
|
||||||
|
|
@ -424,7 +426,7 @@ async playTrackFromQueue() {
|
||||||
}
|
}
|
||||||
|
|
||||||
navigator.mediaSession.metadata = new MediaMetadata({
|
navigator.mediaSession.metadata = new MediaMetadata({
|
||||||
title: track.title || 'Unknown Title',
|
title: trackTitle,
|
||||||
artist: track.artist?.name || 'Unknown Artist',
|
artist: track.artist?.name || 'Unknown Artist',
|
||||||
album: track.album?.title || 'Unknown Album',
|
album: track.album?.title || 'Unknown Album',
|
||||||
artwork: artwork.length > 0 ? artwork : undefined
|
artwork: artwork.length > 0 ? artwork : undefined
|
||||||
|
|
|
||||||
8
js/ui.js
8
js/ui.js
|
|
@ -1,5 +1,5 @@
|
||||||
//ui.js
|
//ui.js
|
||||||
import { formatTime, createPlaceholder, trackDataStore, hasExplicitContent, getTrackArtists } from './utils.js';
|
import { formatTime, createPlaceholder, trackDataStore, hasExplicitContent, getTrackTitle } from './utils.js';
|
||||||
import { recentActivityManager } from './storage.js';
|
import { recentActivityManager } from './storage.js';
|
||||||
|
|
||||||
export class UIRenderer {
|
export class UIRenderer {
|
||||||
|
|
@ -25,8 +25,8 @@ export class UIRenderer {
|
||||||
createTrackItemHTML(track, index, showCover = false) {
|
createTrackItemHTML(track, index, showCover = false) {
|
||||||
const playIconSmall = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>';
|
const playIconSmall = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>';
|
||||||
const trackNumberHTML = `<div class="track-number">${showCover ? playIconSmall : index + 1}</div>`;
|
const trackNumberHTML = `<div class="track-number">${showCover ? playIconSmall : index + 1}</div>`;
|
||||||
const explicitBadge = hasExplicitContent(track) ? this.createExplicitBadge() : '';
|
const explicitBadge = !hasExplicitContent(track) ? this.createExplicitBadge() : '';
|
||||||
const trackArtists = getTrackArtists(track);
|
const trackTitle = getTrackTitle(track);
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="track-item" data-track-id="${track.id}">
|
<div class="track-item" data-track-id="${track.id}">
|
||||||
|
|
@ -35,7 +35,7 @@ export class UIRenderer {
|
||||||
${showCover ? `<img src="${this.api.getCoverUrl(track.album?.cover, '80')}" alt="Track Cover" class="track-item-cover" loading="lazy">` : ''}
|
${showCover ? `<img src="${this.api.getCoverUrl(track.album?.cover, '80')}" alt="Track Cover" class="track-item-cover" loading="lazy">` : ''}
|
||||||
<div class="track-item-details">
|
<div class="track-item-details">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
${track.title}
|
${trackTitle}
|
||||||
${explicitBadge}
|
${explicitBadge}
|
||||||
</div>
|
</div>
|
||||||
<div class="artist">${trackArtists}</div>
|
<div class="artist">${trackArtists}</div>
|
||||||
|
|
|
||||||
13
js/utils.js
13
js/utils.js
|
|
@ -70,7 +70,7 @@ export const buildTrackFilename = (track, quality) => {
|
||||||
|
|
||||||
const artistName = sanitizeForFilename(track.artist?.name);
|
const artistName = sanitizeForFilename(track.artist?.name);
|
||||||
const albumTitle = sanitizeForFilename(track.album?.title);
|
const albumTitle = sanitizeForFilename(track.album?.title);
|
||||||
const trackTitle = sanitizeForFilename(track.title);
|
const trackTitle = sanitizeForFilename(getTrackTitle(track));
|
||||||
|
|
||||||
return `${artistName} - ${albumTitle} - ${padded} ${trackTitle}.${extension}`;
|
return `${artistName} - ${albumTitle} - ${padded} ${trackTitle}.${extension}`;
|
||||||
};
|
};
|
||||||
|
|
@ -157,10 +157,7 @@ export const debounce = (func, wait) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getTrackArtists = (track = {}, { fallback = 'Unknown Artist' } = {}) => {
|
export const getTrackTitle = (track, { fallback = 'Unknown Title' } = {}) => {
|
||||||
if (track?.artists?.length) {
|
if (!track?.title) return fallback;
|
||||||
return track.artists.map(artist => artist?.name).join(', ');
|
return track?.version ? `${track.title} (${track.version})` : track.title;
|
||||||
}
|
};
|
||||||
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -9,19 +9,19 @@
|
||||||
"orientation": "portrait-primary",
|
"orientation": "portrait-primary",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/assets/192.png",
|
"src": "/assets/appicon.png",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any"
|
"purpose": "any"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/assets/512.png",
|
"src": "/assets/appicon.png",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any"
|
"purpose": "any"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/assets/512.png",
|
"src": "/assets/appicon.png",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable"
|
"purpose": "maskable"
|
||||||
|
|
|
||||||
18
styles.css
18
styles.css
|
|
@ -117,6 +117,8 @@
|
||||||
html {
|
html {
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
|
@ -125,6 +127,9 @@ body {
|
||||||
font-family: 'Inter', sans-serif;
|
font-family: 'Inter', sans-serif;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: background-color 0.3s ease, color 0.3s ease;
|
transition: background-color 0.3s ease, color 0.3s ease;
|
||||||
|
height: 100%;
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
|
|
@ -142,6 +147,7 @@ a {
|
||||||
.app-container {
|
.app-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
height: 100dvh;
|
||||||
grid-template:
|
grid-template:
|
||||||
"sidebar main" 1fr
|
"sidebar main" 1fr
|
||||||
"player player" auto / 280px 1fr;
|
"player player" auto / 280px 1fr;
|
||||||
|
|
@ -589,7 +595,10 @@ a {
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-item:hover .track-menu-btn,
|
.track-item:hover .track-menu-btn {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
@media (hover: none) {
|
@media (hover: none) {
|
||||||
.track-menu-btn {
|
.track-menu-btn {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
@ -1005,7 +1014,7 @@ input:checked + .slider::before {
|
||||||
#context-menu,
|
#context-menu,
|
||||||
.queue-track-menu {
|
.queue-track-menu {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: fixed;
|
||||||
background-color: var(--card);
|
background-color: var(--card);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
|
|
@ -1554,6 +1563,8 @@ input:checked + .slider::before {
|
||||||
"header" auto
|
"header" auto
|
||||||
"main" 1fr
|
"main" 1fr
|
||||||
"player" auto / 1fr;
|
"player" auto / 1fr;
|
||||||
|
height: 100vh;
|
||||||
|
height: 100dvh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-content {
|
.main-content {
|
||||||
|
|
@ -1637,6 +1648,7 @@ input:checked + .slider::before {
|
||||||
padding: var(--spacing-md);
|
padding: var(--spacing-md);
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.now-playing-bar .track-info {
|
.now-playing-bar .track-info {
|
||||||
grid-area: track;
|
grid-area: track;
|
||||||
|
|
@ -1685,7 +1697,7 @@ input:checked + .slider::before {
|
||||||
}
|
}
|
||||||
|
|
||||||
#download-notifications {
|
#download-notifications {
|
||||||
bottom: 160px;
|
bottom: 10px;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
left: 10px;
|
left: 10px;
|
||||||
max-width: none;
|
max-width: none;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue