对obsidian的markdown格式转hexo格式时,如何如obsidian一样很好的引入其他markdown的内容。
需求
- 可以对引用的标题,按不同级别,设置不同大小的字号。
- 引用的内容不影响了文章的目录。badcase如图所示
- 引用的代码块要正常显示。
解决方案
- 在 hexo根目录,若没有,则新建文件夹:scripts。
在文件夹中新建文件include_custom.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
108const fs = require('fs'); // 引入文件系统模块
const path = require('path'); // 引入路径模块
const { escapeHTML } = require('hexo-util'); // 引入 Hexo 工具模块中的 escapeHTML 函数
// 递归解析嵌套的 include_custom 标签
function renderIncludeCustom(content, hexo) {
const includeTagRegex = /{% include_custom ([^%]+) %}/g;
let match;
while ((match = includeTagRegex.exec(content)) !== null) {
const args = match[1].split(' ');
const nestedContent = includeCustom(args, hexo);
content = content.replace(match[0], nestedContent);
}
return content;
}
function includeCustom(args, hexo) {
const relativePath = args[0]; // 获取相对路径
const heading = args[1] ? args[1].replace(/['"]/g, '') : null; // 获取标题,如果有
const absolutePath = path.join(hexo.source_dir, relativePath); // 获取文件的绝对路径
if (!fs.existsSync(absolutePath)) {
return `File not found: ${relativePath}`; // 返回错误信息
}
let content = fs.readFileSync(absolutePath, 'utf8'); // 读取文件内容
if (heading) {
const headingLevel = heading.match(/^#+/)[0].length; // 确定标题级别
const escapedHeading = escapeHTML(heading.replace(/#+\s*/, '').trim()); // 转义标题内容
const regex = new RegExp(`(^#{${headingLevel}}\\s+${escapedHeading}\\s*$)`, 'gm'); // 创建正则表达式,匹配指定级别的标题
const match = regex.exec(content); // 查找匹配的标题
if (match) {
const startIndex = match.index; // 获取标题的起始位置
let endIndex = content.length; // 初始化内容的结束位置为文件末尾
let inCodeBlock = false; // 初始化代码块标记
let currentPos = 0; // 当前字符位置
// 查找下一个同级或更高级的标题,确定结束位置
const lines = content.split('\n');
for (let i = 0; i < lines.length; i++) {
if (currentPos > startIndex) {
const line = lines[i];
if ((line.match(/```/g) || []).length % 2 !== 0) {
inCodeBlock = !inCodeBlock; // 切换代码块标记
}
if (!inCodeBlock && new RegExp(`^#{1,${headingLevel}}\\s`).test(line)) {
endIndex = currentPos; // 更新内容的结束位置
break;
}
}
currentPos += lines[i].length + 1; // 更新当前字符位置,包含换行符
}
content = content.substring(startIndex, endIndex).trim(); // 提取指定标题下的内容
} else {
return `No match found for heading: ${heading}`; // 返回错误信息
}
}
// 标记代码块,排除代码块中的内容
let inCodeBlock = false; // 重置代码块标记
let processedContent = content.split('\n').map(line => {
if ((line.match(/```/g) || []).length % 2 !== 0) {
inCodeBlock = !inCodeBlock; // 切换代码块标记
return line; // 返回当前行(代码块起始或结束)
}
if (!inCodeBlock) {
// 如果不在代码块中,处理标题
return line.replace(/^# (.+)$/gm, '<div class="custom-quote-h1">$1</div>')
.replace(/^## (.+)$/gm, '<div class="custom-quote-h2">$1</div>')
.replace(/^### (.+)$/gm, '<div class="custom-quote-h3">$1</div>')
.replace(/^#### (.+)$/gm, '<div class="custom-quote-h4">$1</div>')
.replace(/^##### (.+)$/gm, '<div class="custom-quote-h5">$1</div>')
.replace(/^###### (.+)$/gm, '<div class="custom-quote-h6">$1</div>');
}
return line; // 返回当前行
}).join('\n'); // 合并处理后的行
// 处理代码块,将其包裹在 <pre><code> 标签中,并添加 Prism.js 类
processedContent = processedContent.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => {
const languageClass = lang ? `language-${lang}` : 'language-plaintext'; // 确定代码语言
const escapedCode = escapeHTML(code.trim()); // 转义代码内容
return `<pre class="line-numbers ${languageClass}"><code class="${languageClass}">${escapedCode}</code></pre>`; // 返回包裹后的代码块
});
// 处理内联代码块,将其包裹在 <code> 标签中
processedContent = processedContent.replace(/`([^`]+)`/g, (match, code) => {
const escapedCode = escapeHTML(code.trim()); // 转义内联代码内容
return `<code>${escapedCode}</code>`; // 返回包裹后的内联代码块
});
// 递归解析嵌套的 include_custom 标签
processedContent = renderIncludeCustom(processedContent, hexo);
// 使用 Hexo 自带的 Markdown 渲染器解析剩余内容
processedContent = hexo.render.renderSync({ text: processedContent, engine: 'markdown' });
// 包装为自定义样式
return `<div class="custom-quote">${processedContent}</div>`;
}
hexo.extend.tag.register('include_custom', function(args) {
return includeCustom(args, hexo);
}, { async: false });确保自定义 CSS 文件中有正确的样式,即在 themes/next/source/css/custom.css或source/css/custom.css 文件中添加以下样式(若没有,则新建,建议themes/next/source/css/custom.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.custom-quote {
border-left: 4px solid #ccc;
padding-left: 10px;
margin: 20px 0;
background-color: #f9f9f9;
font-style: italic;
}
.custom-quote .custom-quote-h1 {
font-size: 1.5em;
font-weight: bold;
}
.custom-quote .custom-quote-h2 {
font-size: 1.3em;
font-weight: bold;
}
.custom-quote .custom-quote-h3 {
font-size: 1.1em;
font-weight: bold;
}
.custom-quote .custom-quote-h4 {
font-size: 1em;
font-weight: bold;
}
.custom-quote .custom-quote-h5 {
font-size: 0.9em;
font-weight: bold;
}
.custom-quote .custom-quote-h6 {
font-size: 0.8em;
font-weight: bold;
}
.custom-quote pre {
background: #f5f5f5;
padding: 10px;
border-radius: 5px;
overflow: auto;
white-space: pre-wrap; /* 确保换行符被正确处理 */
}
.custom-quote code {
background: #f5f5f5;
padding: 2px 4px;
border-radius: 3px;
}
/* 更改代码块的背景颜色,确保生效 */
pre[class*="language-"] {
background: #f8f6f6 ; /* 设置为你喜欢的背景颜色 */
}
.image-row {
display: flex;
gap: 10px; /* 图片之间的间隔 */
}
.image-row img {
max-width: 100%; /* 确保图片不会超出容器宽度 */
}在
themes/next/layout/_partials/head/head.swig
末尾添加1
2
3
4
5
6
7
8
9
10
11
12{% if theme.prism_plugin.enable %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/themes/prism.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/prism.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/plugins/line-numbers/prism-line-numbers.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/plugins/line-numbers/prism-line-numbers.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/components/prism-python.min.js"></script> <!-- Python支持 -->
<script>
document.addEventListener('DOMContentLoaded', (event) => {
Prism.highlightAll();
});
</script>
{% endif %}更改代码块的背景颜色
1
2
3
4/* 更改代码块的背景颜色,确保生效 */
pre[class*="language-"] {
background: #f5f2f0 !important; /* 设置为你喜欢的背景颜色 */
}这里加!important用于确保Prism.js的样式没有覆盖自定义样式
在根目录的_config.yml中添加:
1
2prism_plugin:
enable: true重启
1
hexo clean && hexo g && hexo s