style: auto-fix linting issues

This commit is contained in:
JulienMaille 2026-01-16 17:37:12 +00:00 committed by Julien Maille
parent 82301e8a44
commit bae0d0a170
19 changed files with 118 additions and 205 deletions

View file

@ -19,7 +19,7 @@ export default [
}, },
rules: { rules: {
'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], 'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
'no-console': ['warn', { allow: ['warn', 'error'] }], 'no-console': ['warn', { allow: ['log', 'warn', 'error'] }],
}, },
}, },
]; ];

View file

@ -152,7 +152,7 @@ export function initializeFirebaseSettingsUI() {
customFirebaseConfigContainer.classList.add('visible'); customFirebaseConfigContainer.classList.add('visible');
toggleFirebaseConfigBtn.textContent = 'Hide Custom Configuration'; toggleFirebaseConfigBtn.textContent = 'Hide Custom Configuration';
} }
} catch (e) { } catch {
firebaseConfigInput.value = currentConfig; firebaseConfigInput.value = currentConfig;
} }
} }
@ -180,7 +180,7 @@ export function initializeFirebaseSettingsUI() {
prompt('Copy this link:', link); prompt('Copy this link:', link);
}); });
} }
} catch (e) { } catch {
alert('Invalid configuration found.'); alert('Invalid configuration found.');
} }
}); });

View file

@ -1,3 +1,4 @@
import PocketBase from 'pocketbase';
import { db } from '../db.js'; import { db } from '../db.js';
import { authManager } from './auth.js'; import { authManager } from './auth.js';
@ -292,7 +293,9 @@ const syncManager = {
if (typeof extraData === 'string') { if (typeof extraData === 'string') {
try { try {
extraData = JSON.parse(extraData); extraData = JSON.parse(extraData);
} catch (e) {} } catch {
// Ignore
}
} }
if (!rawCover && extraData && typeof extraData === 'object') { if (!rawCover && extraData && typeof extraData === 'object') {

View file

@ -10,13 +10,7 @@ import {
import { UIRenderer } from './ui.js'; import { UIRenderer } from './ui.js';
import { Player } from './player.js'; import { Player } from './player.js';
import { LastFMScrobbler } from './lastfm.js'; import { LastFMScrobbler } from './lastfm.js';
import { import { LyricsManager, openLyricsPanel, clearLyricsPanelSync } from './lyrics.js';
LyricsManager,
openLyricsPanel,
clearLyricsPanelSync,
renderLyricsInFullscreen,
clearFullscreenLyricsSync,
} from './lyrics.js';
import { createRouter, updateTabTitle } from './router.js'; import { createRouter, updateTabTitle } from './router.js';
import { initializeSettings } from './settings.js'; import { initializeSettings } from './settings.js';
import { initializePlayerEvents, initializeTrackInteractions, handleTrackAction } from './events.js'; import { initializePlayerEvents, initializeTrackInteractions, handleTrackAction } from './events.js';
@ -176,7 +170,7 @@ function showOfflineNotification() {
document.body.appendChild(notification); document.body.appendChild(notification);
setTimeout(() => { setTimeout(() => {
notification.style.animation = 'slideOut 0.3s ease forwards'; notification.style.animation = 'slide-out 0.3s ease forwards';
setTimeout(() => notification.remove(), 300); setTimeout(() => notification.remove(), 300);
}, 5000); }, 5000);
} }
@ -184,7 +178,7 @@ function showOfflineNotification() {
function hideOfflineNotification() { function hideOfflineNotification() {
const notification = document.querySelector('.offline-notification'); const notification = document.querySelector('.offline-notification');
if (notification) { if (notification) {
notification.style.animation = 'slideOut 0.3s ease forwards'; notification.style.animation = 'slide-out 0.3s ease forwards';
setTimeout(() => notification.remove(), 300); setTimeout(() => notification.remove(), 300);
} }
} }
@ -398,7 +392,7 @@ document.addEventListener('DOMContentLoaded', async () => {
if (!userPlaylist) { if (!userPlaylist) {
try { try {
userPlaylist = await syncManager.getPublicPlaylist(playlistId); userPlaylist = await syncManager.getPublicPlaylist(playlistId);
} catch (e) { } catch {
// Not a public playlist // Not a public playlist
} }
} }
@ -461,7 +455,7 @@ document.addEventListener('DOMContentLoaded', async () => {
} else { } else {
try { try {
await syncManager.unpublishPlaylist(playlist.id); await syncManager.unpublishPlaylist(playlist.id);
} catch (e) { } catch {
// Ignore error if it wasn't public // Ignore error if it wasn't public
} }
} }
@ -1292,7 +1286,7 @@ async function parseCSV(csvText, api, onProgress) {
const cleanTitle = (t) => const cleanTitle = (t) =>
t t
.split(' - ')[0] .split(' - ')[0]
.replace(/\s*[\(\[]feat\.?.*?[\)\]]/i, '') .replace(/\s*[([]feat\.?.*?[)\]]/i, '')
.trim(); .trim();
const cleanedTitle = cleanTitle(trackTitle); const cleanedTitle = cleanTitle(trackTitle);
const isTitleCleaned = cleanedTitle !== trackTitle; const isTitleCleaned = cleanedTitle !== trackTitle;

View file

@ -57,7 +57,7 @@ export class APICache {
return cached.data; return cached.data;
} }
} catch (error) { } catch (error) {
console.debug('IndexedDB read error:', error); console.log('IndexedDB read error:', error);
} }
} }
@ -83,7 +83,7 @@ export class APICache {
try { try {
await this.setInIndexedDB(entry); await this.setInIndexedDB(entry);
} catch (error) { } catch (error) {
console.debug('IndexedDB write error:', error); console.log('IndexedDB write error:', error);
} }
} }
} }
@ -163,7 +163,7 @@ export class APICache {
} }
}; };
} catch (error) { } catch (error) {
console.debug('Failed to clear expired IndexedDB entries:', error); console.log('Failed to clear expired IndexedDB entries:', error);
} }
} }
} }

