mirror of
https://github.com/cotes2020/jekyll-theme-chirpy.git
synced 2025-12-19 06:06:54 +00:00
perf: speed up page rendering and jekyll build process (#2034)
Some checks failed
Close stale issues and PRs / stale (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Style Lint / stylelint (push) Has been cancelled
Lint Commit Messages / commitlint (push) Has been cancelled
Build and Deploy / build (push) Has been cancelled
Build and Deploy / deploy (push) Has been cancelled
Some checks failed
Close stale issues and PRs / stale (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Style Lint / stylelint (push) Has been cancelled
Lint Commit Messages / commitlint (push) Has been cancelled
Build and Deploy / build (push) Has been cancelled
Build and Deploy / deploy (push) Has been cancelled
- Ensure inline scripts execute after the DOM has fully loaded. - Use Rollup to bundle the theme-mode and Mermaid scripts, reducing the number of Jekyll include snippets.
This commit is contained in:
@@ -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';
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
60
_javascript/modules/components/mermaid.js
Normal file
60
_javascript/modules/components/mermaid.js
Normal file
@@ -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 › <https://github.com/mermaid-js/mermaid/issues/311#issuecomment-332557344>
|
||||
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);
|
||||
}
|
||||
}
|
||||
15
_javascript/modules/components/mode-toggle.js
Normal file
15
_javascript/modules/components/mode-toggle.js
Normal file
@@ -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();
|
||||
});
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
135
_javascript/modules/theme.js
Normal file
135
_javascript/modules/theme.js
Normal file
@@ -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;
|
||||
Reference in New Issue
Block a user