CI / build (push) Failing after 6m2s
Introduce a Node build system for minified CSS/JS with content hashes, add PWA support, i18n helpers, accessibility improvements, and more translatable strings. Move theme configuration from bookmarks to localStorage, add Playwright tests, and set up CI workflows.
123 lines
4.3 KiB
JavaScript
123 lines
4.3 KiB
JavaScript
// =====================================================================
|
|
// Theme configuration and dark/light toggle for Shaarli Pro.
|
|
// Keeps theme/mode preferences client-side without creating bookmarks.
|
|
// Used by the dark/light switch AND the theme picker (tools.html).
|
|
// =====================================================================
|
|
(function () {
|
|
'use strict';
|
|
|
|
const STORAGE_KEY = 'shaarit_themes_config';
|
|
|
|
function readConfig() {
|
|
try {
|
|
const raw = localStorage.getItem(STORAGE_KEY);
|
|
if (!raw) return null;
|
|
const parsed = JSON.parse(raw);
|
|
return (parsed && typeof parsed === 'object') ? parsed : null;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function writeConfig(config) {
|
|
try {
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(config));
|
|
return true;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Merge partial updates into the persisted config and save to localStorage.
|
|
function save(partial) {
|
|
const base = readConfig() || {};
|
|
const config = Object.assign(
|
|
{ version: 2, themes: [], default: 'DEFAULT', mode: 'dark' },
|
|
base,
|
|
partial || {}
|
|
);
|
|
const ok = writeConfig(config);
|
|
return Promise.resolve({ ok, config });
|
|
}
|
|
|
|
// Reads the current persisted config from localStorage (no write).
|
|
function load() {
|
|
return Promise.resolve(readConfig());
|
|
}
|
|
|
|
// Kept for backward compatibility with older callers.
|
|
function findCandidates() {
|
|
return Promise.resolve([]);
|
|
}
|
|
|
|
window.ShaaritThemeConfig = { save, load, findCandidates };
|
|
})();
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
'use strict';
|
|
|
|
const themeCheckbox = document.getElementById('theme-toggle-checkbox');
|
|
const themeIconLight = document.getElementById('theme-icon-light');
|
|
const themeLabelSpan = document.querySelector('.theme-toggle-label span');
|
|
const i18n = window.ShaarliProI18n || { t: function (k) { return k; } };
|
|
|
|
function checkThemeRestrictions() {
|
|
const themeId = localStorage.getItem('shaarit_theme_id') || 'DEFAULT';
|
|
const darkOnlyThemes = ['LINEAR', 'SPOTIFY', 'NOTION', 'DISCORD', 'DRACULA', 'ONE_DARK_PRO', 'TOKYO_NIGHT', 'NORD', 'NIGHT_OWL', 'ANTHRACITE', 'CYBERPUNK', 'NAVY_ELEGANCE', 'EARTHY'];
|
|
return darkOnlyThemes.indexOf(themeId) !== -1;
|
|
}
|
|
|
|
function updateTheme(theme) {
|
|
// Enforce dark mode if the theme only supports dark
|
|
if (checkThemeRestrictions()) {
|
|
theme = 'dark';
|
|
}
|
|
|
|
document.documentElement.setAttribute('data-theme', theme);
|
|
localStorage.setItem('theme', theme);
|
|
|
|
if (themeCheckbox) {
|
|
themeCheckbox.checked = theme === 'dark';
|
|
// Disable the checkbox if restricted to dark mode
|
|
themeCheckbox.disabled = checkThemeRestrictions();
|
|
themeCheckbox.closest('.theme-switch').style.opacity = themeCheckbox.disabled ? '0.5' : '1';
|
|
}
|
|
if (themeIconLight) {
|
|
themeIconLight.className = theme === 'dark' ? 'mdi mdi-weather-night' : 'mdi mdi-weather-sunny';
|
|
}
|
|
if (themeLabelSpan) {
|
|
themeLabelSpan.textContent = theme === 'dark' ? i18n.t('Dark Mode') : i18n.t('Light Mode');
|
|
}
|
|
}
|
|
|
|
async function syncModeToBookmark(mode) {
|
|
try {
|
|
if (!window.ShaaritThemeConfig) return;
|
|
await window.ShaaritThemeConfig.save({ mode });
|
|
} catch (e) {
|
|
// eslint-disable-next-line no-console
|
|
console.error('[shaarit] Failed to sync mode to bookmark', e);
|
|
}
|
|
}
|
|
|
|
// Handle themeChanged event fired by tools.html
|
|
window.addEventListener('themeChanged', () => {
|
|
updateTheme(localStorage.getItem('theme') || 'dark');
|
|
});
|
|
|
|
// Init Theme
|
|
const savedTheme = localStorage.getItem('theme') ||
|
|
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
|
updateTheme(savedTheme);
|
|
|
|
if (themeCheckbox) {
|
|
themeCheckbox.addEventListener('change', () => {
|
|
if (checkThemeRestrictions()) return; // Extra safety
|
|
const next = themeCheckbox.checked ? 'dark' : 'light';
|
|
updateTheme(next);
|
|
// Sync mode to bookmark in background
|
|
syncModeToBookmark(next);
|
|
});
|
|
}
|
|
});
|