View file

@ -136,7 +136,7 @@ export class MusicDatabase {
try { try {
const result = await this.performTransaction(storeName, 'readonly', (store) => store.get(id)); const result = await this.performTransaction(storeName, 'readonly', (store) => store.get(id));
return !!result; return !!result;
} catch (e) { } catch {
return false; return false;
} }
} }
@ -316,12 +316,10 @@ export class MusicDatabase {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const transaction = db.transaction(storeName, 'readwrite'); const transaction = db.transaction(storeName, 'readwrite');
const store = transaction.objectStore(storeName); const store = transaction.objectStore(storeName);
let hasChanges = false;
// force clear on first sync // force clear on first sync
console.log(`Clearing ${storeName} to Make Sure Everythings Good`); console.log(`Clearing ${storeName} to Make Sure Everythings Good`);
store.clear(); store.clear();
hasChanges = true;
itemsArray.forEach((item) => { itemsArray.forEach((item) => {
if (item.id && typeof item.id === 'string' && !isNaN(item.id)) { if (item.id && typeof item.id === 'string' && !isNaN(item.id)) {

View file

@ -64,7 +64,7 @@ export function showNotification(message) {
// Auto remove // Auto remove
setTimeout(() => { setTimeout(() => {
notifEl.style.animation = 'slideOut 0.3s ease'; notifEl.style.animation = 'slide-out 0.3s ease forwards';
setTimeout(() => notifEl.remove(), 300); setTimeout(() => notifEl.remove(), 300);
}, 1500); }, 1500);
} }
@ -162,7 +162,7 @@ function removeDownloadTask(trackId) {
if (!task) return; if (!task) return;
const { taskEl } = task; const { taskEl } = task;
taskEl.style.animation = 'slideOut 0.3s ease'; taskEl.style.animation = 'slide-out 0.3s ease forwards';
setTimeout(() => { setTimeout(() => {
taskEl.remove(); taskEl.remove();
@ -179,7 +179,7 @@ function removeBulkDownloadTask(notifEl) {
const task = bulkDownloadTasks.get(notifEl); const task = bulkDownloadTasks.get(notifEl);
if (!task) return; if (!task) return;
notifEl.style.animation = 'slideOut 0.3s ease'; notifEl.style.animation = 'slide-out 0.3s ease forwards';
setTimeout(() => { setTimeout(() => {
notifEl.remove(); notifEl.remove();
@ -225,6 +225,7 @@ async function downloadTrackBlob(track, quality, api, lyricsManager = null, sign
} }
// Handle DASH streams (blob URLs) // Handle DASH streams (blob URLs)
let blob;
if (streamUrl.startsWith('blob:')) { if (streamUrl.startsWith('blob:')) {
try { try {
const downloader = new DashDownloader(); const downloader = new DashDownloader();
@ -266,7 +267,7 @@ async function generateAndDownloadZip(zip, filename, notification, progressTotal
compression: 'STORE', compression: 'STORE',
streamFiles: true, streamFiles: true,
}) })
.on('data', (chunk, metadata) => { .on('data', (chunk) => {
writable.write(chunk); writable.write(chunk);
}) })
.on('error', (err) => { .on('error', (err) => {
@ -364,7 +365,7 @@ async function downloadTracksToZip(
zip.file(`${folderName}/${lrcFilename}`, lrcContent); zip.file(`${folderName}/${lrcFilename}`, lrcContent);
} }
} }
} catch (error) { } catch {
console.log('Could not add lyrics for:', trackTitle); console.log('Could not add lyrics for:', trackTitle);
} }
} }
@ -518,7 +519,7 @@ export async function downloadDiscography(artist, selectedReleases, api, quality
zip.file(`${fullFolderPath}/${lrcFilename}`, lrcContent); zip.file(`${fullFolderPath}/${lrcFilename}`, lrcContent);
} }
} }
} catch (error) { } catch {
// Silent fail for lyrics in bulk // Silent fail for lyrics in bulk
} }
} }
@ -547,7 +548,7 @@ export async function downloadDiscography(artist, selectedReleases, api, quality
} }
} }
function createBulkDownloadNotification(type, name, totalItems) { function createBulkDownloadNotification(type, name, _totalItems) {
const container = createDownloadNotification(); const container = createDownloadNotification();
const notifEl = document.createElement('div'); const notifEl = document.createElement('div');
@ -608,7 +609,7 @@ function completeBulkDownload(notifEl, success = true, message = null) {
statusEl.style.color = '#10b981'; statusEl.style.color = '#10b981';
setTimeout(() => { setTimeout(() => {
notifEl.style.animation = 'slideOut 0.3s ease'; notifEl.style.animation = 'slide-out 0.3s ease forwards';
setTimeout(() => notifEl.remove(), 300); setTimeout(() => notifEl.remove(), 300);
}, 3000); }, 3000);
} else { } else {
@ -617,7 +618,7 @@ function completeBulkDownload(notifEl, success = true, message = null) {
statusEl.style.color = '#ef4444'; statusEl.style.color = '#ef4444';
setTimeout(() => { setTimeout(() => {
notifEl.style.animation = 'slideOut 0.3s ease'; notifEl.style.animation = 'slide-out 0.3s ease forwards';
setTimeout(() => notifEl.remove(), 300); setTimeout(() => notifEl.remove(), 300);
}, 5000); }, 5000);
} }
@ -660,7 +661,7 @@ export async function downloadTrackWithMetadata(track, quality, api, lyricsManag
ongoingDownloads.add(downloadKey); ongoingDownloads.add(downloadKey);
try { try {
const { taskEl } = addDownloadTask(track.id, enrichedTrack, filename, api, controller); addDownloadTask(track.id, enrichedTrack, filename, api, controller);
await api.downloadTrack(track.id, quality, filename, { await api.downloadTrack(track.id, quality, filename, {
signal: controller.signal, signal: controller.signal,
@ -678,7 +679,7 @@ export async function downloadTrackWithMetadata(track, quality, api, lyricsManag
if (lyricsData) { if (lyricsData) {
lyricsManager.downloadLRC(lyricsData, track); lyricsManager.downloadLRC(lyricsData, track);
} }
} catch (error) { } catch {
console.log('Could not download lyrics for track'); console.log('Could not download lyrics for track');
} }
} }

View file

@ -1,25 +1,13 @@
//js/events.js //js/events.js
import { import { SVG_PLAY, SVG_PAUSE, SVG_VOLUME, SVG_MUTE, REPEAT_MODE, trackDataStore, formatTime } from './utils.js';
SVG_PLAY,
SVG_PAUSE,
SVG_VOLUME,
SVG_MUTE,
REPEAT_MODE,
trackDataStore,
RATE_LIMIT_ERROR_MESSAGE,
buildTrackFilename,
getTrackTitle,
formatTime,
} from './utils.js';
import { lastFMStorage, waveformSettings } from './storage.js'; import { lastFMStorage, waveformSettings } from './storage.js';
import { showNotification, downloadTrackWithMetadata } from './downloads.js'; import { showNotification, downloadTrackWithMetadata } from './downloads.js';
import { lyricsSettings, downloadQualitySettings } from './storage.js'; import { downloadQualitySettings } from './storage.js';
import { updateTabTitle } from './router.js'; import { updateTabTitle } from './router.js';
import { db } from './db.js'; import { db } from './db.js';
import { syncManager } from './accounts/pocketbase.js'; import { syncManager } from './accounts/pocketbase.js';
import { waveformGenerator } from './waveform.js'; import { waveformGenerator } from './waveform.js';
let currentWaveformPeaks = null;
let currentTrackIdForWaveform = null; let currentTrackIdForWaveform = null;
export function initializePlayerEvents(player, audioPlayer, scrobbler, ui) { export function initializePlayerEvents(player, audioPlayer, scrobbler, ui) {
@ -396,7 +384,7 @@ function initializeSmoothSliders(audioPlayer, player) {
} }
}); });
document.addEventListener('mouseup', (e) => { document.addEventListener('mouseup', () => {
if (isSeeking) { if (isSeeking) {
// Commit the seek // Commit the seek
if (!isNaN(audioPlayer.duration)) { if (!isNaN(audioPlayer.duration)) {
@ -412,7 +400,7 @@ function initializeSmoothSliders(audioPlayer, player) {
} }
}); });
document.addEventListener('touchend', (e) => { document.addEventListener('touchend', () => {
if (isSeeking) { if (isSeeking) {
if (!isNaN(audioPlayer.duration)) { if (!isNaN(audioPlayer.duration)) {
audioPlayer.currentTime = lastSeekPosition * audioPlayer.duration; audioPlayer.currentTime = lastSeekPosition * audioPlayer.duration;
@ -562,7 +550,7 @@ export async function handleTrackAction(
if (!playlist) { if (!playlist) {
try { try {
playlist = await syncManager.getPublicPlaylist(item.id); playlist = await syncManager.getPublicPlaylist(item.id);
} catch (e) { } catch {
// Ignore // Ignore
} }
} }

View file

@ -1,5 +1,4 @@
//js/lastfm.js //js/lastfm.js
import { delay, getTrackArtists } from './utils.js';
export class LastFMScrobbler { export class LastFMScrobbler {
constructor() { constructor() {
@ -66,7 +65,7 @@ export class LastFMScrobbler {
try { try {
const { default: md5 } = await import('https://cdn.jsdelivr.net/npm/md5@2.3.0/+esm'); const { default: md5 } = await import('https://cdn.jsdelivr.net/npm/md5@2.3.0/+esm');
return md5(signatureString); return md5(signatureString);
} catch (e) { } catch {
console.error('MD5 library not available'); console.error('MD5 library not available');
throw new Error('MD5 library required for Last.fm'); throw new Error('MD5 library required for Last.fm');
} }

View file

@ -1,10 +1,9 @@
//js/lyrics.js //js/lyrics.js
import { getTrackTitle, getTrackArtists, buildTrackFilename, SVG_DOWNLOAD, SVG_CLOSE } from './utils.js'; import { getTrackTitle, getTrackArtists, buildTrackFilename, SVG_CLOSE } from './utils.js';
import { sidePanelManager } from './side-panel.js'; import { sidePanelManager } from './side-panel.js';
// Dictionary path for kuromoji // Dictionary path for kuromoji
// Using CDN - the kuroshiro-analyzer loaded from unpkg will use this as base for fetching dict files // Using CDN - the kuroshiro-analyzer loaded from unpkg will use this as base for fetching dict files
const KUROMOJI_DICT_PATH = 'https://cdn.jsdelivr.net/npm/kuromoji@0.1.2/dict/';
export class LyricsManager { export class LyricsManager {
constructor(api) { constructor(api) {
@ -190,7 +189,7 @@ export class LyricsManager {
getRomajiMode() { getRomajiMode() {
try { try {
return localStorage.getItem('lyricsRomajiMode') === 'true'; return localStorage.getItem('lyricsRomajiMode') === 'true';
} catch (e) { } catch {
return false; return false;
} }
} }
@ -549,7 +548,7 @@ export async function openLyricsPanel(track, audioPlayer, lyricsManager) {
romajiBtn.addEventListener('click', async () => { romajiBtn.addEventListener('click', async () => {
const amLyrics = sidePanelManager.panel.querySelector('am-lyrics'); const amLyrics = sidePanelManager.panel.querySelector('am-lyrics');
if (amLyrics) { if (amLyrics) {
const newMode = await manager.toggleRomajiMode(amLyrics); await manager.toggleRomajiMode(amLyrics);
updateRomajiBtn(); updateRomajiBtn();
} }
}); });

View file

@ -243,7 +243,6 @@ async function readMp3Metadata(file, metadata) {
if (file.size > 128) { if (file.size > 128) {
const tailBuffer = await file.slice(file.size - 128).arrayBuffer(); const tailBuffer = await file.slice(file.size - 128).arrayBuffer();
const tailView = new DataView(tailBuffer);
const tag = new TextDecoder().decode(new Uint8Array(tailBuffer, 0, 3)); const tag = new TextDecoder().decode(new Uint8Array(tailBuffer, 0, 3));
if (tag === 'TAG') { if (tag === 'TAG') {
const title = new TextDecoder() const title = new TextDecoder()
@ -288,33 +287,6 @@ function readID3Text(view) {
return decoder.decode(buffer).replace(/\0/g, ''); return decoder.decode(buffer).replace(/\0/g, '');
} }
function readID3Picture(view) {
let offset = 1;
const encoding = view.getUint8(0);
let mimeEnd = offset;
while (view.getUint8(mimeEnd) !== 0) mimeEnd++;
const mime = new TextDecoder('iso-8859-1').decode(
view.buffer.slice(view.byteOffset + offset, view.byteOffset + mimeEnd)
);
offset = mimeEnd + 1;
const picType = view.getUint8(offset);
offset++;
let descEnd = offset;
while (
view.getUint8(descEnd) !== 0 ||
(encoding === 1 || encoding === 2 ? view.getUint8(descEnd + 1) !== 0 : false)
)
descEnd++;
offset = descEnd + (encoding === 1 || encoding === 2 ? 2 : 1);
const imgData = view.buffer.slice(view.byteOffset + offset, view.byteOffset + view.byteLength);
const blob = new Blob([imgData], { type: mime });
return URL.createObjectURL(blob);
}
/** /**
* Adds Vorbis comment metadata to FLAC files * Adds Vorbis comment metadata to FLAC files
*/ */
@ -438,7 +410,7 @@ function createVorbisCommentBlock(track) {
if (!isNaN(year)) { if (!isNaN(year)) {
comments.push(['DATE', String(year)]); comments.push(['DATE', String(year)]);
} }
} catch (error) { } catch {
// Invalid date, skip // Invalid date, skip
} }
} }
@ -787,7 +759,7 @@ function createMp4MetadataAtoms(track) {
if (!isNaN(year)) { if (!isNaN(year)) {
tags['©day'] = String(year); tags['©day'] = String(year);
} }
} catch (error) { } catch {
// Invalid date, skip // Invalid date, skip
} }
} }
@ -862,12 +834,6 @@ function rebuildMp4WithMetadata(dataView, atoms, metadataAtoms) {
// Write preserved children of moov // Write preserved children of moov
for (const child of filteredMoovChildren) { for (const child of filteredMoovChildren) {
const childStart = moovAtom.offset + 8 + child.offset; // child.offset is relative to moov body start in our parseMp4Atoms helper usage?
// Wait, parseMp4Atoms returns absolute offsets usually?
// Let's verify parseMp4Atoms usage.
// When we passed a slice DataView, the offsets returned by parseMp4Atoms
// are relative to the start of that DataView (which is moov body start).
const absoluteChildStart = moovAtom.offset + 8 + child.offset; const absoluteChildStart = moovAtom.offset + 8 + child.offset;
newFile.set(originalArray.subarray(absoluteChildStart, absoluteChildStart + child.size), offset); newFile.set(originalArray.subarray(absoluteChildStart, absoluteChildStart + child.size), offset);
offset += child.size; offset += child.size;

View file

@ -618,7 +618,7 @@ export class Player {
position: Math.min(this.audio.currentTime, duration), position: Math.min(this.audio.currentTime, duration),
}); });
} catch (error) { } catch (error) {
console.debug('Failed to update Media Session position:', error); console.log('Failed to update Media Session position:', error);
} }
} }

View file

@ -58,7 +58,7 @@ export function initializeSettings(scrobbler, player, api, ui) {
authButtonsContainer.style.display = 'flex'; authButtonsContainer.style.display = 'flex';
emailInput.value = ''; emailInput.value = '';
passwordInput.value = ''; passwordInput.value = '';
} catch (e) { } catch {
// Error handled in authManager // Error handled in authManager
} }
}); });
@ -78,7 +78,7 @@ export function initializeSettings(scrobbler, player, api, ui) {
authButtonsContainer.style.display = 'flex'; authButtonsContainer.style.display = 'flex';
emailInput.value = ''; emailInput.value = '';
passwordInput.value = ''; passwordInput.value = '';
} catch (e) { } catch {
// Error handled in authManager // Error handled in authManager
} }
}); });
@ -165,7 +165,7 @@ export function initializeSettings(scrobbler, player, api, ui) {
lastfmToggle.checked = true; lastfmToggle.checked = true;
alert(`Successfully connected to Last.fm as ${result.username}!`); alert(`Successfully connected to Last.fm as ${result.username}!`);
} }
} catch (e) { } catch {
// Still waiting // Still waiting
} }
}, 2000); }, 2000);

View file

@ -1,4 +1,5 @@
//js/smooth-scrolling.js //js/smooth-scrolling.js
/* global Lenis */
import { smoothScrollingSettings } from './storage.js'; import { smoothScrollingSettings } from './storage.js';
let lenis = null; let lenis = null;

View file

@ -31,7 +31,7 @@ export const apiSettings = {
if (isSimpleArray) { if (isSimpleArray) {
groupedInstances.api = [...data.api]; groupedInstances.api = [...data.api];
} else { } else {
for (const [provider, config] of Object.entries(data.api)) { for (const [, config] of Object.entries(data.api)) {
if (config.cors === false && Array.isArray(config.urls)) { if (config.cors === false && Array.isArray(config.urls)) {
groupedInstances.api.push(...config.urls); groupedInstances.api.push(...config.urls);
} }
@ -122,7 +122,7 @@ export const apiSettings = {
} }
return data; return data;
} catch (e) { } catch {
return { speeds: {}, timestamp: Date.now() }; return { speeds: {}, timestamp: Date.now() };
} }
}, },
@ -141,7 +141,7 @@ export const apiSettings = {
try { try {
localStorage.setItem(this.SPEED_TEST_CACHE_KEY, JSON.stringify(currentCache)); localStorage.setItem(this.SPEED_TEST_CACHE_KEY, JSON.stringify(currentCache));
} catch (e) { } catch {
console.warn('[SpeedTest] Failed to cache results'); console.warn('[SpeedTest] Failed to cache results');
} }
@ -253,7 +253,7 @@ export const recentActivityManager = {
if (!parsed.playlists) parsed.playlists = []; if (!parsed.playlists) parsed.playlists = [];
if (!parsed.mixes) parsed.mixes = []; if (!parsed.mixes) parsed.mixes = [];
return parsed; return parsed;
} catch (e) { } catch {
return { artists: [], albums: [], playlists: [], mixes: [] }; return { artists: [], albums: [], playlists: [], mixes: [] };
} }
}, },
@ -311,7 +311,7 @@ export const themeManager = {
getTheme() { getTheme() {
try { try {
return localStorage.getItem(this.STORAGE_KEY) || 'system'; return localStorage.getItem(this.STORAGE_KEY) || 'system';
} catch (e) { } catch {
return 'system'; return 'system';
} }
}, },
@ -343,7 +343,7 @@ export const themeManager = {
try { try {
const stored = localStorage.getItem(this.CUSTOM_THEME_KEY); const stored = localStorage.getItem(this.CUSTOM_THEME_KEY);
return stored ? JSON.parse(stored) : null; return stored ? JSON.parse(stored) : null;
} catch (e) { } catch {
return null; return null;
} }
}, },
@ -363,13 +363,10 @@ export const themeManager = {
}; };
export const lastFMStorage = { export const lastFMStorage = {
STORAGE_KEY: 'lastfm-enabled',
LOVE_ON_LIKE_KEY: 'lastfm-love-on-like',
isEnabled() { isEnabled() {
try { try {
return localStorage.getItem(this.STORAGE_KEY) === 'true'; return localStorage.getItem(this.STORAGE_KEY) === 'true';
} catch (e) { } catch {
return false; return false;
} }
}, },
@ -381,7 +378,7 @@ export const lastFMStorage = {
shouldLoveOnLike() { shouldLoveOnLike() {
try { try {
return localStorage.getItem(this.LOVE_ON_LIKE_KEY) === 'true'; return localStorage.getItem(this.LOVE_ON_LIKE_KEY) === 'true';
} catch (e) { } catch {
return false; return false;
} }
}, },
@ -397,7 +394,7 @@ export const nowPlayingSettings = {
getMode() { getMode() {
try { try {
return localStorage.getItem(this.STORAGE_KEY) || 'cover'; return localStorage.getItem(this.STORAGE_KEY) || 'cover';
} catch (e) { } catch {
return 'cover'; return 'cover';
} }
}, },
@ -413,7 +410,7 @@ export const lyricsSettings = {
shouldDownloadLyrics() { shouldDownloadLyrics() {
try { try {
return localStorage.getItem(this.DOWNLOAD_WITH_TRACKS) === 'true'; return localStorage.getItem(this.DOWNLOAD_WITH_TRACKS) === 'true';
} catch (e) { } catch {
return false; return false;
} }
}, },
@ -430,7 +427,7 @@ export const backgroundSettings = {
try { try {
// Default to true if not set // Default to true if not set
return localStorage.getItem(this.STORAGE_KEY) !== 'false'; return localStorage.getItem(this.STORAGE_KEY) !== 'false';
} catch (e) { } catch {
return true; return true;
} }
}, },
@ -448,7 +445,7 @@ export const trackListSettings = {
const mode = localStorage.getItem(this.STORAGE_KEY) || 'dropdown'; const mode = localStorage.getItem(this.STORAGE_KEY) || 'dropdown';
document.documentElement.setAttribute('data-track-actions-mode', mode); document.documentElement.setAttribute('data-track-actions-mode', mode);
return mode; return mode;
} catch (e) { } catch {
return 'dropdown'; return 'dropdown';
} }
}, },
@ -467,7 +464,7 @@ export const cardSettings = {
try { try {
const val = localStorage.getItem(this.COMPACT_ARTIST_KEY); const val = localStorage.getItem(this.COMPACT_ARTIST_KEY);
return val === null ? true : val === 'true'; return val === null ? true : val === 'true';
} catch (e) { } catch {
return true; return true;
} }
}, },
@ -479,7 +476,7 @@ export const cardSettings = {
isCompactAlbum() { isCompactAlbum() {
try { try {
return localStorage.getItem(this.COMPACT_ALBUM_KEY) === 'true'; return localStorage.getItem(this.COMPACT_ALBUM_KEY) === 'true';
} catch (e) { } catch {
return false; return false;
} }
}, },
@ -512,7 +509,7 @@ export const downloadQualitySettings = {
getQuality() { getQuality() {
try { try {
return localStorage.getItem(this.STORAGE_KEY) || 'LOSSLESS'; return localStorage.getItem(this.STORAGE_KEY) || 'LOSSLESS';
} catch (e) { } catch {
return 'LOSSLESS'; return 'LOSSLESS';
} }
}, },
@ -527,7 +524,7 @@ export const waveformSettings = {
isEnabled() { isEnabled() {
try { try {
return localStorage.getItem(this.STORAGE_KEY) === 'true'; return localStorage.getItem(this.STORAGE_KEY) === 'true';
} catch (e) { } catch {
return false; return false;
} }
}, },
@ -543,7 +540,7 @@ export const smoothScrollingSettings = {
isEnabled() { isEnabled() {
try { try {
return localStorage.getItem(this.STORAGE_KEY) === 'true'; return localStorage.getItem(this.STORAGE_KEY) === 'true';
} catch (e) { } catch {
return false; return false;
} }
}, },
@ -560,7 +557,7 @@ export const queueManager = {
try { try {
const data = localStorage.getItem(this.STORAGE_KEY); const data = localStorage.getItem(this.STORAGE_KEY);
return data ? JSON.parse(data) : null; return data ? JSON.parse(data) : null;
} catch (e) { } catch {
return null; return null;
} }
}, },

View file

@ -5,7 +5,6 @@ import {
SVG_HEART, SVG_HEART,
SVG_DOWNLOAD, SVG_DOWNLOAD,
formatTime, formatTime,
trackDataStore,
getTrackTitle, getTrackTitle,
getTrackArtists, getTrackArtists,
escapeHtml, escapeHtml,
@ -158,7 +157,7 @@ export function initializeUIInteractions(player, api) {
try { try {
let addedCount = 0; let addedCount = 0;
for (const track of currentQueue) { for (const track of currentQueue) {
const playlist = await db.addTrackToPlaylist(playlistId, track); await db.addTrackToPlaylist(playlistId, track);
addedCount++; addedCount++;
} }
@ -302,7 +301,6 @@ export function initializeUIInteractions(player, api) {
trackMixItem.style.display = hasMix ? 'block' : 'none'; trackMixItem.style.display = hasMix ? 'block' : 'none';
} }
const rect = item.getBoundingClientRect();
const menuWidth = 150; const menuWidth = 150;
const menuHeight = 200; const menuHeight = 200;
@ -325,7 +323,7 @@ export function initializeUIInteractions(player, api) {
} }
}); });
item.addEventListener('dragstart', (e) => { item.addEventListener('dragstart', () => {
draggedQueueIndex = index; draggedQueueIndex = index;
item.style.opacity = '0.5'; item.style.opacity = '0.5';
}); });

View file

@ -15,8 +15,9 @@ import {
escapeHtml, escapeHtml,
} from './utils.js'; } from './utils.js';
import { openLyricsPanel } from './lyrics.js'; import { openLyricsPanel } from './lyrics.js';
import { recentActivityManager, backgroundSettings, trackListSettings, cardSettings } from './storage.js'; import { recentActivityManager, backgroundSettings, cardSettings } from './storage.js';
import { db } from './db.js'; import { db } from './db.js';
import { showNotification } from './downloads.js';
import { getVibrantColorFromImage } from './vibrant-color.js'; import { getVibrantColorFromImage } from './vibrant-color.js';
import { syncManager } from './accounts/pocketbase.js'; import { syncManager } from './accounts/pocketbase.js';
@ -68,7 +69,7 @@ export class UIRenderer {
this.vibrantColorCache.set(url, null); this.vibrantColorCache.set(url, null);
this.resetVibrantColor(); this.resetVibrantColor();
} }
} catch (e) { } catch {
this.vibrantColorCache.set(url, null); this.vibrantColorCache.set(url, null);
this.resetVibrantColor(); this.resetVibrantColor();
} }
@ -161,7 +162,6 @@ export class UIRenderer {
} }
createTrackItemHTML(track, index, showCover = false, hasMultipleDiscs = false) { createTrackItemHTML(track, index, showCover = false, hasMultipleDiscs = false) {
const playIconSmall = SVG_PLAY;
const trackImageHTML = showCover const trackImageHTML = showCover
? `<img src="${this.api.getCoverUrl(track.album?.cover)}" alt="Track Cover" class="track-item-cover" loading="lazy">` ? `<img src="${this.api.getCoverUrl(track.album?.cover)}" alt="Track Cover" class="track-item-cover" loading="lazy">`
: ''; : '';
@ -625,7 +625,6 @@ export class UIRenderer {
const title = document.getElementById('fullscreen-track-title'); const title = document.getElementById('fullscreen-track-title');
const artist = document.getElementById('fullscreen-track-artist'); const artist = document.getElementById('fullscreen-track-artist');
const nextTrackEl = document.getElementById('fullscreen-next-track'); const nextTrackEl = document.getElementById('fullscreen-next-track');
const lyricsContainer = document.getElementById('fullscreen-lyrics-container');
const lyricsToggleBtn = document.getElementById('toggle-fullscreen-lyrics-btn'); const lyricsToggleBtn = document.getElementById('toggle-fullscreen-lyrics-btn');
const coverUrl = this.api.getCoverUrl(track.album?.cover, '1280'); const coverUrl = this.api.getCoverUrl(track.album?.cover, '1280');

View file

@ -288,7 +288,7 @@ export async function getCoverBlob(api, coverId) {
return blob; return blob;
} }
} }
} catch (e) { } catch {
// Network error (CORS rejection not handled by SW), try proxy // Network error (CORS rejection not handled by SW), try proxy
const url = api.getCoverUrl(coverId, '1280'); const url = api.getCoverUrl(coverId, '1280');
const blob = await fetchWithProxy(url); const blob = await fetchWithProxy(url);

View file

@ -1,3 +1,4 @@
/* stylelint-disable no-descending-specificity */
:root { :root {
color-scheme: light dark; color-scheme: light dark;
@ -14,6 +15,8 @@
--shadow-lg: 0 10px 30px rgb(0, 0, 0, 0.5); --shadow-lg: 0 10px 30px rgb(0, 0, 0, 0.5);
--shadow-xl: 0 20px 60px rgb(0, 0, 0, 0.8); --shadow-xl: 0 20px 60px rgb(0, 0, 0, 0.8);
--cover-filter: blur(50px) brightness(0.4); --cover-filter: blur(50px) brightness(0.4);
--player-bar-height-desktop: 90px;
--player-bar-height-mobile: 130px;
} }
:root[data-theme='monochrome'] { :root[data-theme='monochrome'] {
@ -408,6 +411,17 @@ kbd {
cursor: pointer; cursor: pointer;
} }
.search-bar svg {
position: absolute;
left: 0.75rem;
top: 50%;
transform: translateY(-50%);
color: var(--muted-foreground);
width: 20px;
height: 20px;
pointer-events: none;
}
.sidebar-nav .nav-item a:hover { .sidebar-nav .nav-item a:hover {
background-color: var(--secondary); background-color: var(--secondary);
color: var(--foreground); color: var(--foreground);
@ -489,19 +503,11 @@ kbd {
.search-bar { .search-bar {
position: relative; position: relative;
width: 100%; width: 80%;
max-width: 400px; max-width: 100%;
} margin-bottom: var(--spacing-xl);
display: flex;
.search-bar svg { align-items: center;
position: absolute;
left: 0.75rem;
top: 50%;
transform: translateY(-50%);
color: var(--muted-foreground);
width: 20px;
height: 20px;
pointer-events: none;
} }
.search-bar input { .search-bar input {
@ -534,10 +540,10 @@ body.has-page-background .track-item:hover {
.page.active { .page.active {
display: block; display: block;
animation: fadeIn 0.25s cubic-bezier(0.4, 0, 0.2, 1); animation: fade-in 0.25s cubic-bezier(0.4, 0, 0.2, 1);
} }
@keyframes fadeIn { @keyframes fade-in {
from { from {
opacity: 0; opacity: 0;
transform: translateY(4px); transform: translateY(4px);
@ -549,7 +555,7 @@ body.has-page-background .track-item:hover {
} }
} }
@keyframes scaleIn { @keyframes scale-in {
from { from {
opacity: 0; opacity: 0;
transform: scale(0.95); transform: scale(0.95);
@ -561,7 +567,7 @@ body.has-page-background .track-item:hover {
} }
} }
@keyframes slideIn { @keyframes slide-in {
from { from {
opacity: 0; opacity: 0;
transform: translateX(100%); transform: translateX(100%);
@ -573,7 +579,7 @@ body.has-page-background .track-item:hover {
} }
} }
@keyframes slideOut { @keyframes slide-out {
from { from {
opacity: 1; opacity: 1;
transform: translateX(0); transform: translateX(0);
@ -1000,9 +1006,11 @@ body.has-page-background .track-item:hover {
display: flex; display: flex;
} }
.track-item:hover .track-menu-btn { .track-item:hover .track-menu-btn {
opacity: 1; opacity: 1;
} padding: 0.5rem;
margin: 0;
}
.track-menu-btn:hover { .track-menu-btn:hover {
background-color: rgb(var(--highlight-rgb), 0.2); background-color: rgb(var(--highlight-rgb), 0.2);
@ -1087,7 +1095,7 @@ body.has-page-background .track-item:hover {
align-items: center; align-items: center;
gap: 1rem; gap: 1rem;
flex-wrap: wrap; flex-wrap: wrap;
word-break: break-word; overflow-wrap: break-word;
} }
.detail-header-info .title.long-title { .detail-header-info .title.long-title {
@ -1547,9 +1555,10 @@ input:checked + .slider::before {
.volume-controls { .volume-controls {
display: flex; display: flex;
justify-content: flex-end; justify-content: center !important;
align-items: center; align-items: flex-end !important;
gap: 0.75rem; gap: 0.5rem !important;
flex-direction: column !important;
} }
.volume-controls button { .volume-controls button {
@ -1721,15 +1730,13 @@ input:checked + .slider::before {
.fullscreen-cover-content { .fullscreen-cover-content {
display: flex; display: flex;
flex-direction: column; flex-direction: row;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 100%; width: 100%;
height: 100%; height: 100%;
/* Remove fixed padding to allow flex centering to work within the overlay's padded box */
padding: 1rem;
position: relative; position: relative;
padding: 1rem;
} }
#close-fullscreen-cover-btn { #close-fullscreen-cover-btn {
@ -1781,7 +1788,7 @@ input:checked + .slider::before {
font-weight: 700; font-weight: 700;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
color: var(--foreground); color: var(--foreground);
word-break: break-word; overflow-wrap: break-word;
} }
#fullscreen-track-artist { #fullscreen-track-artist {
@ -1825,7 +1832,7 @@ input:checked + .slider::before {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
box-shadow: var(--shadow-xl); box-shadow: var(--shadow-xl);
animation: scaleIn 0.2s cubic-bezier(0.4, 0, 0.2, 1); animation: scale-in 0.2s cubic-bezier(0.4, 0, 0.2, 1);
} }
#queue-modal-header { #queue-modal-header {
@ -2317,7 +2324,7 @@ input:checked + .slider::before {
border-radius: var(--radius); border-radius: var(--radius);
padding: 1rem; padding: 1rem;
box-shadow: var(--shadow-lg); box-shadow: var(--shadow-lg);
animation: slideIn 0.3s ease; animation: slide-in 0.3s ease;
min-width: 300px; min-width: 300px;
} }
@ -2419,7 +2426,7 @@ input:checked + .slider::before {
align-items: center; align-items: center;
gap: 1rem; gap: 1rem;
max-width: 350px; max-width: 350px;
animation: slideIn 0.3s ease; animation: slide-in 0.3s ease;
} }
.offline-notification svg { .offline-notification svg {
@ -2511,11 +2518,6 @@ input:checked + .slider::before {
} }
/* Side Panels (Lyrics & Queue) */ /* Side Panels (Lyrics & Queue) */
:root {
--player-bar-height-desktop: 90px;
--player-bar-height-mobile: 130px;
}
.side-panel { .side-panel {
position: fixed; position: fixed;
right: 0; right: 0;
@ -2756,12 +2758,6 @@ input:checked + .slider::before {
} }
/* Updated Volume Controls Layout */ /* Updated Volume Controls Layout */
.volume-controls {
flex-direction: column !important;
align-items: flex-end !important;
gap: 0.5rem !important;
justify-content: center !important;
}
.player-actions-row { .player-actions-row {
display: flex; display: flex;
@ -2864,21 +2860,6 @@ img[src=''] {
padding: 1rem 0; padding: 1rem 0;
} }
.search-bar {
display: flex;
align-items: center;
width: 80%;
max-width: 100%;
}
.fullscreen-cover-content {
display: flex;
flex-direction: row;
width: 100%;
height: 100%;
position: relative;
}
.fullscreen-main-view { .fullscreen-main-view {
flex: 1; flex: 1;
display: flex; display: flex;
@ -2945,7 +2926,7 @@ img[src=''] {
max-width: 400px; max-width: 400px;
width: 90%; width: 90%;
box-shadow: var(--shadow-xl); box-shadow: var(--shadow-xl);
animation: scaleIn 0.2s ease; animation: scale-in 0.2s ease;
max-height: 90vh; max-height: 90vh;
overflow-y: auto; overflow-y: auto;
} }
@ -3013,7 +2994,7 @@ img[src=''] {
text-align: center; text-align: center;
} }
@keyframes scaleIn { @keyframes scale-in {
from { from {
transform: scale(0.95); transform: scale(0.95);
opacity: 0; opacity: 0;
@ -3027,13 +3008,13 @@ img[src=''] {
#playlist-modal { #playlist-modal {
opacity: 1; opacity: 1;
animation-name: fadeInOpacity; animation-name: fade-in-opacity;
animation-iteration-count: 1; animation-iteration-count: 1;
animation-timing-function: ease-in; animation-timing-function: ease-in;
animation-duration: 0.1s; animation-duration: 0.1s;
} }
@keyframes fadeInOpacity { @keyframes fade-in-opacity {
0% { 0% {
opacity: 0; opacity: 0;
} }
@ -3056,7 +3037,7 @@ img[src=''] {
z-index: 10001; z-index: 10001;
max-width: 400px; max-width: 400px;
min-width: 350px; min-width: 350px;
animation: slideIn 0.3s ease; animation: slide-in 0.3s ease;
} }
.csv-import-progress .progress-header { .csv-import-progress .progress-header {
@ -3349,6 +3330,9 @@ img[src=''] {
@media (max-width: 768px) { @media (max-width: 768px) {
.player-controls .progress-container { .player-controls .progress-container {
order: -1; order: -1;
max-width: none;
font-size: 0.75rem;
gap: 0.5rem;
} }
#fullscreen-cover-overlay { #fullscreen-cover-overlay {
@ -3556,12 +3540,6 @@ img[src=''] {
height: 32px; height: 32px;
} }
.player-controls .progress-container {
max-width: none;
font-size: 0.75rem;
gap: 0.5rem;
}
.desktop-only { .desktop-only {
display: none !important; display: none !important;
} }
@ -3632,11 +3610,6 @@ img[src=''] {
white-space: nowrap; white-space: nowrap;
} }
.track-menu-btn {
padding: 0.5rem;
margin: 0;
}
.track-menu-btn svg { .track-menu-btn svg {
width: 18px; width: 18px;
height: 18px; height: 18px;
@ -3776,10 +3749,6 @@ img[src=''] {
.mobile-only { .mobile-only {
display: flex !important; display: flex !important;
} }
.desktop-only {
display: none !important;
}
} }
@media (max-width: 480px) { @media (max-width: 480px) {
@ -3922,6 +3891,7 @@ img[src=''] {
} }
} }
/* stylelint-disable-next-line media-feature-name-value-no-unknown */
@media (display-mode: window-controls-overlay) { @media (display-mode: window-controls-overlay) {
.app-container { .app-container {
margin-top: env(titlebar-area-height, 0); margin-top: env(titlebar-area-height, 0);