Web changes: Split shared helpers, bootstrap logic, route groups, and upload-sync behavior out of web.js. Kept web.js focused on wiring and server startup. Fixed screen playlist reassignment so changing a screen’s playlist now triggers a refresh. Fixed single-slide playlist refresh behavior so updates do not get stuck behind the current slide. Player changes: Split websocket/runtime handling into runtime.js. Split playlist assembly and revision hashing into playlist.js. Split onboarding and player HTTP routes into dedicated modules. Split render utilities and template loading into render-helpers.js. Kept player.js mostly as startup/orchestration. Validation: Rebuilt both services with Docker Compose. Smoke-checked web and player routes after the refactor. Verified get_errors was clean on the touched modules.
118 lines
3.6 KiB
JavaScript
118 lines
3.6 KiB
JavaScript
(function () {
|
|
var THEME_STORAGE_KEY = 'web-theme';
|
|
|
|
function getPreferredTheme() {
|
|
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
return 'dark';
|
|
}
|
|
return 'light';
|
|
}
|
|
|
|
function getStoredTheme() {
|
|
try {
|
|
var storedTheme = window.localStorage.getItem(THEME_STORAGE_KEY);
|
|
if (storedTheme === 'dark' || storedTheme === 'light') {
|
|
return storedTheme;
|
|
}
|
|
} catch (error) {
|
|
return null;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function setStoredTheme(theme) {
|
|
try {
|
|
window.localStorage.setItem(THEME_STORAGE_KEY, theme);
|
|
} catch (error) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
function applyTheme(theme) {
|
|
var normalizedTheme = theme === 'dark' ? 'dark' : 'light';
|
|
var nextTheme = normalizedTheme === 'dark' ? 'light' : 'dark';
|
|
var nextThemeLabel = nextTheme === 'dark' ? 'Dark mode' : 'Light mode';
|
|
|
|
document.documentElement.dataset.bsTheme = normalizedTheme;
|
|
document.documentElement.style.colorScheme = normalizedTheme;
|
|
|
|
Array.prototype.forEach.call(document.querySelectorAll('[data-theme-toggle]'), function (toggleButton) {
|
|
var icon = toggleButton.querySelector('.theme-toggle__icon');
|
|
toggleButton.setAttribute('aria-pressed', normalizedTheme === 'dark' ? 'true' : 'false');
|
|
toggleButton.setAttribute('aria-label', 'Switch to ' + nextThemeLabel.toLowerCase());
|
|
if (icon) {
|
|
icon.classList.remove('theme-toggle__icon--moon', 'theme-toggle__icon--sun');
|
|
icon.classList.add(normalizedTheme === 'dark' ? 'theme-toggle__icon--sun' : 'theme-toggle__icon--moon');
|
|
}
|
|
});
|
|
|
|
return normalizedTheme;
|
|
}
|
|
|
|
function initThemeToggle() {
|
|
var toggleButtons = Array.prototype.slice.call(document.querySelectorAll('[data-theme-toggle]'));
|
|
var storedTheme = getStoredTheme();
|
|
var theme = storedTheme || getPreferredTheme();
|
|
|
|
applyTheme(theme);
|
|
|
|
if (!toggleButtons.length) {
|
|
return;
|
|
}
|
|
|
|
Array.prototype.forEach.call(toggleButtons, function (toggleButton) {
|
|
toggleButton.addEventListener('click', function () {
|
|
var nextTheme = document.documentElement.dataset.bsTheme === 'dark' ? 'light' : 'dark';
|
|
setStoredTheme(nextTheme);
|
|
applyTheme(nextTheme);
|
|
});
|
|
});
|
|
}
|
|
|
|
function initSidebarToggle() {
|
|
var body = document.body;
|
|
var toggleButtons = Array.prototype.slice.call(document.querySelectorAll('[data-sidebar-toggle]'));
|
|
var backdrop = document.querySelector('[data-sidebar-backdrop]');
|
|
|
|
if (!toggleButtons.length || !backdrop) {
|
|
return;
|
|
}
|
|
|
|
function setSidebarOpen(isOpen) {
|
|
body.classList.toggle('is-sidebar-open', isOpen);
|
|
Array.prototype.forEach.call(toggleButtons, function (toggleButton) {
|
|
toggleButton.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
|
|
});
|
|
}
|
|
|
|
function toggleSidebar() {
|
|
setSidebarOpen(!body.classList.contains('is-sidebar-open'));
|
|
}
|
|
|
|
Array.prototype.forEach.call(toggleButtons, function (toggleButton) {
|
|
toggleButton.addEventListener('click', function () {
|
|
toggleSidebar();
|
|
});
|
|
});
|
|
|
|
backdrop.addEventListener('click', function () {
|
|
setSidebarOpen(false);
|
|
});
|
|
|
|
document.addEventListener('keydown', function (event) {
|
|
if (event.key === 'Escape') {
|
|
setSidebarOpen(false);
|
|
}
|
|
});
|
|
}
|
|
|
|
window.getPreferredTheme = getPreferredTheme;
|
|
window.getStoredTheme = getStoredTheme;
|
|
window.setStoredTheme = setStoredTheme;
|
|
window.applyTheme = applyTheme;
|
|
window.initThemeToggle = initThemeToggle;
|
|
window.initSidebarToggle = initSidebarToggle;
|
|
|
|
initThemeToggle();
|
|
initSidebarToggle();
|
|
}()); |