项目结构描述

Shiroi 魔改(typo)日志

(解耦合) 标签页

标签页用于显示标题和站点图标.

  1. 创建文件layout/_partial/label.ejs.

标签页将实现使用icon.svg为图标 标题可在站点配置文件_config.yml或者主题配置文件_config.shiroi.yml中配置(存在优先关系).

1
2
3
4
5
6
7
8
<!-- 标签页 -->
<title>
<%= page.title || config.title || theme.title %>
</title>

<% if (theme.favicon){ %>
<%- favicon_tag(theme.favicon) %>
<% } %>
  1. layout/layout.ejs中注入配置
1
2
<!-- 标签页 -->
<%- partial('_partial/label') %>

(解耦合) 字体

字体是全局主题使用的字体,继承了 typo 主题的配置

  1. 创建文件layout/_partial/font.ejs.
1
2
3
4
5
  <!-- 字体 -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap" rel="stylesheet">
  1. layout/_partial/global.ejs中注入配置
1
2
<!-- 字体 -->
<%- partial('_partial/font.ejs') %>

(解耦合) 导航栏

导航栏是每个页面最上方的 top bar, 用于展示站点配置文件中的选项menu.

  1. 创建文件layout/_partial/header.ejs.

导航栏将实现使用icon.svg为图标 左侧标题可在站点配置文件中配置. 右侧图标可以在icon/文件夹中找到对应名字更换. 右侧图标后名字取决于主题配置文件的 menu 中的配置.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--(头部 top bar) Header 样式 -->
<%- css('css/header.css') %>

<header class="header">
<section class="header-container">
<a class="header-logo" href="<%- url_for('icon.svg') %>"><%= config.title %></a>
<ul class="header-nav">
<%
for (const name in theme.menu) {
const iconName = name;
%>
<li>
<a href="<%- url_for(theme.menu[name]) %>">
<img src="<%- url_for('icon/' + iconName + '.svg') %>" alt="<%= name %> icon" style="width:1.2em;height:1.2em;vertical-align:middle;">
<%= name %>
</a>
</li>
<% } %>
</ul>
</section>
</header>
  1. layout/layout.ejs中注入配置
1
2
<!-- 导航栏 -->
<%- partial('_partial/header') %>
  1. 创建样式文件source/css/header.css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
.header {
position: sticky;
top: -48px;
background: inherit;
margin: 0;
padding: 64px 8vw 0;
max-width: 100%;
width: 100%;
}

.header-container {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--color-hr);
}

.header-logo {
font-size: 2rem;
font-weight: 600;
color: var(--font-color);
margin-bottom: 16px;
}

.header-nav {
margin: 0 0 4px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
align-content: center;
list-style: none;
}

.header-nav li {
padding: 2px 0;
margin-right: 24px;
}

.header-nav li:last-of-type {
margin-right: 0;
}

.header-nav li a {
color: var(--font-color);
}

.header-nav li a img {
margin-right: 0.3em;
vertical-align: middle;
display: inline-block;
}

.header-nav li a:hover {
text-decoration: underline;
}

页脚

页脚是底部的版权注释, 可在主题配置文件中的选项copyright修改相关描述.

typo 中已有layout/_partial/footer.ejs文件,用于描述页脚的配置.

数学公式支持

如果你的markdown渲染器和作者一样为hexo-renderer-syzoj-renderer;那么此开关不是必要的; 如果是其他渲染器,可能需要这个开关的支持。

  1. 在主题配置文件中添加开关
1
mathjax: true
  1. layout/global.ejs添加如下代码,以使得渲染器支持数学公式
1
2
3
<!-- 数学公式 -->
<%- css('css/mathjax.css') %>
<%- js('js/mathjax.js') %>
  1. 添加样式文件source/css/mathjax.css使得渲染器渲染出字体为Times New Roman。以及支持让公式在小屏幕下自动换行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
mathjax.css
用于美化 MathJax 渲染的数学公式字体和排版
*/

.mathjax-formula {
font-family: 'Times New Roman', Times, serif;
font-size: 1.05em;
line-height: 1.5;
}

/* 让公式在小屏幕下自动换行 */
.MathJax_Display {
overflow-x: auto;
overflow-y: hidden;
width: 100%;
box-sizing: border-box;
}
  1. 添加脚本文件source/js/mathjax.js如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* highlight.js
* 代码块功能主入口,负责:
* 1. 语法高亮初始化
* 2. 代码块头部(语言、复制、主题切换)生成
* 3. 主题切换逻辑
*/
(function() {
// 动态加载 MathJax 脚本
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = window.THEME_CONFIG.mathjax.src ||
'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js';
script.async = true;

// 可选:自定义 MathJax 配置
window.MathJax = {
tex: {
inlineMath: [['$', '$'], ['\\(', '\\)']]
}
// 其他配置可按需添加
};

document.head.appendChild(script);
})();

(文章主体) 去掉标题前的符号#

作者不喜欢标题符号的设计,注释掉上面source/css/post.css中的以下代码即可:

1
2
3
4
5
6
7
8
9
10
11
.post-content h1::before,
.post-content h2::before,
.post-content h3::before,
.post-content h4::before,
.post-content h5::before,
.post-content h6::before {
content: "⌗";
padding-right: 10px;
color: var(--color-hashtag);
font-weight: 600;
}

(文章主体) 在无序列表前面加点

作者更习惯在无序列表前加点,修改source/css/root.css中的全局样式, 注释掉原来的样式,替换成如下样式

1
2
3
4
5
6
7
8
9
10
11
/* ul,
ol {
list-style: none;
} */
.post-content ul {
list-style: disc;
padding-left: 2em; /* 保证缩进美观 */
}
.post-content ul ul {
list-style: circle;
}

(文章可选配置) 标题自动编号

  1. 在配置文件(如 _config.typo.ymlthemes/shiroi/_config.yml)中添加开关
