diff --git a/.gitignore b/.gitignore
index 4f95603..7dd7cef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,4 +23,5 @@ package-lock.json
!.vscode/tasks.json
# Misc
-#_sass/dist
+_sass/vendors
+assets/js/dist
diff --git a/.stylelintrc.json b/.stylelintrc.json
new file mode 100644
index 0000000..b890290
--- /dev/null
+++ b/.stylelintrc.json
@@ -0,0 +1,31 @@
+{
+ "ignoreFiles": ["_sass/vendors/**"],
+ "extends": "stylelint-config-standard-scss",
+ "rules": {
+ "no-descending-specificity": null,
+ "shorthand-property-no-redundant-values": null,
+ "at-rule-no-vendor-prefix": null,
+ "property-no-vendor-prefix": null,
+ "selector-no-vendor-prefix": null,
+ "value-no-vendor-prefix": null,
+ "color-function-notation": "legacy",
+ "alpha-value-notation": "number",
+ "selector-not-notation": "simple",
+ "color-hex-length": "long",
+ "declaration-block-single-line-max-declarations": 3,
+ "scss/operator-no-newline-after": null,
+ "rule-empty-line-before": [
+ "always",
+ {
+ "ignore": ["after-comment", "first-nested"]
+ }
+ ],
+ "value-keyword-case": [
+ "lower",
+ {
+ "ignoreProperties": ["/^\\$/"]
+ }
+ ],
+ "media-feature-range-notation": "prefix"
+ }
+}
diff --git a/_data/origin/cors.yml b/_data/origin/cors.yml
index 1526b06..62fd344 100644
--- a/_data/origin/cors.yml
+++ b/_data/origin/cors.yml
@@ -20,8 +20,8 @@ webfonts: https://fonts.googleapis.com/css2?family=Lato:wght@300;400&family=Sour
# Libraries
toc:
- css: https://cdn.jsdelivr.net/npm/tocbot@4.29.0/dist/tocbot.min.css
- js: https://cdn.jsdelivr.net/npm/tocbot@4.29.0/dist/tocbot.min.js
+ css: https://cdn.jsdelivr.net/npm/tocbot@4.32.2/dist/tocbot.min.css
+ js: https://cdn.jsdelivr.net/npm/tocbot@4.32.2/dist/tocbot.min.js
fontlogos:
css: https://cdn.jsdelivr.net/npm/font-logos@1/assets/font-logos.css
@@ -30,7 +30,7 @@ forkawesome:
css: https://cdn.jsdelivr.net/npm/fork-awesome@1.2.0/css/fork-awesome.min.css
fontawesome:
- css: https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/css/all.min.css
+ css: https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.7.1/css/all.min.css
ballooncss:
css: https://unpkg.com/balloon-css/balloon.min.css
@@ -39,7 +39,7 @@ search:
js: https://cdn.jsdelivr.net/npm/simple-jekyll-search@1.10.0/dest/simple-jekyll-search.min.js
mermaid:
- js: https://cdn.jsdelivr.net/npm/mermaid@11.0.2/dist/mermaid.min.js
+ js: https://cdn.jsdelivr.net/npm/mermaid@11.4.0/dist/mermaid.min.js
dayjs:
js:
diff --git a/_includes/analytics/cloudflare.html b/_includes/analytics/cloudflare.html
index 1eeb1a9..9faa11e 100644
--- a/_includes/analytics/cloudflare.html
+++ b/_includes/analytics/cloudflare.html
@@ -4,4 +4,3 @@
src="https://static.cloudflareinsights.com/beacon.min.js"
data-cf-beacon='{"token": "{{ site.analytics.cloudflare.id }}"}'
>
-
diff --git a/_includes/analytics/fathom.html b/_includes/analytics/fathom.html
index 4b603d3..216bb14 100644
--- a/_includes/analytics/fathom.html
+++ b/_includes/analytics/fathom.html
@@ -2,6 +2,5 @@
-
+ defer
+>
diff --git a/_includes/analytics/google.html b/_includes/analytics/google.html
index d0aac65..dfe4828 100644
--- a/_includes/analytics/google.html
+++ b/_includes/analytics/google.html
@@ -1,7 +1,7 @@
-
diff --git a/_includes/comments.html b/_includes/comment.html
similarity index 100%
rename from _includes/comments.html
rename to _includes/comment.html
diff --git a/_includes/comments/disqus.html b/_includes/comments/disqus.html
index 2b889a4..fd12a3c 100644
--- a/_includes/comments/disqus.html
+++ b/_includes/comments/disqus.html
@@ -1,38 +1,25 @@
-
-
-
-
-
diff --git a/_includes/comments/giscus.html b/_includes/comments/giscus.html
index f9becfe..8058472 100644
--- a/_includes/comments/giscus.html
+++ b/_includes/comments/giscus.html
@@ -1,21 +1,8 @@
-
-
-
diff --git a/_includes/head.html b/_includes/head.html
index 833b3fa..4d756c3 100644
--- a/_includes/head.html
+++ b/_includes/head.html
@@ -57,12 +57,22 @@
endfor %} {% endfor %} {% endunless %}
- {% unless jekyll.environment == 'production' %}
+ {% unless jekyll.environment == 'production' %} <<<<<<< HEAD
- {% endunless %}
+ ||||||| d51345e
+
+ =======
+
+ >>>>>>> upstream_master {% endunless %}
{% endif %}
-
+
- {% unless site.theme_mode %} {% include mode-toggle.html %} {% endunless %} {%
- include metadata-hook.html %}
+ {% unless site.theme_mode %}
+
+ {% endunless %} {% include js-selector.html lang=lang %} {% if
+ jekyll.environment == 'production' %}
+
+ {% if site.pwa.enabled %}
+
+ {% endif %}
+
+
+ {% for analytics in site.analytics %} {% capture str %}{{ analytics }}{%
+ endcapture %} {% assign platform = str | split: '{' | first %} {% if
+ site.analytics[platform].id and site.analytics[platform].id != empty %} {%
+ include analytics/{{ platform }}.html %} {% endif %} {% endfor %} {% endif %}
+ {% include metadata-hook.html %}
diff --git a/_includes/js-selector.html b/_includes/js-selector.html
index 931deb5..a437a04 100644
--- a/_includes/js-selector.html
+++ b/_includes/js-selector.html
@@ -63,12 +63,12 @@
{% capture script %}/assets/js/dist/{{ js }}.min.js{% endcapture %}
-
+
{% if page.math %}
-
-
+
+
{% endif %}
@@ -85,26 +85,3 @@
{% endcase %}
{% endif %}
{% endif %}
-
-{% if page.mermaid %}
- {% include mermaid.html %}
-{% endif %}
-
-{% if jekyll.environment == 'production' %}
-
- {% if site.pwa.enabled %}
-
- {% endif %}
-
-
- {% for analytics in site.analytics %}
- {% capture str %}{{ analytics }}{% endcapture %}
- {% assign type = str | split: '{' | first %}
- {% if site.analytics[type].id and site.analytics[type].id != empty %}
- {% include analytics/{{ type }}.html %}
- {% endif %}
- {% endfor %}
-{% endif %}
diff --git a/_includes/jsdelivr-combine.html b/_includes/jsdelivr-combine.html
index cffa699..0611213 100644
--- a/_includes/jsdelivr-combine.html
+++ b/_includes/jsdelivr-combine.html
@@ -1,6 +1,6 @@
{% assign urls = include.urls | split: ',' %}
-{% assign combined_urls = nil %}
+{% assign combined_urls = null %}
{% assign domain = 'https://cdn.jsdelivr.net/' %}
@@ -15,12 +15,12 @@
{% endif %}
{% elsif url contains '//' %}
-
+
{% else %}
-
+
{% endif %}
{% endfor %}
{% if combined_urls %}
-
+
{% endif %}
diff --git a/_includes/mermaid.html b/_includes/mermaid.html
deleted file mode 100644
index a3a83ed..0000000
--- a/_includes/mermaid.html
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
diff --git a/_includes/mode-toggle.html b/_includes/mode-toggle.html
deleted file mode 100644
index 113ec37..0000000
--- a/_includes/mode-toggle.html
+++ /dev/null
@@ -1,116 +0,0 @@
-
-
-
diff --git a/_includes/search-loader.html b/_includes/search-loader.html
index 2582580..7fd065d 100644
--- a/_includes/search-loader.html
+++ b/_includes/search-loader.html
@@ -19,29 +19,31 @@
{% capture not_found %}{{ site.data.locales[include.lang].search.no_results }}
{% endcapture %}
diff --git a/_includes/toc.html b/_includes/toc.html
index 49c8f90..883bf13 100644
--- a/_includes/toc.html
+++ b/_includes/toc.html
@@ -1,8 +1,9 @@
{% include toc-status.html %}
{% if enable_toc %}
-
- {{- site.data.locales[include.lang].panel.toc -}}
+
+
+ {{- site.data.locales[include.lang].panel.toc -}}
{% endif %}
diff --git a/_javascript/categories.js b/_javascript/categories.js
index 15d8251..ce87d67 100644
--- a/_javascript/categories.js
+++ b/_javascript/categories.js
@@ -1,5 +1,5 @@
import { basic, initSidebar, initTopbar } from './modules/layouts';
-import { categoryCollapse } from './modules/plugins';
+import { categoryCollapse } from './modules/components';
basic();
initSidebar();
diff --git a/_javascript/home.js b/_javascript/home.js
index ef22cb9..7f628a1 100644
--- a/_javascript/home.js
+++ b/_javascript/home.js
@@ -1,5 +1,5 @@
import { basic, initSidebar, initTopbar } from './modules/layouts';
-import { initLocaleDatetime, loadImg } from './modules/plugins';
+import { initLocaleDatetime, loadImg } from './modules/components';
loadImg();
initLocaleDatetime();
diff --git a/_javascript/misc.js b/_javascript/misc.js
index 52b4043..37130da 100644
--- a/_javascript/misc.js
+++ b/_javascript/misc.js
@@ -1,5 +1,5 @@
import { basic, initSidebar, initTopbar } from './modules/layouts';
-import { initLocaleDatetime } from './modules/plugins';
+import { initLocaleDatetime } from './modules/components';
initSidebar();
initTopbar();
diff --git a/_javascript/modules/plugins.js b/_javascript/modules/components.js
similarity index 60%
rename from _javascript/modules/plugins.js
rename to _javascript/modules/components.js
index cc95c1b..95791a6 100644
--- a/_javascript/modules/plugins.js
+++ b/_javascript/modules/components.js
@@ -4,3 +4,7 @@ export { loadImg } from './components/img-loading';
export { imgPopup } from './components/img-popup';
export { initLocaleDatetime } from './components/locale-datetime';
export { initToc } from './components/toc';
+export { loadMermaid } from './components/mermaid';
+export { modeWatcher } from './components/mode-toggle';
+export { back2top } from './components/back-to-top';
+export { loadTooptip } from './components/tooltip-loader';
diff --git a/_javascript/modules/components/img-popup.js b/_javascript/modules/components/img-popup.js
index ac12043..420a226 100644
--- a/_javascript/modules/components/img-popup.js
+++ b/_javascript/modules/components/img-popup.js
@@ -4,7 +4,6 @@
* Dependencies: https://github.com/biati-digital/glightbox
*/
-const html = document.documentElement;
const lightImages = '.popup:not(.dark)';
const darkImages = '.popup:not(.light)';
let selector = lightImages;
@@ -33,26 +32,17 @@ export function imgPopup() {
document.querySelector('.popup.dark') === null
);
- if (
- (html.hasAttribute('data-mode') &&
- html.getAttribute('data-mode') === 'dark') ||
- (!html.hasAttribute('data-mode') &&
- window.matchMedia('(prefers-color-scheme: dark)').matches)
- ) {
+ if (Theme.visualState === Theme.DARK) {
selector = darkImages;
}
let current = GLightbox({ selector: `${selector}` });
- if (hasDualImages && document.getElementById('mode-toggle')) {
+ if (hasDualImages && Theme.switchable) {
let reverse = null;
window.addEventListener('message', (event) => {
- if (
- event.source === window &&
- event.data &&
- event.data.direction === ModeToggle.ID
- ) {
+ if (event.source === window && event.data && event.data.id === Theme.ID) {
updateImages(current, reverse);
}
});
diff --git a/_javascript/modules/components/mermaid.js b/_javascript/modules/components/mermaid.js
new file mode 100644
index 0000000..2b4759f
--- /dev/null
+++ b/_javascript/modules/components/mermaid.js
@@ -0,0 +1,60 @@
+/**
+ * Mermaid-js loader
+ */
+
+const MERMAID = 'mermaid';
+const themeMapper = Theme.getThemeMapper('default', 'dark');
+
+function refreshTheme(event) {
+ if (event.source === window && event.data && event.data.id === Theme.ID) {
+ // Re-render the SVG ›
+ const mermaidList = document.getElementsByClassName(MERMAID);
+
+ [...mermaidList].forEach((elem) => {
+ const svgCode = elem.previousSibling.children.item(0).innerHTML;
+ elem.textContent = svgCode;
+ elem.removeAttribute('data-processed');
+ });
+
+ const newTheme = themeMapper[Theme.visualState];
+
+ mermaid.initialize({ theme: newTheme });
+ mermaid.init(null, `.${MERMAID}`);
+ }
+}
+
+function setNode(elem) {
+ const svgCode = elem.textContent;
+ const backup = elem.parentElement;
+ backup.classList.add('d-none');
+ // Create mermaid node
+ const mermaid = document.createElement('pre');
+ mermaid.classList.add(MERMAID);
+ const text = document.createTextNode(svgCode);
+ mermaid.appendChild(text);
+ backup.after(mermaid);
+}
+
+export function loadMermaid() {
+ if (
+ typeof mermaid === 'undefined' ||
+ typeof mermaid.initialize !== 'function'
+ ) {
+ return;
+ }
+
+ const initTheme = themeMapper[Theme.visualState];
+
+ let mermaidConf = {
+ theme: initTheme
+ };
+
+ const basicList = document.getElementsByClassName('language-mermaid');
+ [...basicList].forEach(setNode);
+
+ mermaid.initialize(mermaidConf);
+
+ if (Theme.switchable) {
+ window.addEventListener('message', refreshTheme);
+ }
+}
diff --git a/_javascript/modules/components/mode-toggle.js b/_javascript/modules/components/mode-toggle.js
new file mode 100644
index 0000000..455ff0a
--- /dev/null
+++ b/_javascript/modules/components/mode-toggle.js
@@ -0,0 +1,15 @@
+/**
+ * Add listener for theme mode toggle
+ */
+
+const $toggle = document.getElementById('mode-toggle');
+
+export function modeWatcher() {
+ if (!$toggle) {
+ return;
+ }
+
+ $toggle.addEventListener('click', () => {
+ Theme.flip();
+ });
+}
diff --git a/_javascript/modules/components/mode-watcher.js b/_javascript/modules/components/mode-watcher.js
deleted file mode 100644
index 9eecd09..0000000
--- a/_javascript/modules/components/mode-watcher.js
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * Add listener for theme mode toggle
- */
-const toggle = document.getElementById('mode-toggle');
-
-export function modeWatcher() {
- if (!toggle) {
- return;
- }
-
- toggle.addEventListener('click', () => {
- modeToggle.flipMode();
- });
-}
diff --git a/_javascript/modules/components/sidebar.js b/_javascript/modules/components/sidebar.js
deleted file mode 100644
index aed759e..0000000
--- a/_javascript/modules/components/sidebar.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * Expand or close the sidebar in mobile screens.
- */
-
-const $sidebar = document.getElementById('sidebar');
-const $trigger = document.getElementById('sidebar-trigger');
-const $mask = document.getElementById('mask');
-
-class SidebarUtil {
- static #isExpanded = false;
-
- static toggle() {
- this.#isExpanded = !this.#isExpanded;
- document.body.toggleAttribute('sidebar-display', this.#isExpanded);
- $sidebar.classList.toggle('z-2', this.#isExpanded);
- $mask.classList.toggle('d-none', !this.#isExpanded);
- }
-}
-
-export function sidebarExpand() {
- $trigger.onclick = $mask.onclick = () => SidebarUtil.toggle();
-}
diff --git a/_javascript/modules/layouts/basic.js b/_javascript/modules/layouts/basic.js
index fb36a8b..b8eddf6 100644
--- a/_javascript/modules/layouts/basic.js
+++ b/_javascript/modules/layouts/basic.js
@@ -1,7 +1,7 @@
-import { back2top } from '../components/back-to-top';
-import { loadTooptip } from '../components/tooltip-loader';
+import { back2top, loadTooptip, modeWatcher } from '../components';
export function basic() {
+ modeWatcher();
back2top();
loadTooptip();
}
diff --git a/_javascript/modules/layouts/sidebar.js b/_javascript/modules/layouts/sidebar.js
index 8795693..bbf5e7d 100644
--- a/_javascript/modules/layouts/sidebar.js
+++ b/_javascript/modules/layouts/sidebar.js
@@ -1,7 +1,19 @@
-import { modeWatcher } from '../components/mode-watcher';
-import { sidebarExpand } from '../components/sidebar';
+const ATTR_DISPLAY = 'sidebar-display';
+const $sidebar = document.getElementById('sidebar');
+const $trigger = document.getElementById('sidebar-trigger');
+const $mask = document.getElementById('mask');
+
+class SidebarUtil {
+ static #isExpanded = false;
+
+ static toggle() {
+ this.#isExpanded = !this.#isExpanded;
+ document.body.toggleAttribute(ATTR_DISPLAY, this.#isExpanded);
+ $sidebar.classList.toggle('z-2', this.#isExpanded);
+ $mask.classList.toggle('d-none', !this.#isExpanded);
+ }
+}
export function initSidebar() {
- modeWatcher();
- sidebarExpand();
+ $trigger.onclick = $mask.onclick = () => SidebarUtil.toggle();
}
diff --git a/_javascript/modules/theme.js b/_javascript/modules/theme.js
new file mode 100644
index 0000000..f9ebf20
--- /dev/null
+++ b/_javascript/modules/theme.js
@@ -0,0 +1,135 @@
+/**
+ * Theme management class
+ *
+ * To reduce flickering during page load, this script should be loaded synchronously.
+ */
+class Theme {
+ static #modeKey = 'mode';
+ static #modeAttr = 'data-mode';
+ static #darkMedia = window.matchMedia('(prefers-color-scheme: dark)');
+ static switchable = !document.documentElement.hasAttribute(this.#modeAttr);
+
+ static get DARK() {
+ return 'dark';
+ }
+
+ static get LIGHT() {
+ return 'light';
+ }
+
+ /**
+ * @returns {string} Theme mode identifier
+ */
+ static get ID() {
+ return 'theme-mode';
+ }
+
+ /**
+ * Gets the current visual state of the theme.
+ *
+ * @returns {string} The current visual state, either the mode if it exists,
+ * or the system dark mode state ('dark' or 'light').
+ */
+ static get visualState() {
+ if (this.#hasMode) {
+ return this.#mode;
+ } else {
+ return this.#sysDark ? this.DARK : this.LIGHT;
+ }
+ }
+
+ static get #mode() {
+ return sessionStorage.getItem(this.#modeKey);
+ }
+
+ static get #isDarkMode() {
+ return this.#mode === this.DARK;
+ }
+
+ static get #hasMode() {
+ return this.#mode !== null;
+ }
+
+ static get #sysDark() {
+ return this.#darkMedia.matches;
+ }
+
+ /**
+ * Maps theme modes to provided values
+ * @param {string} light Value for light mode
+ * @param {string} dark Value for dark mode
+ * @returns {Object} Mapped values
+ */
+ static getThemeMapper(light, dark) {
+ return {
+ [this.LIGHT]: light,
+ [this.DARK]: dark
+ };
+ }
+
+ /**
+ * Initializes the theme based on system preferences or stored mode
+ */
+ static init() {
+ if (!this.switchable) {
+ return;
+ }
+
+ this.#darkMedia.addEventListener('change', () => {
+ const lastMode = this.#mode;
+ this.#clearMode();
+
+ if (lastMode !== this.visualState) {
+ this.#notify();
+ }
+ });
+
+ if (!this.#hasMode) {
+ return;
+ }
+
+ if (this.#isDarkMode) {
+ this.#setDark();
+ } else {
+ this.#setLight();
+ }
+ }
+
+ /**
+ * Flips the current theme mode
+ */
+ static flip() {
+ if (this.#hasMode) {
+ this.#clearMode();
+ } else {
+ this.#sysDark ? this.#setLight() : this.#setDark();
+ }
+ this.#notify();
+ }
+
+ static #setDark() {
+ document.documentElement.setAttribute(this.#modeAttr, this.DARK);
+ sessionStorage.setItem(this.#modeKey, this.DARK);
+ }
+
+ static #setLight() {
+ document.documentElement.setAttribute(this.#modeAttr, this.LIGHT);
+ sessionStorage.setItem(this.#modeKey, this.LIGHT);
+ }
+
+ static #clearMode() {
+ document.documentElement.removeAttribute(this.#modeAttr);
+ sessionStorage.removeItem(this.#modeKey);
+ }
+
+ /**
+ * Notifies other plugins that the theme mode has changed
+ */
+ static #notify() {
+ window.postMessage({ id: this.ID }, '*');
+ }
+}
+
+Theme.init();
+
+export default Theme;
diff --git a/_javascript/page.js b/_javascript/page.js
index 76e8ce9..4b03b79 100644
--- a/_javascript/page.js
+++ b/_javascript/page.js
@@ -1,9 +1,15 @@
import { basic, initSidebar, initTopbar } from './modules/layouts';
-import { loadImg, imgPopup, initClipboard } from './modules/plugins';
+import {
+ loadImg,
+ imgPopup,
+ initClipboard,
+ loadMermaid
+} from './modules/components';
loadImg();
imgPopup();
initSidebar();
initTopbar();
initClipboard();
+loadMermaid();
basic();
diff --git a/_javascript/post.js b/_javascript/post.js
index 1c616ec..dc472b4 100644
--- a/_javascript/post.js
+++ b/_javascript/post.js
@@ -5,8 +5,9 @@ import {
imgPopup,
initLocaleDatetime,
initClipboard,
- initToc
-} from './modules/plugins';
+ initToc,
+ loadMermaid
+} from './modules/components';
loadImg();
initToc();
@@ -15,4 +16,5 @@ initSidebar();
initLocaleDatetime();
initClipboard();
initTopbar();
+loadMermaid();
basic();
diff --git a/_layouts/default.html b/_layouts/default.html
index 1590ef6..a55bfef 100644
--- a/_layouts/default.html
+++ b/_layouts/default.html
@@ -33,7 +33,7 @@ layout: compress
-
Comments powered by Disqus.
-