Files
Masterluke77andGitHub 6f48827f4d Vendoo 1.41.2 – Git Ignore Boundary & Module Tracking Hotfix (#18)
* docs: prepare Vendoo 1.41.2 git-ignore boundary hotfix

* fix: track native source modules with root-anchored runtime ignores
2026-07-09 17:09:00 +02:00

91 lines
3.9 KiB
JavaScript

const STORAGE_KEY = 'vendoo-theme-state-v2';
const LEGACY_KEY = 'vendoo-theme';
const BUILTIN = new Set(['light', 'dark', 'contrast', 'system']);
let activeCustomProperties = [];
function mediaDark() {
return window.matchMedia?.('(prefers-color-scheme: dark)').matches;
}
function mediaReducedMotion() {
return window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
}
function clearCustomProperties() {
for (const property of activeCustomProperties) document.documentElement.style.removeProperty(property);
activeCustomProperties = [];
}
function effectiveBuiltin(themeId) {
return themeId === 'system' ? (mediaDark() ? 'dark' : 'light') : themeId;
}
function normalizeState(input = {}) {
const themeId = String(input.theme_id || input.themeId || 'system');
return {
theme_id: themeId,
density: ['compact', 'comfortable', 'spacious'].includes(input.density) ? input.density : 'comfortable',
reduced_motion: ['system', 'reduce', 'allow'].includes(input.reduced_motion || input.reducedMotion) ? (input.reduced_motion || input.reducedMotion) : 'system',
custom_theme: input.custom_theme || input.customTheme || null,
};
}
function applyState(input, { persist = true, dispatch = true } = {}) {
const state = normalizeState(input);
clearCustomProperties();
const custom = state.custom_theme && state.custom_theme.id === state.theme_id ? state.custom_theme : null;
const baseTheme = custom ? custom.base_theme : state.theme_id;
const resolved = effectiveBuiltin(BUILTIN.has(baseTheme) ? baseTheme : 'light');
document.documentElement.dataset.theme = resolved;
document.documentElement.dataset.themePreference = state.theme_id;
document.documentElement.dataset.density = state.density;
const reduce = state.reduced_motion === 'reduce' || (state.reduced_motion === 'system' && mediaReducedMotion());
document.documentElement.dataset.reducedMotion = reduce ? 'true' : 'false';
if (custom?.css_values) {
for (const [property, value] of Object.entries(custom.css_values)) {
if (!/^--vd-[a-z0-9-]+$/.test(property)) continue;
document.documentElement.style.setProperty(property, String(value));
activeCustomProperties.push(property);
}
}
if (persist) localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
if (dispatch) window.dispatchEvent(new CustomEvent('vendoo:theme-changed', { detail: { ...state, resolved } }));
return { ...state, resolved };
}
function readStoredState() {
try {
const parsed = JSON.parse(localStorage.getItem(STORAGE_KEY) || 'null');
if (parsed) return normalizeState(parsed);
} catch {}
const legacy = localStorage.getItem(LEGACY_KEY);
return normalizeState({ theme_id: BUILTIN.has(legacy) ? legacy : 'system' });
}
function previewTheme({ base_theme = 'light', css_values = {}, density = 'comfortable', reduced_motion = 'system' } = {}) {
return applyState({ theme_id: '__preview__', density, reduced_motion, custom_theme: { id: '__preview__', base_theme, css_values } }, { persist: false });
}
function restoreStored() {
return applyState(readStoredState(), { persist: false });
}
applyState(readStoredState(), { persist: false, dispatch: false });
window.matchMedia?.('(prefers-color-scheme: dark)').addEventListener?.('change', () => {
if (document.documentElement.dataset.themePreference === 'system') restoreStored();
});
window.matchMedia?.('(prefers-reduced-motion: reduce)').addEventListener?.('change', () => restoreStored());
window.VendooTheme = Object.freeze({
get preference() { return document.documentElement.dataset.themePreference || 'system'; },
get resolved() { return document.documentElement.dataset.theme || 'light'; },
applyState,
previewTheme,
restoreStored,
setTheme(themeId, { persist = true } = {}) {
const current = readStoredState();
return applyState({ ...current, theme_id: BUILTIN.has(themeId) ? themeId : 'system', custom_theme: null }, { persist });
},
supported: Object.freeze([...BUILTIN]),
});