1
heading_numbering: true
  1. layout/_partial/head.ejs</head> 前注入两个文件。
1
2
3
4
5
6
<!-- 标题编号 -->
<% if (theme.heading_numbering || config.heading_numbering) { %>
<%- css('css/heading-numbering.css') %>
<%- js('js/heading-numbering.js') %>
<% } %>
</head>
  1. 新建一个文件 source/js/heading-numbering.js,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
document.addEventListener('DOMContentLoaded', function () {
// 只在文章页生效
if (!document.querySelector('.post')) return;

// 只处理 h2-h6
const headings = document.querySelectorAll('.post h2, .post h3, .post h4, .post h5, .post h6');
const nums = [0, 0, 0, 0, 0]; // 对应 h2-h6

headings.forEach(heading => {
const level = parseInt(heading.tagName[1]) - 2; // h2->0, h3->1, ...
if (level < 0) return; // 跳过 h1

// 当前级别+1,低级别归零
nums[level]++;
for (let i = level + 1; i < nums.length; i++) nums[i] = 0;

// 生成编号字符串
const numbering = nums.slice(0, level + 1).join('.');

// 插入编号
heading.innerHTML = `<span class="heading-number">${numbering} </span>` + heading.innerHTML;
});
});
  1. 新建一个文件 source/css/heading-numbering.css,内容如下:
1
2
3
4
5
6
.heading-number {
color: #888;
font-weight: normal;
margin-right: 0.5em;
font-size: 0.95em;
}

(文章可选配置) 三线表样式

此功能用于将标准的 Markdown 表格渲染为学术论文中常见的三线表样式,以增强可读性和专业性。该功能可通过主题配置文件中的开关进行控制。

  1. 首先,在主题配置文件 _config.yml 中添加以下开关:
1
2
# Table style
three_line_table: true
  1. themes/shiroi/layout/_partial/head.ejs 中添加以下代码,以根据配置开关加载样式文件:
1
2
3
4
<!-- 三线表样式 -->
<% if (theme.three_line_table) { %>
<%- css('css/three-line-table.css') %>
<% } %>
  1. 新建一个文件source/css/three-line-table.css ,内容如下。

