hexo-auto-toc 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +34 -0
- package/index.js +165 -0
- package/package.json +28 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 70v
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<a></a> <a href="https://www.npmjs.com/package/hexo-auto-toc"></a> [](https://hexo.io) <a href="https://www.npmjs.com/package/hexo-everyday-calendar"></a>
|
|
2
|
+
|
|
3
|
+
# hexo-auto-toc
|
|
4
|
+
|
|
5
|
+
Automatically generate a fixed table of contents that is fixed to the side of the article page.
|
|
6
|
+
|
|
7
|
+
- Getting Started
|
|
8
|
+
|
|
9
|
+
1. Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install hexo-auto-toc
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
2. The plugin will automatically parse all content within the `<article>` tag and generate a table of contents.
|
|
16
|
+
If your article page structure does not wrap the content inside an `<article>` element, you need to add it manually — typically in the `index.html` file under your `blog` folder.
|
|
17
|
+
|
|
18
|
+
- 使用
|
|
19
|
+
|
|
20
|
+
1. 安装
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install hexo-auto-toc
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
2. 插件会自动对`<article>`包含下的所有内容进行解析,自动生成目录。如果你的文章页面结构中内容没被`<article>`包裹,需要自行添加它(即blog文件夹下的index.html)
|
|
27
|
+
|
|
28
|
+
- 效果 Example
|
|
29
|
+
|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
# Recommendations
|
|
33
|
+
|
|
34
|
+
- hexo-everyday-calendar:This is a practical calendar plugin for hexo bloggers, like contribution statistics on GitHub.
|
package/index.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
hexo.extend.filter.register('after_render:html', function (html) {
|
|
4
|
+
// 用正则找文章内容的容器(这里默认文章内容在class="site-body"的div里)
|
|
5
|
+
// 如果你的主题不是这个类名,需要调整选择器
|
|
6
|
+
/*
|
|
7
|
+
[\s\S] 匹配任何字符(空白或非空白)
|
|
8
|
+
*/
|
|
9
|
+
const contentMatch = html.match(/<article([\s\S]*?)<\/article>/);
|
|
10
|
+
console.log('contentMatch',contentMatch.length)
|
|
11
|
+
if (!contentMatch) return html; // 找不到文章内容就跳过
|
|
12
|
+
|
|
13
|
+
// 给文章内容,后面插入一个目录容器div(空的)
|
|
14
|
+
// 目录的html和js在下面会动态生成
|
|
15
|
+
const tocDiv = `
|
|
16
|
+
<div id="fixed-toc-container" style="
|
|
17
|
+
|
|
18
|
+
position: fixed;
|
|
19
|
+
top: 0vh;
|
|
20
|
+
left: 0px;
|
|
21
|
+
width: 13rem;
|
|
22
|
+
max-height: 100vh;
|
|
23
|
+
overflow-y: auto;
|
|
24
|
+
overflow-x: auto;
|
|
25
|
+
padding: 10px;
|
|
26
|
+
border-left: 2px solid #ccc;
|
|
27
|
+
font-size: 14px;
|
|
28
|
+
line-height: 1.5;
|
|
29
|
+
background: #fff;
|
|
30
|
+
opacity:0.8;
|
|
31
|
+
z-index: 999;
|
|
32
|
+
box-shadow:
|
|
33
|
+
0 2px 4px rgba(0, 0, 0, 0.05), /* 第一层:近处柔和 */
|
|
34
|
+
0 8px 16px rgba(0, 0, 0, 0.08); /* 第二层:远处扩散 */
|
|
35
|
+
">
|
|
36
|
+
<strong>Contents</strong>
|
|
37
|
+
<div id="toggle_btn" style="
|
|
38
|
+
position:absolute;
|
|
39
|
+
right:1rem;
|
|
40
|
+
display:inline-block;
|
|
41
|
+
width:2rem;
|
|
42
|
+
text-align:center;
|
|
43
|
+
border:1px solid #ccc;
|
|
44
|
+
"><</div>
|
|
45
|
+
<ul id="fixed-toc-list" style="list-style: none; padding-left: 10px;"></ul>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<script>
|
|
49
|
+
|
|
50
|
+
const myDiv = document.getElementById('toggle_btn');
|
|
51
|
+
var tocList=document.getElementById("fixed-toc-list");
|
|
52
|
+
var tocContainer=document.getElementById("fixed-toc-container");
|
|
53
|
+
function handleClickBtn() {
|
|
54
|
+
console.log('Div 被点击了!');
|
|
55
|
+
if(myDiv.textContent==='<'){
|
|
56
|
+
myDiv.textContent ='>'
|
|
57
|
+
tocList.style.display='none'
|
|
58
|
+
tocContainer.style.width='8rem'
|
|
59
|
+
}else{
|
|
60
|
+
myDiv.textContent ='<'
|
|
61
|
+
tocList.style.display='block'
|
|
62
|
+
tocContainer.style.width='13rem'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
myDiv.addEventListener('click', handleClickBtn);// 绑定点击事件
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
(function() {
|
|
72
|
+
const Patternstr = "\/\\\\\d{4}\/\\\\\d{2}\/\\\\\d{2}\/\.*"; //本身是字符串插入,所以对符号要转义
|
|
73
|
+
//"^\//*"->"/"
|
|
74
|
+
//"\/.*"->".*"
|
|
75
|
+
//JS引擎中"\"转义
|
|
76
|
+
//正则表达式引擎也是"\"转义
|
|
77
|
+
const dataPattern = new RegExp(Patternstr);
|
|
78
|
+
const path = window.location.pathname;
|
|
79
|
+
console.log(dataPattern.toString(),path,dataPattern.test(path));//控制台在渲染时省略了反斜杠的可视表示。
|
|
80
|
+
if (!dataPattern.test(path)) {
|
|
81
|
+
let toc = document.getElementById('fixed-toc-container');
|
|
82
|
+
if (toc) toc.style.display = 'none';
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
var content = document.querySelector('article');
|
|
86
|
+
//var tocList = document.getElementById('fixed-toc-list');
|
|
87
|
+
if (!content || !tocList) {
|
|
88
|
+
document.getElementById('fixed-toc-container').style.display = 'none';
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
var headings = content.querySelectorAll('h1, h2, h3, h4');
|
|
92
|
+
if (headings.length === 0) {
|
|
93
|
+
document.getElementById('fixed-toc-container').style.display = 'none';
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
headings.forEach(function(heading, i) {
|
|
97
|
+
if (!heading.id) {
|
|
98
|
+
heading.id = 'heading-' + i;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
headings.forEach(function(heading) {
|
|
102
|
+
var level = parseInt(heading.tagName.substring(1));
|
|
103
|
+
var li = document.createElement('li');
|
|
104
|
+
li.style.marginLeft = (level - 1) * 15 + 'px';
|
|
105
|
+
var a = document.createElement('a');
|
|
106
|
+
a.href = '#' + heading.id;
|
|
107
|
+
a.textContent = heading.textContent;
|
|
108
|
+
a.style.color = '#555';
|
|
109
|
+
a.style.textDecoration = 'none';
|
|
110
|
+
a.onmouseover = function() { a.style.textDecoration = 'underline'; };
|
|
111
|
+
a.onmouseout = function() { a.style.textDecoration = 'none'; };
|
|
112
|
+
li.appendChild(a);
|
|
113
|
+
tocList.appendChild(li);
|
|
114
|
+
});
|
|
115
|
+
})();
|
|
116
|
+
</script>
|
|
117
|
+
`;
|
|
118
|
+
|
|
119
|
+
// 把目录div插入到</body>标签前面
|
|
120
|
+
html = html.replace(/<\/body>/, tocDiv + '</body>');
|
|
121
|
+
|
|
122
|
+
return html;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
hexo.extend.injector.register('body_end', () => {//生成静态页面时
|
|
126
|
+
return `
|
|
127
|
+
<script>
|
|
128
|
+
(function() {
|
|
129
|
+
function moveElementIfSmallScreen() {
|
|
130
|
+
const toc = document.getElementById('fixed-toc-container');
|
|
131
|
+
const article = document.querySelector("article");
|
|
132
|
+
const toggleBtn = document.getElementById('toggle_btn');
|
|
133
|
+
if (!toc || !article) return;
|
|
134
|
+
if (window.innerWidth < 1024) {
|
|
135
|
+
console.log('small screen!!')
|
|
136
|
+
if (article.firstElementChild !== toc) {
|
|
137
|
+
article.insertBefore(toc, article.firstChild);//移动节点,而非复制
|
|
138
|
+
}
|
|
139
|
+
toggleBtn.style.display='none';
|
|
140
|
+
// 替换样式:取消 fixed,设置适合正文的样式
|
|
141
|
+
toc.style.position = 'relative';
|
|
142
|
+
toc.style.top = '';
|
|
143
|
+
toc.style.left = '';
|
|
144
|
+
toc.style.width = '100%';
|
|
145
|
+
toc.style.maxHeight = 'none';
|
|
146
|
+
toc.style.overflowY = 'visible';
|
|
147
|
+
toc.style.overflowX = 'visible';
|
|
148
|
+
toc.style.padding = '1em';
|
|
149
|
+
toc.style.borderLeft = 'none';
|
|
150
|
+
toc.style.borderBottom = '1px solid #ddd';
|
|
151
|
+
toc.style.background = '#f9f9f9';
|
|
152
|
+
toc.style.opacity = '1';
|
|
153
|
+
toc.style.zIndex = '';
|
|
154
|
+
toc.style.boxShadow = 'none';
|
|
155
|
+
toc.style.margin = '1rem';
|
|
156
|
+
toc.style.paddingTop= '2rem';
|
|
157
|
+
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
window.addEventListener("DOMContentLoaded", moveElementIfSmallScreen);
|
|
161
|
+
window.addEventListener("resize", moveElementIfSmallScreen);
|
|
162
|
+
})();
|
|
163
|
+
</script>
|
|
164
|
+
`;
|
|
165
|
+
}, 'default');
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hexo-auto-toc",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A hexo plugin automatically generate a fixed table of contents that is fixed to the side of the article page.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"toc",
|
|
7
|
+
"md",
|
|
8
|
+
"hexo",
|
|
9
|
+
"Blog",
|
|
10
|
+
"contents",
|
|
11
|
+
"article"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/70v-Yoyo/hexo-auto-toc#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/70v-Yoyo/hexo-auto-toc/issues"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/70v-Yoyo/hexo-auto-toc.git"
|
|
20
|
+
},
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"author": "wyyyoyo",
|
|
23
|
+
"type": "commonjs",
|
|
24
|
+
"main": "index.js",
|
|
25
|
+
"scripts": {
|
|
26
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
27
|
+
}
|
|
28
|
+
}
|