diff --git a/spotizerr-ui/dev-dist/sw.js b/spotizerr-ui/dev-dist/sw.js index 186f65d..b2596b7 100644 --- a/spotizerr-ui/dev-dist/sw.js +++ b/spotizerr-ui/dev-dist/sw.js @@ -82,7 +82,7 @@ define(['./workbox-f70c5944'], (function (workbox) { 'use strict'; "revision": "3ca0b8505b4bec776b69afdba2768812" }, { "url": "index.html", - "revision": "0.qqj3c2rpjk8" + "revision": "0.ef94id05f1c" }], {}); workbox.cleanupOutdatedCaches(); workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { diff --git a/spotizerr-ui/public/apple-touch-icon-180x180.png b/spotizerr-ui/public/apple-touch-icon-180x180.png index 1a80da1..f4c08db 100644 Binary files a/spotizerr-ui/public/apple-touch-icon-180x180.png and b/spotizerr-ui/public/apple-touch-icon-180x180.png differ diff --git a/spotizerr-ui/public/favicon-128x128.png b/spotizerr-ui/public/favicon-128x128.png index 61bb429..acf077f 100644 Binary files a/spotizerr-ui/public/favicon-128x128.png and b/spotizerr-ui/public/favicon-128x128.png differ diff --git a/spotizerr-ui/public/favicon-16x16.png b/spotizerr-ui/public/favicon-16x16.png index 97c1d8d..3277063 100644 Binary files a/spotizerr-ui/public/favicon-16x16.png and b/spotizerr-ui/public/favicon-16x16.png differ diff --git a/spotizerr-ui/public/favicon-256x256.png b/spotizerr-ui/public/favicon-256x256.png index f28e458..1b3195e 100644 Binary files a/spotizerr-ui/public/favicon-256x256.png and b/spotizerr-ui/public/favicon-256x256.png differ diff --git a/spotizerr-ui/public/favicon-32x32.png b/spotizerr-ui/public/favicon-32x32.png index 51c0734..02e9591 100644 Binary files a/spotizerr-ui/public/favicon-32x32.png and b/spotizerr-ui/public/favicon-32x32.png differ diff --git a/spotizerr-ui/public/favicon-48x48.png b/spotizerr-ui/public/favicon-48x48.png index 68d6b27..1ce92da 100644 Binary files a/spotizerr-ui/public/favicon-48x48.png and b/spotizerr-ui/public/favicon-48x48.png differ diff --git a/spotizerr-ui/public/favicon-64x64.png b/spotizerr-ui/public/favicon-64x64.png index 939fcec..3c1762c 100644 Binary files a/spotizerr-ui/public/favicon-64x64.png and b/spotizerr-ui/public/favicon-64x64.png differ diff --git a/spotizerr-ui/public/favicon-96x96.png b/spotizerr-ui/public/favicon-96x96.png index 41d0a36..e8cac38 100644 Binary files a/spotizerr-ui/public/favicon-96x96.png and b/spotizerr-ui/public/favicon-96x96.png differ diff --git a/spotizerr-ui/public/favicon.ico b/spotizerr-ui/public/favicon.ico index 940c2eb..f85f81a 100644 Binary files a/spotizerr-ui/public/favicon.ico and b/spotizerr-ui/public/favicon.ico differ diff --git a/spotizerr-ui/public/pwa-192x192.png b/spotizerr-ui/public/pwa-192x192.png index db1a7a7..f17481e 100644 Binary files a/spotizerr-ui/public/pwa-192x192.png and b/spotizerr-ui/public/pwa-192x192.png differ diff --git a/spotizerr-ui/public/pwa-512x512-maskable.png b/spotizerr-ui/public/pwa-512x512-maskable.png index a42d633..0e81c1b 100644 Binary files a/spotizerr-ui/public/pwa-512x512-maskable.png and b/spotizerr-ui/public/pwa-512x512-maskable.png differ diff --git a/spotizerr-ui/public/pwa-512x512.png b/spotizerr-ui/public/pwa-512x512.png index e159445..bf2d41d 100644 Binary files a/spotizerr-ui/public/pwa-512x512.png and b/spotizerr-ui/public/pwa-512x512.png differ diff --git a/spotizerr-ui/public/spotizerr.png b/spotizerr-ui/public/spotizerr.png index c8a5679..d887aac 100644 Binary files a/spotizerr-ui/public/spotizerr.png and b/spotizerr-ui/public/spotizerr.png differ diff --git a/spotizerr-ui/scripts/generate-icons.js b/spotizerr-ui/scripts/generate-icons.js index 1d8dbe3..8ca901f 100644 --- a/spotizerr-ui/scripts/generate-icons.js +++ b/spotizerr-ui/scripts/generate-icons.js @@ -8,19 +8,71 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const publicDir = join(__dirname, '../public'); -const pngPath = join(publicDir, 'spotizerr.png'); +const svgPath = join(publicDir, 'spotizerr.svg'); async function generateIcons() { try { - // Check if the PNG file exists - if (!existsSync(pngPath)) { - throw new Error(`PNG file not found at ${pngPath}. Please ensure spotizerr.png exists in the public directory.`); + // Check if the SVG file exists + if (!existsSync(svgPath)) { + throw new Error(`SVG file not found at ${svgPath}. Please ensure spotizerr.svg exists in the public directory.`); } - console.log('🎨 Generating PWA icons from PNG...'); + console.log('🎨 Generating PWA icons from SVG...'); - // Since the source is already 1667x1667 (square), we don't need to worry about aspect ratio - const sourceSize = 1667; + // First, convert SVG to PNG and process it + console.log('📐 Processing SVG: converting to PNG, scaling by 0.67, and centering in black box...'); + + // Create a base canvas size for processing + const baseCanvasSize = 1000; + const scaleFactor = 3; + + // Convert SVG to PNG and get its dimensions + const svgToPng = await sharp(svgPath) + .png() + .toBuffer(); + + const svgMetadata = await sharp(svgToPng).metadata(); + const svgWidth = svgMetadata.width; + const svgHeight = svgMetadata.height; + + // Calculate scaled dimensions + const scaledWidth = Math.round(svgWidth * scaleFactor); + const scaledHeight = Math.round(svgHeight * scaleFactor); + + // Calculate centering offsets + const offsetX = Math.round((baseCanvasSize - scaledWidth) / 2); + const offsetY = Math.round((baseCanvasSize - scaledHeight) / 2); + + // Create the processed base image: scale SVG and center in black box + const processedImage = await sharp({ + create: { + width: baseCanvasSize, + height: baseCanvasSize, + channels: 4, + background: { r: 0, g: 0, b: 0, alpha: 1 } // Pure black background + } + }) + .composite([{ + input: await sharp(svgToPng) + .resize(scaledWidth, scaledHeight) + .png() + .toBuffer(), + top: offsetY, + left: offsetX + }]) + .png() + .toBuffer(); + + console.log(`✅ Processed SVG: ${svgWidth}x${svgHeight} → scaled to ${scaledWidth}x${scaledHeight} → centered in ${baseCanvasSize}x${baseCanvasSize} black box`); + + // Save the processed base image for reference + await sharp(processedImage) + .png() + .toFile(join(publicDir, 'spotizerr.png')); + + console.log(`✅ Saved processed base image as spotizerr.png (${baseCanvasSize}x${baseCanvasSize})`); + + const sourceSize = baseCanvasSize; // Define icon configurations const iconConfigs = [ @@ -51,8 +103,8 @@ async function generateIcons() { } ]; - // Load the source PNG - const sourceImage = sharp(pngPath); + // Use the processed image as source + const sourceImage = sharp(processedImage); for (const config of iconConfigs) { const { size, name, padding } = config; @@ -62,13 +114,13 @@ async function generateIcons() { const paddedSize = Math.round(size * (1 - padding * 2)); const offset = Math.round((size - paddedSize) / 2); - // Create a black background and composite the resized logo on top + // Create a pure black background and composite the resized logo on top await sharp({ create: { width: size, height: size, channels: 4, - background: { r: 15, g: 23, b: 42, alpha: 1 } // #0f172a in RGB + background: { r: 0, g: 0, b: 0, alpha: 1 } // Pure black background } }) .composite([{ @@ -100,7 +152,7 @@ async function generateIcons() { width: maskableSize, height: maskableSize, channels: 4, - background: { r: 15, g: 23, b: 42, alpha: 1 } // Solid background for maskable + background: { r: 0, g: 0, b: 0, alpha: 1 } // Pure black background for maskable } }) .composite([{ @@ -125,7 +177,7 @@ async function generateIcons() { width: size, height: size, channels: 4, - background: { r: 15, g: 23, b: 42, alpha: 1 } + background: { r: 0, g: 0, b: 0, alpha: 1 } // Pure black background } }) .composite([{ @@ -154,7 +206,7 @@ async function generateIcons() { width: size, height: size, channels: 4, - background: { r: 15, g: 23, b: 42, alpha: 1 } + background: { r: 0, g: 0, b: 0, alpha: 1 } // Pure black background } }) .composite([{ @@ -186,8 +238,8 @@ async function generateIcons() { }); console.log(' • favicon.ico (multi-size: 16x16, 32x32, 48x48)'); console.log(''); - console.log('💡 The icons are generated with appropriate padding and the dark theme background.'); - console.log('💡 The source PNG already has the perfect background, so no additional styling needed.'); + console.log('💡 Icons generated from SVG source, scaled by 0.67, and centered on pure black backgrounds.'); + console.log('💡 The SVG logo is automatically processed and optimized for all icon formats.'); console.log('💡 favicon.ico contains multiple sizes for optimal browser compatibility.'); } catch (error) { diff --git a/spotizerr-ui/src/components/Queue.tsx b/spotizerr-ui/src/components/Queue.tsx index c9b8dc0..90c3db3 100644 --- a/spotizerr-ui/src/components/Queue.tsx +++ b/spotizerr-ui/src/components/Queue.tsx @@ -1,4 +1,4 @@ -import { useContext } from "react"; +import { useContext, useState, useRef, useEffect } from "react"; import { FaTimes, FaSync, @@ -127,26 +127,27 @@ const QueueItemCard = ({ item }: { item: QueueItem }) => { const progressText = getProgressText(); return ( -
+
{item.name}
+
{item.artist}
{item.albumName && ( -+
{item.albumName}
)} @@ -155,16 +156,16 @@ const QueueItemCard = ({ item }: { item: QueueItem }) => { {item.type === "album" && ( <>
+
{item.name}
+
{item.artist}
{item.currentTrackTitle && ( -+
{item.currentTrackNumber}/{item.totalTracks}: {item.currentTrackTitle}
)} @@ -173,16 +174,16 @@ const QueueItemCard = ({ item }: { item: QueueItem }) => { {item.type === "playlist" && ( <>
+
{item.name}
+
{item.playlistOwner}
{item.currentTrackTitle && ( -+
{item.currentTrackNumber}/{item.totalTracks}: {item.currentTrackTitle}
)} @@ -190,60 +191,62 @@ const QueueItemCard = ({ item }: { item: QueueItem }) => { )}{progressText}
} + {progressText &&{progressText}
}Error: {item.error}
+Error: {item.error}
The queue is empty.
-Downloads will appear here
+ <> + {/* Mobile backdrop overlay */} + + +The queue is empty.
+Downloads will appear here
+