Added support for search within the template as suggested in #581. I decided to go with a client side search based on [Ninja keys](https://github.com/ssleptsov/ninja-keys), but using [deepdub's fork](https://github.com/deepdub-ai/ninja-keys) as basis since it supports fuzzy search. Had to do a bunch of changes to their code to make it work without using node to install everything. Also changed to use some colors defined in our side and using both pages' titles and descriptions for search. Also had to increase the template max width to better accomodate the new item in navigation bar. Missing implementations: - [ ] One thing I'd love to do (but currently have no idea how) would be to change the text next to the search button depending on the platform. For example, if the user is accessing the site on a mac they should use ⌘k instead of ctrl k. - [x] Test how this looks like (and how it is supposed to work) on devices with smaller screens - [x] Support for offline mode Some screenshots: --- ## Dark version    --- ## Light version    --------- Signed-off-by: George Araujo <george.gcac@gmail.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
253 lines
7.8 KiB
JavaScript
253 lines
7.8 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);
|
|
setSearchTheme(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 setSearchTheme = (theme) => {
|
|
const ninjaKeys = document.querySelector("ninja-keys");
|
|
if (!ninjaKeys) return;
|
|
|
|
if (theme === "dark") {
|
|
ninjaKeys.classList.add("dark");
|
|
} else {
|
|
ninjaKeys.classList.remove("dark");
|
|
}
|
|
};
|
|
|
|
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();
|
|
});
|
|
};
|