mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
193 lines
4.7 KiB
JavaScript
193 lines
4.7 KiB
JavaScript
import { THEME_ACCENT_OVERRIDES } from "../../config/instance-config.js";
|
|
|
|
(() => {
|
|
const VALID_THEMES = new Set(["light", "dark"]);
|
|
const STORAGE_KEY = "bitvid:theme";
|
|
const FALLBACK_THEME = "dark";
|
|
const ACCENT_CSS_VARIABLES = Object.freeze({
|
|
accent: "--color-accent",
|
|
accentStrong: "--color-accent-strong",
|
|
accentPressed: "--color-accent-pressed",
|
|
});
|
|
const ACCENT_TOKENS = Object.keys(ACCENT_CSS_VARIABLES);
|
|
const { documentElement: root } = document;
|
|
|
|
if (!(root instanceof HTMLElement)) {
|
|
return;
|
|
}
|
|
|
|
const sanitizeConfigOverrides = (overrides) => {
|
|
if (!overrides || typeof overrides !== "object") {
|
|
return {};
|
|
}
|
|
|
|
const sanitized = {};
|
|
|
|
Object.entries(overrides).forEach(([theme, themeOverrides]) => {
|
|
if (!VALID_THEMES.has(theme) || typeof themeOverrides !== "object") {
|
|
return;
|
|
}
|
|
|
|
const normalized = {};
|
|
|
|
ACCENT_TOKENS.forEach((token) => {
|
|
const value = themeOverrides[token];
|
|
if (typeof value === "string") {
|
|
const trimmed = value.trim();
|
|
if (trimmed !== "") {
|
|
normalized[token] = trimmed;
|
|
}
|
|
}
|
|
});
|
|
|
|
if (Object.keys(normalized).length > 0) {
|
|
sanitized[theme] = normalized;
|
|
}
|
|
});
|
|
|
|
return sanitized;
|
|
};
|
|
|
|
const CONFIG_ACCENT_OVERRIDES = sanitizeConfigOverrides(
|
|
THEME_ACCENT_OVERRIDES
|
|
);
|
|
|
|
const normalizeTheme = (value) => {
|
|
if (typeof value !== "string") {
|
|
return null;
|
|
}
|
|
const normalized = value.trim().toLowerCase();
|
|
if (normalized === "" || normalized === "default") {
|
|
return FALLBACK_THEME;
|
|
}
|
|
return VALID_THEMES.has(normalized) ? normalized : null;
|
|
};
|
|
|
|
const getParentRoot = () => {
|
|
try {
|
|
if (window.parent && window.parent !== window) {
|
|
const parentDocument = window.parent.document;
|
|
const parentRoot = parentDocument?.documentElement;
|
|
return parentRoot instanceof HTMLElement ? parentRoot : null;
|
|
}
|
|
} catch (error) {
|
|
return null;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
const readParentTheme = () => {
|
|
const parentRoot = getParentRoot();
|
|
if (!parentRoot) {
|
|
return null;
|
|
}
|
|
return normalizeTheme(parentRoot.dataset?.theme);
|
|
};
|
|
|
|
const readStoredTheme = () => {
|
|
try {
|
|
return normalizeTheme(window.localStorage.getItem(STORAGE_KEY));
|
|
} catch (error) {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
const applyTheme = (theme) => {
|
|
if (theme && VALID_THEMES.has(theme)) {
|
|
root.dataset.theme = theme;
|
|
} else {
|
|
root.removeAttribute("data-theme");
|
|
}
|
|
};
|
|
|
|
const readParentAccentOverrides = () => {
|
|
const parentRoot = getParentRoot();
|
|
if (!parentRoot) {
|
|
return null;
|
|
}
|
|
|
|
const overrides = {};
|
|
let hasOverride = false;
|
|
|
|
Object.values(ACCENT_CSS_VARIABLES).forEach((cssVariable) => {
|
|
try {
|
|
const value = parentRoot.style.getPropertyValue(cssVariable);
|
|
if (typeof value === "string") {
|
|
const trimmed = value.trim();
|
|
if (trimmed !== "") {
|
|
overrides[cssVariable] = trimmed;
|
|
hasOverride = true;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
/* noop */
|
|
}
|
|
});
|
|
|
|
return hasOverride ? overrides : null;
|
|
};
|
|
|
|
const applyAccentOverrides = (theme) => {
|
|
const parentOverrides = readParentAccentOverrides();
|
|
const configOverrides = CONFIG_ACCENT_OVERRIDES[theme] || null;
|
|
|
|
ACCENT_TOKENS.forEach((token) => {
|
|
const cssVariable = ACCENT_CSS_VARIABLES[token];
|
|
const parentValue = parentOverrides?.[cssVariable];
|
|
const configValue = configOverrides?.[token];
|
|
const nextValue = parentValue || configValue || "";
|
|
|
|
if (typeof nextValue === "string" && nextValue.trim() !== "") {
|
|
root.style.setProperty(cssVariable, nextValue.trim());
|
|
} else {
|
|
root.style.removeProperty(cssVariable);
|
|
}
|
|
});
|
|
};
|
|
|
|
const syncTheme = () => {
|
|
const resolvedTheme =
|
|
readParentTheme() ?? readStoredTheme() ?? FALLBACK_THEME;
|
|
applyTheme(resolvedTheme);
|
|
applyAccentOverrides(resolvedTheme);
|
|
};
|
|
|
|
syncTheme();
|
|
|
|
const parentRoot = getParentRoot();
|
|
let observer;
|
|
|
|
if (parentRoot && typeof MutationObserver !== "undefined") {
|
|
observer = new MutationObserver(syncTheme);
|
|
observer.observe(parentRoot, {
|
|
attributes: true,
|
|
attributeFilter: ["data-theme", "style"],
|
|
});
|
|
}
|
|
|
|
window.addEventListener("storage", (event) => {
|
|
if (event?.key === STORAGE_KEY) {
|
|
syncTheme();
|
|
}
|
|
});
|
|
|
|
window.addEventListener(
|
|
"pageshow",
|
|
() => {
|
|
syncTheme();
|
|
},
|
|
{ once: true }
|
|
);
|
|
|
|
window.addEventListener(
|
|
"beforeunload",
|
|
() => {
|
|
if (observer) {
|
|
observer.disconnect();
|
|
}
|
|
},
|
|
{ once: true }
|
|
);
|
|
})();
|
|
|