为了避免样式污染用于代码块的表格(它们在渲染后也是 <table> 结构), 所有规则都使用了 :not(.hljs) 选择器进行限定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/* Three-line table style, excluding tables used for code blocks */
.post-content table:not(.hljs) {
width: 100%;
border-collapse: collapse;
border-top: 2px solid var(--color-post-content, #333);
border-bottom: 2px solid var(--color-post-content, #333);
margin: 1.5em 0;
font-size: 0.95em;
}

.post-content table:not(.hljs) th,
.post-content table:not(.hljs) td {
border: none;
padding: 0.75em 1em;
text-align: left;
}

.post-content table:not(.hljs) thead th {
border-bottom: 1px solid var(--color-post-content, #333);
text-align: center;
font-weight: 600;
}

.post-content table:not(.hljs) tbody tr:nth-child(even) {
background-color: var(--color-table-row, #f9f9f9);
}

.post-content table:not(.hljs) caption {
caption-side: bottom;
text-align: center;
font-size: 0.9em;
color: #888;
margin-top: 0.5em;
}

(文章可选配置) 图片样式

此功能用于自动居中图片,并将其 alt 文本作为带编号的图注(例如"图1:xxx")显示。

  1. 在主题配置文件 _config.yaml 中添加以下开关:
1
2
3
4
5
# Image settings
image:
enable: true # 总开关
center: true # 居中图片
caption: true # 将 alt 文本作为带编号的图注显示
  1. 修改布局文件来注入样式资源

layout/_partial/head.ejs 中注入样式:

1
2
3
4
<!-- 图片样式 -->
<% if (theme.image && theme.image.enable) { %>
<%- css('css/image-handler.css') %>
<% } %>

layout/layout.ejs<body> 标签内部添加配置注入:

1
2
3
4
5
6
<body
<% if (theme.image && theme.image.enable) { %>
data-image-center="<%= theme.image.center ? 'true' : 'false' %>"
data-image-caption="<%= theme.image.caption ? 'true' : 'false' %>"
<% } %>
>
  1. 新建文件 source/css/image-handler.css

我们使用 display: table; 技巧让 <figure> 元素的宽度自适应其内容, 从而使 margin: auto; 能够生效,实现真正的居中。

1
2
3
4
5
6
7
8
9
10
11
12
13
.post-content .image-figure {
/* This makes the figure shrink-wrap its content, allowing margin:auto to work */
display: table;
margin-left: auto;
margin-right: auto;
}

.post-content .image-figure figcaption {
margin-top: 0.8em;
font-size: 0.9em;
color: #888;
text-align: center; /* Explicitly center the caption text */
}
  1. 新建文件 source/js/image-handler.js

此脚本会查找文章中被单独包裹在 <p> 标签里的图片, 用一个带有 .image-figure 类的 <figure> 元素替换掉原来的 <p> 标签, 并根据配置添加图注。 通过 document.body.dataset 读取配置,完全解耦 JS 与模板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* image-handler.js
* 自动居中图片,并为图片添加带编号的图注(如 "图1:xxx")
* 配置通过 <body data-image-center="true" data-image-caption="true"> 注入
*/
document.addEventListener('DOMContentLoaded', () => {
const body = document.body;
const center = body.dataset.imageCenter === 'true';
const caption = body.dataset.imageCaption === 'true';

if (!center && !caption) return;

let imageCounter = 0;
const content = document.querySelector('.post-content');
if (!content) return;

const images = content.querySelectorAll('img');

images.forEach(img => {
const parent = img.parentNode;

// 只处理被单独包裹在 <p> 标签里的图片
if (parent.tagName === 'P' && parent.children.length === 1) {
if (caption && img.alt) {
imageCounter++;
const figure = document.createElement('figure');
figure.classList.add('image-figure');

const figcaption = document.createElement('figcaption');
figcaption.innerText = `图 ${imageCounter}: ${img.alt}`;

// 移动图片到新 figure
figure.appendChild(img);
figure.appendChild(figcaption);

// 替换原 <p> 标签
parent.parentNode.replaceChild(figure, parent);

} else if (center) {
// 没有图注时,仅居中
parent.style.textAlign = 'center';
}
}
});
});

(文章可选配置) 代码块样式升级

此功能旨在将默认的代码块替换为一个功能更丰富、样式更现代的组件。 它包含一个标题栏,用于显示代码语言和提供一键复制功能,并采用了仅保留关键分割线的极简设计。

最小化注释全局干扰样式

source/css/post.css中,注释掉如下代码。

1
2
3
4
5
6
7
8
9
10
11
/*
.post-content table td,
.post-content table th {
border: 1px solid #6b6b6b;
padding: 6px 13px;
}

.post-content table tr {
border-top: 1px solid #c6cbd1;
}
*/

功能开关与配置

在主题配置文件 _config.shiroi.yml 中添加以下配置,用于控制复制按钮的显示内容。

1
2
3
4
5
# Code block settings
code_block:
# When copy_button is empty, the button displays the word "copy".
# If a path to an SVG is provided, the SVG will be displayed as a clickable button.
copy_button: icon/copy.svg

代码块逻辑集成

themes/shiroi/source/js/code-block.js 文件中,实现了如下功能

  • 初始化 highlight.js;
  • 创建代码块头部;
  • 添加复制功能;
  • 主题切换功能;

此项目依赖 highlight.js 库,确保它已被引入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// themes/shiroi/source/js/code-block.js

// 确保 highlight.js 库已初始化
if (typeof hljs !== 'undefined') {
hljs.highlightAll();
}

document.addEventListener('DOMContentLoaded', () => {
const codeBlocks = document.querySelectorAll('figure.highlight');
const bodyDataset = document.body.dataset;

// 从 body 的 data-属性中获取配置
const copyButtonConfig = bodyDataset.codeBlockCopyButton;
const themeToggleEnable = bodyDataset.codeBlockThemeToggleEnable === 'true';
const themeToggleConfig = {
enable: themeToggleEnable,
to_light_button: bodyDataset.codeBlockThemeToggleToLightButton,
to_dark_button: bodyDataset.codeBlockThemeToggleToDarkButton,
};

codeBlocks.forEach(codeBlock => {
// 防止重复创建头部
if (codeBlock.querySelector('.code-block-header')) return;

// 添加 .hljs 类以防止三线表样式冲突
const table = codeBlock.querySelector('table');
if (table) {
table.classList.add('hljs');
}

// 获取语言名称
const lang = Array.from(codeBlock.classList).find(cls => cls !== 'highlight') || 'code';

// *** 函数定义:创建代码块头部 ***
const createCodeBlockHeader = (langName) => {
const header = document.createElement('div');
header.className = 'code-block-header';

const langSpan = document.createElement('span');
langSpan.className = 'code-lang-name';
langSpan.innerText = langName;
header.appendChild(langSpan);

const actionsContainer = document.createElement('div');
actionsContainer.className = 'code-block-actions';
header.appendChild(actionsContainer);

return { header, actionsContainer };
};

// *** 函数定义:添加复制按钮 ***
const addCopyButton = (actionsContainer, codeBlock, copyButtonPath) => {
const container = document.createElement('div');
container.className = 'copy-btn-container';

const copyButton = document.createElement('button');
copyButton.className = 'copy-btn';

let originalContent;

if (copyButtonPath) {
const icon = document.createElement('img');
icon.src = '/' + copyButtonPath;
copyButton.appendChild(icon);
originalContent = icon.outerHTML;
} else {
copyButton.innerText = 'copy';
originalContent = 'copy';
}

container.appendChild(copyButton);
actionsContainer.appendChild(container);

copyButton.addEventListener('click', () => {
const codeToCopy = codeBlock.querySelector('.code').innerText;
navigator.clipboard.writeText(codeToCopy).then(() => {
copyButton.innerText = 'copied!';
setTimeout(() => {
copyButton.innerHTML = originalContent;
}, 3000);
}).catch(err => {
console.error('Failed to copy text: ', err);
});
});
};

// *** 函数定义:添加主题切换按钮 ***
const addThemeToggleButton = (actionsContainer, codeBlock, toggleConfig) => {
if (!toggleConfig || !toggleConfig.enable) return;

const lightButton = document.createElement('button');
lightButton.className = 'theme-toggle-btn light';
const lightIcon = document.createElement('img');
lightIcon.src = '/' + toggleConfig.to_dark_button;
lightButton.appendChild(lightIcon);

const darkButton = document.createElement('button');
darkButton.className = 'theme-toggle-btn dark';
const darkIcon = document.createElement('img');
darkIcon.src = '/' + toggleConfig.to_light_button;
darkButton.appendChild(darkIcon);

// Initial state: assume light theme is default
darkButton.style.display = 'inline-block';
lightButton.style.display = 'none';

actionsContainer.appendChild(lightButton);
actionsContainer.appendChild(darkButton);

darkButton.addEventListener('click', () => {
const figure = darkButton.closest('figure.highlight');
if (figure) {
figure.classList.remove('theme-light');
figure.classList.add('theme-dark');
}
darkButton.style.display = 'none';
lightButton.style.display = 'inline-block';
});

lightButton.addEventListener('click', () => {
const figure = lightButton.closest('figure.highlight');
if (figure) {
figure.classList.remove('theme-dark');
figure.classList.add('theme-light');
}
lightButton.style.display = 'none';
darkButton.style.display = 'inline-block';
});
};

// 创建代码块头部
const { header, actionsContainer } = createCodeBlockHeader(lang);
codeBlock.prepend(header);

// 添加复制按钮
if (copyButtonConfig) {
addCopyButton(actionsContainer, codeBlock, copyButtonConfig);
}

// 添加主题切换按钮
if (themeToggleConfig.enable) {
addThemeToggleButton(actionsContainer, codeBlock, themeToggleConfig);
// 默认设置为浅色主题,与CSS变量的默认值保持一致
codeBlock.classList.add('theme-light');
}
});
});

CSS 样式集成

themes/shiroi/source/css/code-block-base.css 文件中集成所有基础样式。

这份样式表经过了多次迭代,拥有极高的优先级,能够覆盖主题中其他冲突的全局样式,确保最终效果的稳定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/* themes/shiroi/source/css/code-block-base.css */

/*
Shiroi Code Block Styling - FINAL
- A clean, headered code block with CSS Variables for theming.
- Language display and copy button
- Only two separator lines: one under the header, one between line numbers and code.
- Fix for rounded corners and persistent borders.
*/

/*
Define all themeable properties as CSS variables with light theme defaults.
Dark theme (or any other theme) will override these variables.
*/
figure.highlight {
--code-bg: #f7f7f7; /* Overall background of the container */
--header-bg: #f7f7f7; /* Header background */
--table-bg: white; /* Code area background */
--gutter-bg: #f7f7f7; /* Line number background, same as header */
--border-color: #eee; /* All separator lines */
--lang-name-color: #555; /* Language name text color */

border-radius: 8px;
margin: 1.5em 0;
overflow: hidden;
background-color: var(--code-bg);
}

/* Add a class to the figure when in dark mode to switch themes */
figure.highlight.theme-dark {
--code-bg: #282c34;
--header-bg: #21252b;
--table-bg: #282c34;
--gutter-bg: #21252b;
--border-color: #3a4049;
--lang-name-color: #abb2bf;
}

/* Header for language name and copy button */
.code-block-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5em 1em;
background-color: var(--header-bg);
border-bottom: 1px solid var(--border-color); /* Horizontal separator */
}

.code-lang-name {
font-size: 0.9em;
color: var(--lang-name-color);
text-transform: capitalize;
font-family: 'JetBrains Mono', monospace;
}

/* Copy button styling */
.copy-btn-container .copy-btn {
border: none;
cursor: pointer;
background-color: transparent;
opacity: 0.6;
}

.copy-btn-container .copy-btn:hover {
opacity: 1;
}

/* Ensure all button images (copy and theme toggle) have consistent size */
.copy-btn-container .copy-btn img,
.code-block-actions .theme-toggle-btn img {
width: 16px;
height: 16px;
vertical-align: middle;
}

/* --- OVERRIDES FOR CONFLICTING GLOBAL STYLES --- */

/* This is the container for the code proper */
figure.highlight table.hljs {
background-color: var(--table-bg);
width: 100%;
margin: 0;
border-spacing: 0;
border-radius: 0 0 8px 8px;
}

/* Nuke all borders on table cells from other stylesheets with high specificity */
.post-content figure.highlight table.hljs td {
border: none !important;
padding: 0 !important; /* Reset padding before applying it below */
}

/* Apply padding and the vertical separator ONLY where intended */
.post-content figure.highlight table.hljs .gutter {
background-color: var(--gutter-bg);
padding: 1em !important;
border-right: 1px solid var(--border-color) !important; /* The one true vertical line */
}

.post-content figure.highlight table.hljs .code {
padding: 1em !important;
}

/* Also color the gutter text */
.post-content figure.highlight table.hljs .gutter .line {
color: var(--lang-name-color); /* Use the same color as the lang name for now */
opacity: 0.5;
}

模板注入

为了让上述功能生效,需要在模板文件中引入对应的CSS和JS,并将配置通过 body 标签的 data- 属性注入。

themes/shiroi/layout/_partial/head.ejs 中添加:

1
2
3
4
5
6
7
<!-- 代码块基础样式 -->
<%- css('css/code-block-base.css') %>

<!-- Codeblock Theme Toggler CSS -->
<% if (theme.code_block && theme.code_block.theme_toggle && theme.code_block.theme_toggle.enable) { %>
<%- css('css/codeblock-theme-toggle.css') %>
<% } %>

themes/shiroi/layout/layout.ejs<body> 标签内部添加配置注入:

1
2
3
4
5
6
7
8
9
10
11
12
<body
<% if (theme.image && theme.image.enable) { %>
data-image-center="<%= theme.image.center ? 'true' : 'false' %>"
data-image-caption="<%= theme.image.caption ? 'true' : 'false' %>"
<% } %>
<% if (theme.code_block) { %>
data-code-block-copy-button="<%= theme.code_block.copy_button || '' %>"
data-code-block-theme-toggle-enable="<%= (theme.code_block.theme_toggle && theme.code_block.theme_toggle.enable) ? 'true' : 'false' %>"
data-code-block-theme-toggle-to-light-button="<%= (theme.code_block.theme_toggle && theme.code_block.theme_toggle.to_light_button) || '' %>"
data-code-block-theme-toggle-to-dark-button="<%= (theme.code_block.theme_toggle && theme.code_block.theme_toggle.to_dark_button) || '' %>"
<% } %>
>

themes/shiroi/layout/post.ejs 的末尾引入逻辑脚本:

1
2
3
4
<%- js('js/code-block-header.js') %>
<%- js('js/code-block-copy.js') %>
<%- js('js/code-block-theme-toggle.js') %>
<%- js('js/code-block.js') %>

代码块昼夜切换主题样式,

code_block:中添加子选项

1
2
3
4
theme_toggle:
enable: true
to_light_button: icon/light-codeblock.svg
to_dark_button: icon/dark-codeblock.svg

我们的设计是将点击代码块右上方的昼夜切换按钮,可以实现代码块内部的深浅主题切换。

  • 当按钮是to_light_button,切换至浅色主题。
  • 当按钮是to_dark_button,切换至深色主题。 代码块内部的主题由 light_theme dark_theme配置,
  • 它们可以是source/css中的css文件;
  • 也可以是指向css的 URL。

最终架构的基础布局的颜色由一组CSS变量控制,这些变量在 code-block-base.css 中定义并拥有浅色主题的默认值。

切换主题时,JavaScript仅在代码块的根元素(<figure>)上添加一个 .theme-dark 类,这个类会激活一组新的CSS变量值,从而瞬间改变所有基础颜色。

vscode-modern-light.cssvscode-modern-dark.css 文件只负责定义对应主题下的语法高亮文本颜色,并且其规则分别被 .theme-light.theme-dark 类所限定。

我们设计了一个 themes/shiroi/source/js/code-block.js 的统一脚本。所有DOM操作,包括创建标题栏、语言名称、主题切换按钮和复制按钮的逻辑,全部被整合到该脚本中,确保了正确的执行顺序,避免了时序冲突和元素重复创建。

类名切换能即时生效

我们在 layout/_partial/head.ejs同时加载浅色和深色两个主题的CSS文件。

1
2
3
4
5
6
7
8
9
10
<!-- themes/shiroi/layout/_partial/head.ejs -->
<% if (is_post()){ %>
<% if (theme.code_block && theme.code_block.theme_toggle && theme.code_block.theme_toggle.enable) { %>
<%- css(theme.code_block.theme_toggle.light_theme) %>
<%- css(theme.code_block.theme_toggle.dark_theme) %>
<% } else { %>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-light.css">
<% } %>
<script src="https://unpkg.com/@highlightjs/cdn-assets@11.9.0/highlight.min.js"></script>
<% } %>

设计核心样式

code-block-base.css文件被重构为使用CSS变量。它定义了浅色主题的默认颜色,并包含一个 .theme-dark 类用于覆盖这些变量以实现深色主题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* themes/shiroi/source/css/code-block-base.css */
figure.highlight {
--code-bg: #f7f7f7;
--header-bg: #f7f7f7;
--table-bg: white;
--gutter-bg: #f7f7f7;
--border-color: #eee;
--lang-name-color: #555;
/* ...其他样式... */
background-color: var(--code-bg);
}

figure.highlight.theme-dark {
--code-bg: #282c34;
--header-bg: #21252b;
--table-bg: #282c34;
--gutter-bg: #21252b;
--border-color: #3a4049;
--lang-name-color: #abb2bf;
}

vscode-modern-dark.cssvscode-modern-light.css这两个文件现在只包含被 .theme-dark.theme-light 限定的文本颜色规则。这两个文件可以充分的自定义。

统一脚本 (code-block.js)

这是所有魔法发生的地方。它在页面加载后执行以下操作:

  1. 遍历所有代码块。
  2. 检查并防止重复创建标题栏。
  3. 为每个代码块默认添加 .theme-light 类。
  4. body 标签的 data- 属性中读取配置。
  5. 动态创建完整的标题栏,包括语言名称、主题切换器和复制按钮,所有相关函数均在此文件中定义。
  6. 为按钮绑定事件监听器。点击时,脚本只做一件事:在 <figure> 元素上切换 .theme-light.theme-dark。 至此,整个功能改造完成,系统稳定、高效且易于维护。在完成上述重构后,语法高亮功能得以完美实现。

注意1:解决根源上的类名不匹配问题 之前遇到的问题根源在于Hexo的 _config.yml 文件中的 highlight.hljs: false 配置,它阻止了hljs-前缀的生成。我们通过以下方式解决了这个问题:

  1. 恢复配置: 我们将 _config.yml 中的 hljs 选项保持为 false,确保代码块能正确地渲染为多行。
  2. 改造主题CSS: 我们对 vscode-modern-light.cssvscode-modern-dark.css 其中所有的 .hljs- 前缀全部移除,并修正了基础文本颜色的选择器,使其与渲染器输出的HTML完全匹配。 例如,在 vscode-modern-dark.css 中:
1
2
3
4
5
6
7
8
9
/* 修改前 */
.post-content figure.highlight.theme-dark .code .hljs-keyword {
color: #c678dd;
}

/* 修改后 */
.post-content figure.highlight.theme-dark .code .keyword {
color: #c678dd;
}

通过这次修改,CSS规则最终与渲染器输出的HTML完全匹配,语法高亮功能也得以完美实现。

注意2: 解决样式冲突!我们采用高优先级地办法保证了功能的独立性和可维护性。 在调试过程中,我们发现全局样式对代码块造成了严重干扰。为了确保样式稳定生效,我们采取了以下方案:

  • themes/shiroi/source/css/post.css 中注释掉了旧的表格样式。
  • themes/shiroi/source/css/code-block-base.css 中使用了高优先级、高特异性的CSS选择器来覆盖其他冲突样式。

代码块标题栏 Mac 主题风格增强

代码块标题栏 Mac主题增强可以通过开关打开,在配置文件中添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# codeblock
code_block:
style: default
copy_button: icon/copy.svg

# Theme toggling for code blocks
# Requires `light_theme` and `dark_theme` to be enabled.
theme_toggle:
enable: true
to_light_button: icon/light-codeblock.svg
to_dark_button: icon/dark-codeblock.svg
light_theme: css/vscode-modern-light.css
dark_theme: css/vscode-modern-dark.css
mac_enhancier:
enable: true
init_folded_status: true # 新增:控制代码块初始是否折叠,仅在mac_enhancier启用时生效

此功能将在代码块标题栏左侧添加Mac风格的交通灯图标,点击交通灯可以实现代码块的折叠与展开。 当 mac_enhancier 启用且 init_folded_statustrue 时,代码块将默认以折叠状态显示。

实现细节

  1. CSS 文件 (themes/shiroi/source/css/code-block-mac-enhancer.css): 新建 themes/shiroi/source/css/code-block-mac-enhancer.css,用于定义Mac风格标题栏的样式和代码块折叠/展开的动画效果。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    /* themes/shiroi/source/css/code-block-mac-enhancer.css */

    .code-block-header.mac-style {
    position: relative;
    padding-left: 70px; /* Make space for buttons */
    }

    .code-block-header.mac-style .code-lang-name {
    text-align: center;
    flex-grow: 1; /* Allow language name to center properly */
    }

    .mac-traffic-lights {
    position: absolute;
    left: 15px;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    gap: 6px; /* Space between circles */
    }

    .mac-traffic-lights .dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    border: 0.5px solid rgba(0, 0, 0, 0.1);
    }

    .mac-traffic-lights .dot.red {
    background-color: #ff5f56;
    }

    .mac-traffic-lights .dot.yellow {
    background-color: #ffbd2e;
    }

    .mac-traffic-lights .dot.green {
    background-color: #27c93f;
    }

    /* Code block collapse/expand styles */
    figure.highlight.is-collapsed table.hljs {
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.3s ease-out;
    }

    figure.highlight table.hljs {
    max-height: 9999px; /* A large value to ensure content is visible when expanded */
    transition: max-height 0.3s ease-in;
    }
  2. JavaScript 逻辑 (themes/shiroi/source/js/code-block.js): 在 themes/shiroi/source/js/code-block.js 文件中,新增 macEnhancerEnableinitFoldedStatus 的配置读取,并在 macEnhancerEnable 启用时,根据 initFoldedStatus 的值来初始折叠代码块。 同时,为交通灯容器添加点击事件监听器,用于切换代码块的 is-collapsed 类。

    以下是相关核心逻辑的更新:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    // themes/shiroi/source/js/code-block.js

    // ... (其他现有代码) ...

    document.addEventListener('DOMContentLoaded', () => {
    const codeBlocks = document.querySelectorAll('figure.highlight');
    const bodyDataset = document.body.dataset;

    // 从 body 的 data-属性中获取配置
    const copyButtonConfig = bodyDataset.codeBlockCopyButton;
    const themeToggleEnable = bodyDataset.codeBlockThemeToggleEnable === 'true';
    const macEnhancerEnable = bodyDataset.codeBlockMacEnhancer === 'true';
    const initFoldedStatus = bodyDataset.codeBlockMacEnhancerInitFoldedStatus === 'true'; // 新增:初始折叠状态开关

    const themeToggleConfig = {
    enable: themeToggleEnable,
    to_light_button: bodyDataset.codeBlockThemeToggleToLightButton,
    to_dark_button: bodyDataset.codeBlockThemeToggleToDarkButton,
    };

    codeBlocks.forEach(codeBlock => {
    // ... (其他现有代码) ...

    // *** 函数定义:添加Mac风格交通灯 ***
    const addMacTrafficLights = (header) => {
    header.classList.add('mac-style');

    const trafficLightsContainer = document.createElement('div');
    trafficLightsContainer.className = 'mac-traffic-lights';

    const redDot = document.createElement('span');
    redDot.className = 'dot red';
    trafficLightsContainer.appendChild(redDot);

    const yellowDot = document.createElement('span');
    yellowDot.className = 'dot yellow';
    trafficLightsContainer.appendChild(yellowDot);

    const greenDot = document.createElement('span');
    greenDot.className = 'dot green';
    trafficLightsContainer.appendChild(greenDot);

    header.prepend(trafficLightsContainer);

    // Add click event to toggle collapse
    trafficLightsContainer.addEventListener('click', () => {
    codeBlock.classList.toggle('is-collapsed');
    });
    };
    // 添加Mac风格增强
    if (macEnhancerEnable) {
    addMacTrafficLights(header);
    // 如果init_folded_status为true,则初始自动折叠代码块
    if (initFoldedStatus) {
    codeBlock.classList.add('is-collapsed');
    }
    }
    });
    });
  3. 布局文件更新:

    • themes/shiroi/layout/layout.ejs: 在 <body> 标签中添加 data-code-block-mac-enhancerdata-code-block-mac-enhancer-init-folded-status 属性。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      <body
      <% if (theme.image && theme.image.enable) { %>
      data-image-center="<%= theme.image.center ? 'true' : 'false' %>"
      data-image-caption="<%= theme.image.caption ? 'true' : 'false' %>"
      <% } %>
      <% if (theme.code_block) { %>
      data-code-block-copy-button="<%= theme.code_block.copy_button || '' %>"
      data-code-block-theme-toggle-enable="<%= (theme.code_block.theme_toggle && theme.code_block.theme_toggle.enable) ? 'true' : 'false' %>"
      data-code-block-theme-toggle-to-light-button="<%= (theme.code_block.theme_toggle && theme.code_block.theme_toggle.to_light_button) || '' %>"
      data-code-block-theme-toggle-to-dark-button="<%= (theme.code_block.theme_toggle && theme.code_block.theme_toggle.to_dark_button) || '' %>"
      <% if (theme.code_block.theme_toggle && theme.code_block.theme_toggle.mac_enhancier) { %>
      data-code-block-mac-enhancer="true"
      data-code-block-mac-enhancer-init-folded-status="<%= (theme.code_block.theme_toggle.mac_enhancier.init_folded_status) ? 'true' : 'false' %>"
      <% } else { %>
      data-code-block-mac-enhancer="false"
      data-code-block-mac-enhancer-init-folded-status="false"
      <% } %>
      <% } %>
      >
    • themes/shiroi/layout/_partial/head.ejs: 条件性地加载 code-block-mac-enhancer.css
      1
      2
      3
      4
      <!-- Codeblock Mac Enhancer CSS -->
      <% if (theme.code_block && theme.code_block.theme_toggle && theme.code_block.theme_toggle.mac_enhancier && theme.code_block.theme_toggle.mac_enhancier.enable) { %>
      <%- css('css/code-block-mac-enhancer.css') %>
      <% } %>
  4. 最终调试和样式冲突

    • 此功能的实现也遵循了之前解决类名不匹配和样式冲突的原则,确保了新样式能正确应用且不干扰其他部分。

(文章可选配置) 添加跟随目录

  1. 在主题配置文件中添加选项
1
2
3
toc:
enable: true
position: left # 自定义 left/right/none
  1. 创建文件latout/_partial/toc.ejs
1
2
3
4
5
6
7
8
9
10
11
  <% if (theme.toc && theme.toc.enable && page.toc !== false) { %>
<aside class="toc-container toc-left">
<%- toc(post.content) %>
</aside>
<% } %>
```
3. 在`layout/_partial/head.ejs`中注入配置
```ejs
<% if (theme.toc && theme.toc.enable) { %>
<%- css('css/toc.css') %>
<% } %>
  1. 配置样式,创建文件cource/css/_custom/toc.styl

添加标签页

  1. 执行hexo new page tags, 在生成的index.mdFront Matter前添加 选项
1
2
3
4
title: tags
date: 2025-06-26 13:52:55
type: "tags"
layout: "tags"
  1. 创建文件layout/tags.ejs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  <div class="tag-overview-container">
<!-- <h2 class="page-title">所有标签</h2> -->
<div class="tag-card-grid">
<% site.tags.sort('name').each(function(tag){ %>
<a class="tag-card" data-tag-name="<%= tag.name %>">
<div class="tag-card-icon">
<img src="<%= url_for('icon/tag-icon.svg') %>" alt="Tag Icon" style="width:1.2em;height:1.2em;vertical-align:middle;">
</div>
<div class="tag-card-info">
<span class="tag-card-name"><%= tag.name %></span>
<span class="tag-card-count">(<%= tag.posts.length %>)</span>
</div>
</a>
<% }) %>
</div>

<div id="filtered-articles-container" class="filtered-articles-container">
<!-- Articles will be dynamically loaded here -->
</div>

<div id="all-posts-data" data-posts='<%- JSON.stringify(site.posts.map(function(post){ return {title: post.title, path: url_for(post.path), tags: post.tags.map(function(tag){ return tag.name; }), date: date(post.date, "YYYY-MM-DD")} })) %>'></div>
</div>
  1. 创建脚本文件source/js/tags-filters.js文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  document.addEventListener('DOMContentLoaded', function() {
const allPostsDataElement = document.getElementById('all-posts-data');
const filteredArticlesContainer = document.getElementById('filtered-articles-container');
let allPosts = [];

if (allPostsDataElement && allPostsDataElement.dataset.posts) {
try {
allPosts = JSON.parse(allPostsDataElement.dataset.posts);
} catch (e) {
console.error('Error parsing posts data:', e);
}
}

const tagCards = document.querySelectorAll('.tag-card');

function renderArticles(articles) {
if (articles.length === 0) {
filteredArticlesContainer.innerHTML = '<p>暂无相关文章。</p>';
return;
}

let html = '<ul class="filtered-article-list">';
articles.forEach(article => {
html += `
<li class="filtered-article-list-item">
<span class="filtered-article-date">${article.date}</span>
<a href="${article.path}" class="filtered-article-title">${article.title}</a>
</li>
`;
});
html += '</ul>';
filteredArticlesContainer.innerHTML = html;
}

tagCards.forEach(card => {
card.addEventListener('click', function(event) {
event.preventDefault(); // Prevent default link behavior
const tagName = this.dataset.tagName;

const filteredPosts = allPosts.filter(post => {
return post.tags && post.tags.includes(tagName);
});

renderArticles(filteredPosts);
});
});

// Optional: Initial rendering of all articles or a default tag's articles
// renderArticles(allPosts);
});
  1. 创建样式文件tags.css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
.tag-overview-container {
max-width: 900px;
margin: 40px auto;
padding: 20px;
background-color: #fff;
}

.page-title {
text-align: center;
margin-bottom: 30px;
color: #333;
font-size: 2em;
}

.tag-card-grid {
display: flex; /* Changed to Flexbox for better centering with wrapping */
flex-wrap: wrap; /* Allow items to wrap to the next line */
justify-content: center; /* Center flex items within their rows */
/* Removed grid-template-columns and gap as they are grid-specific */
}

.tag-card {
margin: 10px; /* Add margin for spacing between flex items */
display: flex;
align-items: center;
padding: 15px;
border: 1px solid #eee;
border-radius: 10px; /* Rounded corners for the card */
text-decoration: none;
color: inherit;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
transition: all 0.3s ease;
}

.tag-card:hover {
transform: translateY(-3px);
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
}

.tag-card-icon {
width: 30px;
height: 30px;
margin-right: 10px;
display: flex; /* For centering SVG */
align-items: center;
justify-content: center;
}

.tag-card-icon svg {
width: 100%;
height: 100%;
fill: #007bff; /* Color for the SVG icon */
}

.tag-card-info {
flex-grow: 1;
}

.tag-card-name {
font-size: 1.2em;
font-weight: bold;
color: #333;
}

.tag-card-count {
font-size: 0.9em;
color: #777;
margin-left: 5px;
}

/* Remove old styles no longer used */
.tag-item-group,
.tag-name,
.tag-name a,
.tag-name a:hover,
.tag-post-list,
.tag-post-list-item,
.tag-post-list-item a,
.tag-post-list-item a:hover,
.tag-post-list-item time {
display: none; /* Hide old elements */
}

.filtered-articles-container {
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid #eee;
}

.filtered-article-list {
list-style: none;
padding-left: 0;
}

.filtered-article-list-item {
padding: 10px 0;
border-bottom: 1px dotted #f0f0f0;
}

.filtered-article-list-item:last-child {
border-bottom: none;
}

.filtered-article-title {
text-decoration: none;
color: #333;
font-size: 1.1em;
transition: color 0.2s ease;
}

.filtered-article-title:hover {
color: #007bff;
}

添加about页(同步Github Profile)

  1. 本地克隆远端仓库git clone https://github.com/Kytolly/Kytolly.git

  2. 编写站点目录下编写同步脚本scripts/sync-about-page.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
const fs = require('fs');
const path = require('path');

const LOCAL_README_PATH = path.join(__dirname, '..', 'resume', 'Kytolly', 'README.md');
const ABOUT_PAGE_PATH = path.join(__dirname, '..', 'source', 'about', 'index.md');

async function syncAboutPage() {
try {
console.log('Reading local README content...');
let githubReadmeContent = fs.readFileSync(LOCAL_README_PATH, 'utf8');

// Remove GitHub specific elements and unnecessary lines
githubReadmeContent = githubReadmeContent.replace(/<\>.*?<\>/g, ''); // Remove < > logo < >
githubReadmeContent = githubReadmeContent.replace(/!\[.*\]\(https:\/\/github-readme-stats\.vercel\.app\/api.*?\)/g, ''); // Remove GitHub stats images
githubReadmeContent = githubReadmeContent.replace(/<script src=".*?<\/script>/g, ''); // Remove script tags
githubReadmeContent = githubReadmeContent.replace(/##\s*Repository files navigation[\s\S]*?##\s*About/g, '## About'); // Remove GitHub navigation section
githubReadmeContent = githubReadmeContent.split('## About')[0] + '## About\n' + githubReadmeContent.split('## About')[1]; // Keep the 'About' section for now and manually adjust
githubReadmeContent = githubReadmeContent.replace(/\s*###\s*\n/g, ''); // Remove empty ### lines

// Read existing about page to preserve front-matter
const existingContent = fs.readFileSync(ABOUT_PAGE_PATH, 'utf8');
const frontMatterEndIndex = existingContent.indexOf('---', 3); // Find second --- after initial front-matter
let frontMatter = '';
if (frontMatterEndIndex !== -1) {
frontMatter = existingContent.substring(0, frontMatterEndIndex + 3);
} else {
// Fallback if no second --- found, assume basic front-matter
frontMatter = `---
title: 关于
date: ${new Date().toISOString().split('T')[0]} 12:00:00
layout: page
---
`;
}

// Combine front-matter with new content
const newContent = `${frontMatter}\n\n${githubReadmeContent}`;

fs.writeFileSync(ABOUT_PAGE_PATH, newContent, 'utf8');
console.log('About page synchronized successfully with local README.');
} catch (error) {
console.error('Error synchronizing about page:', error.message);
}
}

syncAboutPage();
  1. 创建页面hexo new page about

主题外配置

启用 syzoj_renderer

  1. 安装渲染器hexo-renderer-syzoj-renderer
1
2
npm uninstall hexo-renderer-marked
npm install hexo-renderer-syzoj-renderer
  1. 在配置文件_config_yml添加选项
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# syzoj-renderer config
syzoj_renderer:
cache_file: cache.json # File to store cache, related to Hexo's base directory.
highlighter: hexo # Code highlighter, 'prism' or 'hexo', the later uses highlight.js.
options: # syzoj-renderer's options, see https://github.com/syzoj/syzoj-renderer.
highlight:
expandTab: null # expandTab > 0 to enable expand tab, which replaces one tab to that namy spaces.
wrapper: # Strings that'll be added to highlighted code's beginning and ending.
- <pre><code>
- </code></pre>
markdownItMergeCells: false # Enable markdown-it-merge-cells or not, which'll merge adjacent cells with same
# content in Markdown tables.
markdownIt: # markdown-it's options, see https://github.com/markdown-it/markdown-it.
html: true
breaks: false
linkify: true
typographer: false
markdownItMath: # markdown-it-math-loose's options, see https://github.com/Menci/markdown-it-math-loose.
inlineOpen: $
inlineClose: $
blockOpen: $$
blockClose: $$

启用 giscus

  1. 在站点配置文件_config.yml添加选项
1
2
3
4
5
6
7
8
9
10
11
12
13
14
giscus:
enable: true
repo: Kytolly/MyGiscus
repo_id: R_kgDOK2X_9Q
category: Announcements
category_id: DIC_kwDOK2X_9c4Cbicm
mapping: pathname
strict: 0
reactions_enabled: 1
emit_metadata: 1
input_position: top
theme: preferred_color_scheme
lang: zh-CN
# loading: lazy
  1. 创建文件layout/_partial/giscus.ejs,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<% if (config.giscus.enable && (page.giscus !== false) ) { %>
<script src="https://giscus.app/client.js"
data-repo="<%= config.giscus.repo %>"
data-repo-id="<%= config.giscus.repo_id %>"
data-category="<%= config.giscus.category %>"
data-category-id="<%= config.giscus.category_id %>"
data-mapping="<%= config.giscus.mapping %>"
data-strict="<%= config.giscus.strict %>"
data-reactions-enabled="<%= config.giscus.reactions_enabled %>"
data-emit-metadata="<%= config.giscus.emit_metadata %>"
data-input-position="<%= config.giscus.input_position %>"
data-theme="<%= config.giscus.theme %>"
data-lang="<%= config.giscus.lang %>"
data-loading="<%= config.giscus.loading %>"
crossorigin="anonymous"
async>
</script>
<% } %>
  1. layout/post.ejs文件末尾注入配置
1
2
<!-- 插入giscus评论区 -->
<%- partial('_partial/giscus') %>
  1. 若有必要在某个文章中关闭评论区,在文章的Front Matter添加
1
2
giscus: false