mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-17 09:53:07 +09:00
232 lines
6.9 KiB
JavaScript
232 lines
6.9 KiB
JavaScript
/* Dark Mode Theme Manager
|
|
* Handles OS dark mode detection and manual theme toggle
|
|
*/
|
|
|
|
var ThemeManager = window.ThemeManager || {};
|
|
|
|
ThemeManager.getPrefBranch = function() {
|
|
if (!this.prefBranch) {
|
|
this.prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
|
|
.getService(Components.interfaces.nsIPrefService)
|
|
.getBranch("browser.theme.");
|
|
}
|
|
|
|
return this.prefBranch;
|
|
};
|
|
|
|
ThemeManager.getGlobalPrefBranch = function() {
|
|
if (!this.globalPrefBranch) {
|
|
this.globalPrefBranch = Components.classes["@mozilla.org/preferences-service;1"]
|
|
.getService(Components.interfaces.nsIPrefService)
|
|
.getBranch("");
|
|
}
|
|
|
|
return this.globalPrefBranch;
|
|
};
|
|
|
|
ThemeManager.init = function() {
|
|
this.getPrefBranch();
|
|
|
|
// Check stored preference first, then OS preference.
|
|
this.applyTheme();
|
|
|
|
// Listen for preference and system theme changes.
|
|
this.setupPrefObserver();
|
|
this.setupMediaQueryListener();
|
|
};
|
|
|
|
ThemeManager.setupPrefObserver = function() {
|
|
if (!this.prefObserverRegistered) {
|
|
this.getPrefBranch().addObserver("", this, false);
|
|
this.prefObserverRegistered = true;
|
|
}
|
|
};
|
|
|
|
ThemeManager.observe = function(subject, topic, data) {
|
|
// browser.theme.mode changes are intentionally applied on next startup
|
|
// so the preferences UI setting is restart-required.
|
|
if (topic === "nsPref:changed" && data === "mode") {
|
|
return;
|
|
}
|
|
};
|
|
|
|
ThemeManager.shouldFollowSystem = function() {
|
|
try {
|
|
return this.getPrefBranch().getCharPref("mode") === "system";
|
|
} catch (e) {
|
|
return true;
|
|
}
|
|
};
|
|
|
|
ThemeManager.readWindowsThemeValue = function(rootKey, valueName) {
|
|
var regKey = Components.classes["@mozilla.org/windows-registry-key;1"]
|
|
.createInstance(Components.interfaces.nsIWindowsRegKey);
|
|
var winReg = Components.interfaces.nsIWindowsRegKey;
|
|
var personalizePath = "Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
|
|
|
|
try {
|
|
regKey.open(rootKey, personalizePath, winReg.ACCESS_READ);
|
|
if (regKey.hasValue(valueName)) {
|
|
return regKey.readIntValue(valueName);
|
|
}
|
|
} catch (e) {
|
|
// Ignore and return null below.
|
|
}
|
|
|
|
try {
|
|
regKey.close();
|
|
} catch (e2) {
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
ThemeManager.isSystemDarkModePreferred = function() {
|
|
// Prefer Windows registry signal when available.
|
|
try {
|
|
var winReg = Components.interfaces.nsIWindowsRegKey;
|
|
var appsUseLight = this.readWindowsThemeValue(winReg.ROOT_KEY_CURRENT_USER, "AppsUseLightTheme");
|
|
var systemUseLight = this.readWindowsThemeValue(winReg.ROOT_KEY_CURRENT_USER, "SystemUsesLightTheme");
|
|
|
|
// Some setups expose values under HKLM only.
|
|
if (appsUseLight === null) {
|
|
appsUseLight = this.readWindowsThemeValue(winReg.ROOT_KEY_LOCAL_MACHINE, "AppsUseLightTheme");
|
|
}
|
|
if (systemUseLight === null) {
|
|
systemUseLight = this.readWindowsThemeValue(winReg.ROOT_KEY_LOCAL_MACHINE, "SystemUsesLightTheme");
|
|
}
|
|
|
|
// Treat either key requesting dark as dark mode.
|
|
if (appsUseLight !== null || systemUseLight !== null) {
|
|
return appsUseLight === 0 || systemUseLight === 0;
|
|
}
|
|
} catch (e) {
|
|
// Fall through to standards-based media query.
|
|
}
|
|
|
|
// Fallback for non-Windows or unavailable registry signal.
|
|
if (window.matchMedia) {
|
|
var mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
if (mediaQuery.media !== "not all") {
|
|
return mediaQuery.matches;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
ThemeManager.setupMediaQueryListener = function() {
|
|
// Listen for OS dark mode changes if supported
|
|
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").media !== "not all") {
|
|
var darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
darkModeQuery.addListener(function(e) {
|
|
// Apply if following system mode (or no explicit pref).
|
|
if (this.shouldFollowSystem() || !this.hasUserPreference()) {
|
|
this.setTheme(e.matches ? "dark" : "light");
|
|
}
|
|
}.bind(this));
|
|
}
|
|
};
|
|
|
|
ThemeManager.hasUserPreference = function() {
|
|
try {
|
|
var pref = this.getPrefBranch().getCharPref("mode");
|
|
return pref !== "";
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
ThemeManager.applyTheme = function() {
|
|
var theme = "light";
|
|
|
|
// Check user preference first
|
|
try {
|
|
var userPref = this.getPrefBranch().getCharPref("mode");
|
|
if (userPref === "dark" || userPref === "light") {
|
|
theme = userPref;
|
|
} else if (userPref === "system") {
|
|
theme = this.isSystemDarkModePreferred() ? "dark" : "light";
|
|
}
|
|
} catch (e) {
|
|
// Preference doesn't exist, check OS.
|
|
theme = this.isSystemDarkModePreferred() ? "dark" : "light";
|
|
}
|
|
|
|
this.setTheme(theme);
|
|
};
|
|
|
|
ThemeManager.setTheme = function(theme) {
|
|
var docElement = document.documentElement;
|
|
|
|
if (theme === "dark") {
|
|
docElement.setAttribute("data-theme", "dark");
|
|
} else {
|
|
docElement.removeAttribute("data-theme");
|
|
}
|
|
|
|
try {
|
|
var globalPrefs = this.getGlobalPrefBranch();
|
|
var mode = "system";
|
|
var prefersColorScheme = 3;
|
|
|
|
try {
|
|
mode = this.getPrefBranch().getCharPref("mode");
|
|
} catch (e2) {
|
|
}
|
|
|
|
if (mode === "dark") {
|
|
prefersColorScheme = 2;
|
|
} else if (mode === "light") {
|
|
prefersColorScheme = 1;
|
|
}
|
|
|
|
globalPrefs.setIntPref("browser.display.prefers_color_scheme", prefersColorScheme);
|
|
globalPrefs.setIntPref("ui.color_scheme", theme === "dark" ? 2 : 1);
|
|
globalPrefs.setBoolPref("mozilla.widget.disable-native-theme", false);
|
|
} catch (e) {
|
|
// Ignore pref write failures and keep the chrome theme applied.
|
|
}
|
|
};
|
|
|
|
// Public method to toggle theme
|
|
ThemeManager.toggleDarkMode = function() {
|
|
var docElement = document.documentElement;
|
|
var isDark = docElement.getAttribute("data-theme") === "dark";
|
|
|
|
try {
|
|
this.getPrefBranch().setCharPref("mode", !isDark ? "dark" : "light");
|
|
} catch (e) {
|
|
console.error("Failed to set theme preference:", e);
|
|
}
|
|
|
|
this.setTheme(!isDark ? "dark" : "light");
|
|
};
|
|
|
|
// Get current theme mode
|
|
ThemeManager.getTheme = function() {
|
|
var docElement = document.documentElement;
|
|
return docElement.getAttribute("data-theme") === "dark" ? "dark" : "light";
|
|
};
|
|
|
|
window.ThemeManager = ThemeManager;
|
|
this.ThemeManager = ThemeManager;
|
|
if (typeof top != "undefined") {
|
|
top.ThemeManager = ThemeManager;
|
|
}
|
|
window.toggleDarkMode = function() {
|
|
return ThemeManager.toggleDarkMode();
|
|
};
|
|
this.toggleDarkMode = window.toggleDarkMode;
|
|
if (typeof top != "undefined") {
|
|
top.toggleDarkMode = window.toggleDarkMode;
|
|
}
|
|
|
|
// Initialize when document is ready
|
|
if (document.readyState === "interactive" || document.readyState === "complete") {
|
|
ThemeManager.init();
|
|
} else {
|
|
document.addEventListener("DOMContentLoaded", function() {
|
|
ThemeManager.init();
|
|
});
|
|
}
|