feat(settings): add disable blur effects preference

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
This commit is contained in:
Kp7700andarena-agent authored and edideaur committed 2026-09-29 11:30:34 +03:00
1 parent 5588719727
commit 5b1e6ef9b2
6 files changed
+133

No files matched your search

+14
View File
@@ -114,6 +114,10 @@
<script>
(() => {
try {
if (localStorage.getItem('disable-blur-effects') === 'true') {
document.documentElement.classList.add('disable-blur-effects');
}
const theme = localStorage.getItem('monochrome-theme');
const css = localStorage.getItem('custom_theme_css');
const palette = localStorage.getItem('monochrome-custom-theme');
@@ -3211,6 +3215,16 @@
<span class="slider"></span>
</label>
</div>
<div class="setting-item">
<div class="info">
<span class="label">Disable Blur Effects</span>
<span class="description">Turn off visual blur effects throughout the app</span>
</div>
<label class="toggle-switch">
<input type="checkbox" id="disable-blur-effects-toggle" />
<span class="slider"></span>
</label>
</div>
<div class="setting-item">
<div class="info">
<span class="label">Dynamic Colors</span>
+12
View File
@@ -10,6 +10,17 @@ import {
SVG_GLOBE,
} from './icons.js';
import { sidePanelManager } from './side-panel.js';
import { disableBlurEffectsSettings } from './storage.js';
function applyBlurEffectsPreferenceToLyrics() {
// am-lyrics renders its blur styles inside a shadow root.
const disabled = disableBlurEffectsSettings.isEnabled();
document.querySelectorAll('am-lyrics').forEach((element) => {
element.toggleAttribute('no-blur', disabled);
});
}
window.addEventListener('disable-blur-effects-changed', applyBlurEffectsPreferenceToLyrics);
const loadAmLyrics = () => {
const images = Array.from(document.images).filter((img) => !img.complete);
@@ -1213,6 +1224,7 @@ async function renderLyricsComponent(container, track, audioPlayer, lyricsManage
amLyrics.setAttribute('hover-background-color', 'color-mix(in srgb, var(--primary) 16%, transparent)');
amLyrics.setAttribute('autoscroll', '');
amLyrics.setAttribute('interpolate', '');
amLyrics.toggleAttribute('no-blur', disableBlurEffectsSettings.isEnabled());
if (trackIsJapanese(track)) amLyrics.setAttribute('lang', 'ja');
amLyrics.style.height = '100%';
amLyrics.style.width = '100%';
+11
View File
@@ -6,6 +6,7 @@ import {
fullscreenCoverClickSettings,
lyricsSettings,
backgroundSettings,
disableBlurEffectsSettings,
dynamicColorSettings,
cardSettings,
artistBannerSettings,
@@ -5882,6 +5883,16 @@ export async function initializeSettings(scrobbler, player, api, ui) {
});
}
// Disable Blur Effects Toggle
disableBlurEffectsSettings.apply();
const disableBlurEffectsToggle = document.getElementById('disable-blur-effects-toggle');
if (disableBlurEffectsToggle) {
disableBlurEffectsToggle.checked = disableBlurEffectsSettings.isEnabled();
disableBlurEffectsToggle.addEventListener('change', (e) => {
disableBlurEffectsSettings.setEnabled(e.target.checked);
});
}
// Dynamic Color Toggle
const dynamicColorToggle = document.getElementById('dynamic-color-toggle');
if (dynamicColorToggle) {
+28
View File
@@ -564,6 +564,34 @@ export const backgroundSettings = {
},
};
export const disableBlurEffectsSettings = {
STORAGE_KEY: 'disable-blur-effects',
ROOT_CLASS: 'disable-blur-effects',
isEnabled() {
try {
return localStorage.getItem(this.STORAGE_KEY) === 'true';
} catch {
return false;
}
},
apply() {
const enabled = this.isEnabled();
const root = typeof document === 'undefined' ? null : document.documentElement;
root?.classList.toggle(this.ROOT_CLASS, enabled);
if (typeof window !== 'undefined') {
window.dispatchEvent(new CustomEvent('disable-blur-effects-changed', { detail: { enabled } }));
}
},
setEnabled(enabled) {
localStorage.setItem(this.STORAGE_KEY, enabled ? 'true' : 'false');
this.apply();
},
};
export const dynamicColorSettings = {
STORAGE_KEY: 'dynamic-color-enabled',
+37
View File
@@ -5,6 +5,7 @@ import {
lastFMStorage,
nowPlayingSettings,
gaplessPlaybackSettings,
disableBlurEffectsSettings,
exponentialVolumeSettings,
audioEffectsSettings,
silenceRemovalSettings,
@@ -15,6 +16,7 @@ import {
describe('storage.js', () => {
beforeEach(() => {
localStorage.clear();
document.documentElement.classList.remove(disableBlurEffectsSettings.ROOT_CLASS);
vi.clearAllMocks();
});
@@ -93,6 +95,41 @@ describe('storage.js', () => {
});
});
describe('disableBlurEffectsSettings', () => {
test('defaults to disabled and applies its persisted state to the document', () => {
expect(disableBlurEffectsSettings.isEnabled()).toBe(false);
expect(document.documentElement.classList.contains(disableBlurEffectsSettings.ROOT_CLASS)).toBe(false);
disableBlurEffectsSettings.setEnabled(true);
expect(localStorage.getItem(disableBlurEffectsSettings.STORAGE_KEY)).toBe('true');
expect(document.documentElement.classList.contains(disableBlurEffectsSettings.ROOT_CLASS)).toBe(true);
disableBlurEffectsSettings.setEnabled(false);
expect(localStorage.getItem(disableBlurEffectsSettings.STORAGE_KEY)).toBe('false');
expect(document.documentElement.classList.contains(disableBlurEffectsSettings.ROOT_CLASS)).toBe(false);
});
test('restores the document class from a persisted preference', () => {
localStorage.setItem(disableBlurEffectsSettings.STORAGE_KEY, 'true');
disableBlurEffectsSettings.apply();
expect(document.documentElement.classList.contains(disableBlurEffectsSettings.ROOT_CLASS)).toBe(true);
});
test('notifies blur consumers when the preference changes', () => {
let detail;
const onChange = (event) => {
detail = event.detail;
};
window.addEventListener('disable-blur-effects-changed', onChange);
disableBlurEffectsSettings.setEnabled(true);
expect(detail).toEqual({ enabled: true });
window.removeEventListener('disable-blur-effects-changed', onChange);
});
});
describe('silence removal and crossfade settings', () => {
test('defaults to silence removal on and crossfade off', () => {
expect(silenceRemovalSettings.isEnabled()).toBe(true);
+31
View File
@@ -12004,3 +12004,34 @@ body.sidebar-collapsed #sidebar-nav-donate {
line-height: 1.45;
color: var(--muted-foreground);
}
/* Disable Blur Effects preference */
:root.disable-blur-effects,
:root.disable-blur-effects *,
:root.disable-blur-effects *::before,
:root.disable-blur-effects *::after {
/* stylelint-disable-next-line property-no-vendor-prefix */
-webkit-backdrop-filter: none !important;
backdrop-filter: none !important;
}
/* Keep the album background's existing color treatment while removing its blur. */
:root.disable-blur-effects #page-background {
filter: brightness(0.4) !important;
}
:root.disable-blur-effects[data-theme='white'] #page-background {
filter: brightness(1.6) opacity(0.35) !important;
}
/* Preserve the visualizer's saturation and brightness when its CSS blur is disabled. */
:root.disable-blur-effects #fullscreen-cover-overlay:not(.ui-hidden) #visualizer-container {
filter: saturate(0.9) brightness(0.8) !important;
}
:root.disable-blur-effects .synced-line,
:root.disable-blur-effects .synced-line.active,
:root.disable-blur-effects .synced-line.upcoming,
:root.disable-blur-effects .synced-line.past {
filter: none !important;
}