The theme toggle button now has a third option, which follows the user's
system preferences.
- In the code there's now a distinction between the theme setting (which
can be "dark", "light" or "system") and the computed theme.
- The theme setting is now stored as the "theme-setting" local storage
variable. Since this is different from the old variable ("theme"), this
will effectively reset all current user themes to the default "system".
Maybe this is not what you want.
- The "system" theme icon is currently a half circle symbol.
- The toggle button now displays the current theme setting, rather than
the next theme setting (as far as I know this is consistent with other
sites which have three theme options).
- `theme.js` is now loaded regardless of `site.enable_darkmode`. This is
because other scripts which were always loaded relied on being able to
determine the theme. `theme.js` no longer initialises the theme itself
though; this only happens when `site.enable_darkmode`.
- When the theme setting is "system", the theme will change immediately
whenever the user changes their system theme.
#2261
---------
Signed-off-by: George Araujo <george.gcac@gmail.com>
Co-authored-by: George Araujo <george.gcac@gmail.com>
241 lines
7.6 KiB
JavaScript
241 lines
7.6 KiB
JavaScript
// Has to be in the head tag, otherwise a flicker effect will occur.
|
|
|
|
// Toggle through light, dark, and system theme settings.
|
|
let toggleThemeSetting = () => {
|
|
let themeSetting = determineThemeSetting();
|
|
if (themeSetting == "system") {
|
|
setThemeSetting("light");
|
|
} else if (themeSetting == "light") {
|
|
setThemeSetting("dark");
|
|
} else {
|
|
setThemeSetting("system");
|
|
}
|
|
};
|
|
|
|
// Change the theme setting and apply the theme.
|
|
let setThemeSetting = (themeSetting) => {
|
|
localStorage.setItem("theme", themeSetting);
|
|
|
|
document.documentElement.setAttribute("data-theme-setting", themeSetting);
|
|
|
|
applyTheme();
|
|
};
|
|
|
|
// Apply the computed dark or light theme to the website.
|
|
let applyTheme = () => {
|
|
let theme = determineComputedTheme();
|
|
|
|
transTheme();
|
|
setHighlight(theme);
|
|
setGiscusTheme(theme);
|
|
|
|
// if mermaid is not defined, do nothing
|
|
if (typeof mermaid !== "undefined") {
|
|
setMermaidTheme(theme);
|
|
}
|
|
|
|
// if diff2html is not defined, do nothing
|
|
if (typeof Diff2HtmlUI !== "undefined") {
|
|
setDiff2htmlTheme(theme);
|
|
}
|
|
|
|
// if echarts is not defined, do nothing
|
|
if (typeof echarts !== "undefined") {
|
|
setEchartsTheme(theme);
|
|
}
|
|
|
|
// if vegaEmbed is not defined, do nothing
|
|
if (typeof vegaEmbed !== "undefined") {
|
|
setVegaLiteTheme(theme);
|
|
}
|
|
|
|
document.documentElement.setAttribute("data-theme", theme);
|
|
|
|
// Add class to tables.
|
|
let tables = document.getElementsByTagName("table");
|
|
for (let i = 0; i < tables.length; i++) {
|
|
if (theme == "dark") {
|
|
tables[i].classList.add("table-dark");
|
|
} else {
|
|
tables[i].classList.remove("table-dark");
|
|
}
|
|
}
|
|
|
|
// Set jupyter notebooks themes.
|
|
let jupyterNotebooks = document.getElementsByClassName("jupyter-notebook-iframe-container");
|
|
for (let i = 0; i < jupyterNotebooks.length; i++) {
|
|
let bodyElement = jupyterNotebooks[i].getElementsByTagName("iframe")[0].contentWindow.document.body;
|
|
if (theme == "dark") {
|
|
bodyElement.setAttribute("data-jp-theme-light", "false");
|
|
bodyElement.setAttribute("data-jp-theme-name", "JupyterLab Dark");
|
|
} else {
|
|
bodyElement.setAttribute("data-jp-theme-light", "true");
|
|
bodyElement.setAttribute("data-jp-theme-name", "JupyterLab Light");
|
|
}
|
|
}
|
|
|
|
// Updates the background of medium-zoom overlay.
|
|
if (typeof medium_zoom !== "undefined") {
|
|
medium_zoom.update({
|
|
background: getComputedStyle(document.documentElement).getPropertyValue("--global-bg-color") + "ee", // + 'ee' for trasparency.
|
|
});
|
|
}
|
|
};
|
|
|
|
let setHighlight = (theme) => {
|
|
if (theme == "dark") {
|
|
document.getElementById("highlight_theme_light").media = "none";
|
|
document.getElementById("highlight_theme_dark").media = "";
|
|
} else {
|
|
document.getElementById("highlight_theme_dark").media = "none";
|
|
document.getElementById("highlight_theme_light").media = "";
|
|
}
|
|
};
|
|
|
|
let setGiscusTheme = (theme) => {
|
|
function sendMessage(message) {
|
|
const iframe = document.querySelector("iframe.giscus-frame");
|
|
if (!iframe) return;
|
|
iframe.contentWindow.postMessage({ giscus: message }, "https://giscus.app");
|
|
}
|
|
|
|
sendMessage({
|
|
setConfig: {
|
|
theme: theme,
|
|
},
|
|
});
|
|
};
|
|
|
|
let addMermaidZoom = (records, observer) => {
|
|
var svgs = d3.selectAll(".mermaid svg");
|
|
svgs.each(function () {
|
|
var svg = d3.select(this);
|
|
svg.html("<g>" + svg.html() + "</g>");
|
|
var inner = svg.select("g");
|
|
var zoom = d3.zoom().on("zoom", function (event) {
|
|
inner.attr("transform", event.transform);
|
|
});
|
|
svg.call(zoom);
|
|
});
|
|
observer.disconnect();
|
|
};
|
|
|
|
let setMermaidTheme = (theme) => {
|
|
if (theme == "light") {
|
|
// light theme name in mermaid is 'default'
|
|
// https://mermaid.js.org/config/theming.html#available-themes
|
|
theme = "default";
|
|
}
|
|
|
|
/* Re-render the SVG, based on https://github.com/cotes2020/jekyll-theme-chirpy/blob/master/_includes/mermaid.html */
|
|
document.querySelectorAll(".mermaid").forEach((elem) => {
|
|
// Get the code block content from previous element, since it is the mermaid code itself as defined in Markdown, but it is hidden
|
|
let svgCode = elem.previousSibling.childNodes[0].innerHTML;
|
|
elem.removeAttribute("data-processed");
|
|
elem.innerHTML = svgCode;
|
|
});
|
|
|
|
mermaid.initialize({ theme: theme });
|
|
window.mermaid.init(undefined, document.querySelectorAll(".mermaid"));
|
|
|
|
const observable = document.querySelector(".mermaid svg");
|
|
if (observable !== null) {
|
|
var observer = new MutationObserver(addMermaidZoom);
|
|
const observerOptions = { childList: true };
|
|
observer.observe(observable, observerOptions);
|
|
}
|
|
};
|
|
|
|
let setDiff2htmlTheme = (theme) => {
|
|
document.querySelectorAll(".diff2html").forEach((elem) => {
|
|
// Get the code block content from previous element, since it is the diff code itself as defined in Markdown, but it is hidden
|
|
let textData = elem.previousSibling.childNodes[0].innerHTML;
|
|
elem.innerHTML = "";
|
|
const configuration = { colorScheme: theme, drawFileList: true, highlight: true, matching: "lines" };
|
|
const diff2htmlUi = new Diff2HtmlUI(elem, textData, configuration);
|
|
diff2htmlUi.draw();
|
|
});
|
|
};
|
|
|
|
let setEchartsTheme = (theme) => {
|
|
document.querySelectorAll(".echarts").forEach((elem) => {
|
|
// Get the code block content from previous element, since it is the echarts code itself as defined in Markdown, but it is hidden
|
|
let jsonData = elem.previousSibling.childNodes[0].innerHTML;
|
|
echarts.dispose(elem);
|
|
|
|
if (theme === "dark") {
|
|
var chart = echarts.init(elem, "dark-fresh-cut");
|
|
} else {
|
|
var chart = echarts.init(elem);
|
|
}
|
|
|
|
chart.setOption(JSON.parse(jsonData));
|
|
});
|
|
};
|
|
|
|
let setVegaLiteTheme = (theme) => {
|
|
document.querySelectorAll(".vega-lite").forEach((elem) => {
|
|
// Get the code block content from previous element, since it is the vega lite code itself as defined in Markdown, but it is hidden
|
|
let jsonData = elem.previousSibling.childNodes[0].innerHTML;
|
|
elem.innerHTML = "";
|
|
if (theme === "dark") {
|
|
vegaEmbed(elem, JSON.parse(jsonData), { theme: "dark" });
|
|
} else {
|
|
vegaEmbed(elem, JSON.parse(jsonData));
|
|
}
|
|
});
|
|
};
|
|
|
|
let transTheme = () => {
|
|
document.documentElement.classList.add("transition");
|
|
window.setTimeout(() => {
|
|
document.documentElement.classList.remove("transition");
|
|
}, 500);
|
|
};
|
|
|
|
// Determine the expected state of the theme toggle, which can be "dark", "light", or
|
|
// "system". Default is "system".
|
|
let determineThemeSetting = () => {
|
|
let themeSetting = localStorage.getItem("theme");
|
|
if (themeSetting != "dark" && themeSetting != "light" && themeSetting != "system") {
|
|
themeSetting = "system";
|
|
}
|
|
return themeSetting;
|
|
};
|
|
|
|
// Determine the computed theme, which can be "dark" or "light". If the theme setting is
|
|
// "system", the computed theme is determined based on the user's system preference.
|
|
let determineComputedTheme = () => {
|
|
let themeSetting = determineThemeSetting();
|
|
if (themeSetting == "system") {
|
|
const userPref = window.matchMedia;
|
|
if (userPref && userPref("(prefers-color-scheme: dark)").matches) {
|
|
return "dark";
|
|
} else {
|
|
return "light";
|
|
}
|
|
} else {
|
|
return themeSetting;
|
|
}
|
|
};
|
|
|
|
let initTheme = () => {
|
|
let themeSetting = determineThemeSetting();
|
|
|
|
setThemeSetting(themeSetting);
|
|
|
|
// Add event listener to the theme toggle button.
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
const mode_toggle = document.getElementById("light-toggle");
|
|
|
|
mode_toggle.addEventListener("click", function () {
|
|
toggleThemeSetting();
|
|
});
|
|
});
|
|
|
|
// Add event listener to the system theme preference change.
|
|
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", ({ matches }) => {
|
|
applyTheme();
|
|
});
|
|
};
|