Full-screen Visualizer
diff --git a/js/settings.js b/js/settings.js
index 53a3270..2d8d8dc 100644
--- a/js/settings.js
+++ b/js/settings.js
@@ -5,6 +5,7 @@ import {
nowPlayingSettings,
lyricsSettings,
backgroundSettings,
+ dynamicColorSettings,
cardSettings,
waveformSettings,
replayGainSettings,
@@ -779,6 +780,19 @@ export function initializeSettings(scrobbler, player, api, ui) {
});
}
+ // Dynamic Color Toggle
+ const dynamicColorToggle = document.getElementById('dynamic-color-toggle');
+ if (dynamicColorToggle) {
+ dynamicColorToggle.checked = dynamicColorSettings.isEnabled();
+ dynamicColorToggle.addEventListener('change', (e) => {
+ dynamicColorSettings.setEnabled(e.target.checked);
+ if (!e.target.checked) {
+ // Reset colors immediately when disabled
+ window.dispatchEvent(new CustomEvent('reset-dynamic-color'));
+ }
+ });
+ }
+
// Waveform Toggle
const waveformToggle = document.getElementById('waveform-toggle');
if (waveformToggle) {
diff --git a/js/storage.js b/js/storage.js
index f76cbbc..f545a4b 100644
--- a/js/storage.js
+++ b/js/storage.js
@@ -237,7 +237,7 @@ export const themeManager = {
if (theme === 'system') {
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
- document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
+ document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'white');
} else {
document.documentElement.setAttribute('data-theme', theme);
}
@@ -371,6 +371,23 @@ export const backgroundSettings = {
},
};
+export const dynamicColorSettings = {
+ STORAGE_KEY: 'dynamic-color-enabled',
+
+ isEnabled() {
+ try {
+ // Default to true if not set
+ return localStorage.getItem(this.STORAGE_KEY) !== 'false';
+ } catch {
+ return true;
+ }
+ },
+
+ setEnabled(enabled) {
+ localStorage.setItem(this.STORAGE_KEY, enabled ? 'true' : 'false');
+ },
+};
+
export const cardSettings = {
COMPACT_ARTIST_KEY: 'card-compact-artist',
COMPACT_ALBUM_KEY: 'card-compact-album',
@@ -1155,7 +1172,7 @@ export const sidebarSectionSettings = {
if (typeof window !== 'undefined' && window.matchMedia) {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
if (themeManager.getTheme() === 'system') {
- document.documentElement.setAttribute('data-theme', e.matches ? 'dark' : 'light');
+ document.documentElement.setAttribute('data-theme', e.matches ? 'dark' : 'white');
}
});
}
@@ -1168,10 +1185,10 @@ export const fontSettings = {
getDefaultConfig() {
return {
- type: 'preset',
- family: 'Inter',
- fallback: 'sans-serif',
- weights: [400, 500, 600, 700, 800],
+ type: 'google',
+ family: 'IBM Plex Mono',
+ fallback: 'monospace',
+ weights: [100, 200, 300, 400, 500, 600, 700],
};
},
diff --git a/js/ui.js b/js/ui.js
index b4a5426..0903a57 100644
--- a/js/ui.js
+++ b/js/ui.js
@@ -23,9 +23,11 @@ import { openLyricsPanel } from './lyrics.js';
import {
recentActivityManager,
backgroundSettings,
+ dynamicColorSettings,
cardSettings,
visualizerSettings,
homePageSettings,
+ fontSettings,
} from './storage.js';
import { db } from './db.js';
import { getVibrantColorFromImage } from './vibrant-color.js';
@@ -42,7 +44,6 @@ import {
createProjectCardHTML,
createTrackFromSong,
} from './tracker.js';
-import { fontSettings } from './storage.js';
fontSettings.applyFont();
@@ -85,6 +86,11 @@ export class UIRenderer {
this.searchAbortController = null;
this.vibrantColorCache = new Map();
this.visualizer = null;
+
+ // Listen for dynamic color reset events
+ window.addEventListener('reset-dynamic-color', () => {
+ this.resetVibrantColor();
+ });
}
// Helper for Heart Icon
@@ -96,7 +102,13 @@ export class UIRenderer {
}
async extractAndApplyColor(url) {
- if (!backgroundSettings.isEnabled() || !url) {
+ if (!url) {
+ this.resetVibrantColor();
+ return;
+ }
+
+ // Check if dynamic coloring is enabled
+ if (!dynamicColorSettings.isEnabled()) {
this.resetVibrantColor();
return;
}
@@ -695,7 +707,7 @@ export class UIRenderer {
const root = document.documentElement;
const theme = root.getAttribute('data-theme');
- const isLightMode = theme === 'light';
+ const isLightMode = theme === 'white';
let hex = color.replace('#', '');
// Handle shorthand hex
diff --git a/js/visualizers/lcd.js b/js/visualizers/lcd.js
index 6382288..475904c 100644
--- a/js/visualizers/lcd.js
+++ b/js/visualizers/lcd.js
@@ -190,7 +190,7 @@ export class LCDPreset {
const { kick, primaryColor, mode } = params;
this.primaryColor = primaryColor;
- const isDark = document.documentElement.getAttribute('data-theme') !== 'light';
+ const isDark = document.documentElement.getAttribute('data-theme') !== 'white';
// --- Background ---
ctx.clearRect(0, 0, width, height);
diff --git a/js/visualizers/particles.js b/js/visualizers/particles.js
index be2d535..527a817 100644
--- a/js/visualizers/particles.js
+++ b/js/visualizers/particles.js
@@ -18,7 +18,7 @@ export class ParticlesPreset {
const { width, height } = canvas;
const { kick, intensity, primaryColor, mode } = params;
const sensitivity = params.sensitivity || 1.0;
- const isDark = document.documentElement.getAttribute('data-theme') !== 'light';
+ const isDark = document.documentElement.getAttribute('data-theme') !== 'white';
// Clear background
ctx.clearRect(0, 0, width, height);
diff --git a/js/visualizers/unknown_pleasures_webgl.js b/js/visualizers/unknown_pleasures_webgl.js
index 491c993..a43e85a 100644
--- a/js/visualizers/unknown_pleasures_webgl.js
+++ b/js/visualizers/unknown_pleasures_webgl.js
@@ -457,7 +457,7 @@ export class UnknownPleasuresWebGL {
draw(ctx, canvas, analyser, dataArray, params) {
const gl = ctx;
const { width, height } = canvas;
- const isDark = document.documentElement.getAttribute('data-theme') !== 'light';
+ const isDark = document.documentElement.getAttribute('data-theme') !== 'white';
// Set CSS blend mode based on mode and theme
// Solid: normal (opaque background)
diff --git a/styles.css b/styles.css
index 5d51d83..711283e 100644
--- a/styles.css
+++ b/styles.css
@@ -43,23 +43,23 @@
:root[data-theme='monochrome'] {
color-scheme: dark;
- --background: #000;
- --foreground: #fafafa;
- --card: #111;
- --card-foreground: #fafafa;
- --primary: #fafafa;
- --primary-foreground: #111;
- --secondary: #27272a;
- --secondary-foreground: #fafafa;
- --muted: #27272a;
- --muted-foreground: #a1a1aa;
- --border: #27272a;
- --input: #27272a;
- --ring: #fafafa;
- --highlight: #fff;
- --highlight-rgb: 255, 255, 255;
+ --background: #0a0a0a;
+ --foreground: #f5f5f5;
+ --card: #141414;
+ --card-foreground: #f5f5f5;
+ --primary: #f5f5f5;
+ --primary-foreground: #0a0a0a;
+ --secondary: #1f1f1f;
+ --secondary-foreground: #e0e0e0;
+ --muted: #1f1f1f;
+ --muted-foreground: #a0a0a0;
+ --border: #2a2a2a;
+ --input: #1f1f1f;
+ --ring: #f5f5f5;
+ --highlight: #f5f5f5;
+ --highlight-rgb: 245, 245, 245;
--active-highlight: var(--highlight);
- --explicit-badge: #fafafa;
+ --explicit-badge: #f5f5f5;
}
:root[data-theme='dark'] {
@@ -231,33 +231,33 @@
--muted-foreground: #6c6f85;
--border: #ccd0da;
--input: #bcc0cc;
- --ring: #1e66f5;
+ --ring: #fdfdfd;
--highlight: #1e66f5;
--highlight-rgb: #7287fd;
--active-highlight: #7287fd;
--explicit-badge: #df8e1d;
}
-:root[data-theme='light'] {
+:root[data-theme='white'] {
color-scheme: light;
- --background: #fff;
- --foreground: #000;
- --card: #f4f4f5;
- --card-foreground: #000;
- --primary: #2563eb;
- --primary-foreground: #fff;
- --secondary: #e4e4e7;
- --secondary-foreground: #000;
- --muted: #e4e4e7;
- --muted-foreground: #62626a;
- --border: #e4e4e7;
- --input: #e4e4e7;
- --ring: #2563eb;
- --highlight: #2563eb;
- --highlight-rgb: 37, 99, 235;
+ --background: #f5f5f5;
+ --foreground: #1a1a1a;
+ --card: #e8e8e8;
+ --card-foreground: #1a1a1a;
+ --primary: #1a1a1a;
+ --primary-foreground: #f5f5f5;
+ --secondary: #ddd;
+ --secondary-foreground: #2a2a2a;
+ --muted: #e0e0e0;
+ --muted-foreground: #555;
+ --border: #ccc;
+ --input: #e0e0e0;
+ --ring: #1a1a1a;
+ --highlight: #1a1a1a;
+ --highlight-rgb: 26, 26, 26;
--active-highlight: var(--highlight);
- --explicit-badge: #f58a8a;
+ --explicit-badge: #1a1a1a;
--cover-filter: blur(50px) brightness(1.6) opacity(0.35);
}
@@ -268,7 +268,7 @@
margin: 0;
padding: 0;
-webkit-tap-highlight-color: transparent;
- font-family: var(--font-family, 'Inter', sans-serif) !important;
+ font-family: var(--font-family, 'IBM Plex Mono', monospace) !important;
}
html {
@@ -281,7 +281,7 @@ html {
body {
background-color: var(--background);
color: var(--foreground);
- font-family: var(--font-family, 'Inter', sans-serif) !important;
+ font-family: var(--font-family, 'IBM Plex Mono', monospace) !important;
overflow: hidden;
transition:
background-color 0.3s ease,
@@ -391,7 +391,7 @@ kbd {
}
/* Light mode adjustments */
-:root[data-theme='light'] #page-background {
+:root[data-theme='white'] #page-background {
mask-image: linear-gradient(to bottom, rgb(0, 0, 0, 1) 0%, rgb(0, 0, 0, 0) 100%);
}
@@ -423,7 +423,7 @@ kbd {
animation: slide-up var(--transition-slow) var(--ease-out-back);
}
-:root[data-theme='light'] .now-playing-bar {
+:root[data-theme='white'] .now-playing-bar {
background-color: color-mix(in srgb, var(--card) 80%, transparent);
border-color: rgb(0, 0, 0, 0.1);
}