hexo-theme-stellar 1.33.1 → 1.35.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/README.md +2 -0
- package/_config.yml +19 -14
- package/layout/_partial/comments/artalk/script.ejs +56 -41
- package/layout/_partial/comments/beaudar/script.ejs +35 -20
- package/layout/_partial/comments/giscus/script.ejs +34 -19
- package/layout/_partial/comments/twikoo/script.ejs +29 -14
- package/layout/_partial/comments/utterances/script.ejs +35 -20
- package/layout/_partial/comments/waline/script.ejs +40 -28
- package/layout/_partial/cover/wiki_cover.ejs +1 -1
- package/layout/_partial/head.ejs +53 -7
- package/layout/_partial/scripts/defines.ejs +21 -3
- package/layout/_partial/scripts/lazyload.ejs +2 -2
- package/layout/_partial/scripts/services.ejs +101 -41
- package/layout/_partial/scripts/utils.ejs +100 -4
- package/layout/_partial/sidebar/index_leftbar.ejs +3 -0
- package/layout/_partial/sidebar/search.ejs +3 -0
- package/layout/_partial/widgets/ghuser.ejs +18 -4
- package/layout/_partial/widgets/linklist.ejs +1 -1
- package/layout/_partial/widgets/timeline.ejs +36 -33
- package/layout/_plugins/copycode.ejs +9 -5
- package/layout/_plugins/mermaid.ejs +28 -24
- package/layout/_plugins/scrollreveal.ejs +14 -13
- package/layout/_plugins/swiper.ejs +2 -2
- package/layout/index.ejs +4 -2
- package/layout/index_topic.ejs +4 -2
- package/layout/index_wiki.ejs +6 -4
- package/layout/layout.ejs +2 -2
- package/package.json +5 -2
- package/scripts/commands/new-note.js +49 -0
- package/scripts/events/index.js +2 -1
- package/scripts/events/lib/doc_tree.js +7 -4
- package/scripts/events/lib/merge_posts.js +3 -1
- package/scripts/events/lib/notebooks.js +5 -3
- package/scripts/events/lib/path_normalize.js +22 -0
- package/scripts/filters/lib/img_onerror.js +0 -2
- package/scripts/generators/search.js +3 -1
- package/scripts/helpers/json_ld.js +14 -10
- package/scripts/helpers/pretty_url.js +12 -8
- package/scripts/lib/path_utils.js +24 -0
- package/scripts/tags/lib/chat.js +16 -9
- package/scripts/tags/lib/frame.js +2 -1
- package/scripts/tags/lib/image.js +6 -5
- package/scripts/tags/lib/link.js +6 -4
- package/scripts/tags/lib/timeline.js +101 -79
- package/source/css/_common/canonical.styl +11 -6
- package/source/css/_common/highlight.styl +4 -1
- package/source/css/_common/html.styl +4 -0
- package/source/css/_common/loading.styl +5 -1
- package/source/css/_components/md.styl +1 -0
- package/source/css/_components/pages/article-story.styl +2 -0
- package/source/css/_components/partial/article-footer.styl +1 -0
- package/source/css/_components/partial/navbar.styl +2 -0
- package/source/css/_components/sidebar/menu.styl +1 -0
- package/source/css/_components/sidebar/search.styl +4 -0
- package/source/css/_components/sidebar/sidebar.styl +12 -5
- package/source/css/_components/tag-plugins/chat.styl +6 -8
- package/source/css/_components/tag-plugins/folding.styl +1 -0
- package/source/css/_components/tag-plugins/gallery.styl +2 -0
- package/source/css/_components/tag-plugins/grid.styl +1 -0
- package/source/css/_components/tag-plugins/image.styl +3 -0
- package/source/css/_components/tag-plugins/link.styl +1 -0
- package/source/css/_components/tag-plugins/navbar.styl +15 -13
- package/source/css/_components/tag-plugins/note.styl +1 -0
- package/source/css/_components/tag-plugins/okr.styl +1 -0
- package/source/css/_components/tag-plugins/sites.styl +2 -0
- package/source/css/_components/tag-plugins/timeline.styl +9 -0
- package/source/css/_components/tag-plugins/vote.styl +2 -0
- package/source/css/_components/widgets/components.styl +1 -0
- package/source/css/_components/widgets/ghuser.styl +19 -3
- package/source/css/_components/widgets/list.styl +1 -0
- package/source/css/_components/widgets/toc.styl +3 -0
- package/source/css/_components/widgets/widgets.styl +4 -0
- package/source/css/_custom.styl +2 -1
- package/source/css/_defines/func.styl +8 -0
- package/source/css/_plugins/comments/artalk.styl +6 -2
- package/source/css/_plugins/comments/twikoo.styl +2 -0
- package/source/css/_plugins/index.styl +0 -1
- package/source/js/main.js +76 -26
- package/source/js/plugins/copycode.js +34 -27
- package/source/js/search/local-search.js +5 -2
- package/source/js/services/memos.js +42 -4
- package/source/js/services/rss.js +200 -0
package/layout/index.ejs
CHANGED
|
@@ -12,15 +12,17 @@ function layout_post_card(layout, post, content) {
|
|
|
12
12
|
if (layout == 'post' && post.cover != undefined && post.poster != undefined) {
|
|
13
13
|
layout += ' photo';
|
|
14
14
|
}
|
|
15
|
-
el += `<
|
|
15
|
+
el += `<div class="post-card-wrap${scrollreveal(' ')}">`;
|
|
16
|
+
el += `<a class="post-card ${layout}" href="${pretty_url(post.link || post.path)}">`
|
|
16
17
|
el += content;
|
|
17
18
|
el += '</a>';
|
|
19
|
+
el += '</div>';
|
|
18
20
|
return el;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
function layout_post_list(partial) {
|
|
22
24
|
var el = '';
|
|
23
|
-
el +=
|
|
25
|
+
el += `<div class="post-list post">`;
|
|
24
26
|
page.posts.each(function(post){
|
|
25
27
|
if (post.indexing != false) {
|
|
26
28
|
el += layout_post_card('post', post, partial(post));
|
package/layout/index_topic.ejs
CHANGED
|
@@ -7,8 +7,8 @@ function layout_topic_list(partial) {
|
|
|
7
7
|
if (topic == null) {
|
|
8
8
|
continue
|
|
9
9
|
}
|
|
10
|
-
el += `<div class="post-
|
|
11
|
-
el += `<a class="post-card wiki topic
|
|
10
|
+
el += `<div class="post-card-wrap${scrollreveal(' ')}">`
|
|
11
|
+
el += `<a class="post-card wiki topic" href="${pretty_url(topic.homepage?.path || '/')}">`
|
|
12
12
|
el += partial(topic)
|
|
13
13
|
el += `</a>`
|
|
14
14
|
el += `</div>`
|
|
@@ -18,9 +18,11 @@ function layout_topic_list(partial) {
|
|
|
18
18
|
function layoutDiv() {
|
|
19
19
|
var el = ''
|
|
20
20
|
el += partial('_partial/main/navbar/nav_tabs_blog')
|
|
21
|
+
el += `<div class="post-list wiki topic">`;
|
|
21
22
|
el += layout_topic_list(function(topic) {
|
|
22
23
|
return partial('_partial/main/post_list/topic_card', {topic: topic})
|
|
23
24
|
})
|
|
25
|
+
el += `</div>`
|
|
24
26
|
return el
|
|
25
27
|
}
|
|
26
28
|
%>
|
package/layout/index_wiki.ejs
CHANGED
|
@@ -12,15 +12,15 @@ function layout_wiki_list(partial) {
|
|
|
12
12
|
}
|
|
13
13
|
if (page.filter === false) {
|
|
14
14
|
// wikiList
|
|
15
|
-
el += `<div class="post-
|
|
16
|
-
el += `<a class="post-card wiki
|
|
15
|
+
el += `<div class="post-card-wrap${scrollreveal(' ')}">`
|
|
16
|
+
el += `<a class="post-card wiki" href="${pretty_url(proj.homepage?.path || '/')}">`
|
|
17
17
|
el += partial(proj)
|
|
18
18
|
el += `</a>`
|
|
19
19
|
el += `</div>`
|
|
20
20
|
} else if (proj.tags && proj.tags.includes(page.tagName) === true) {
|
|
21
21
|
// filtered wikiList
|
|
22
|
-
el += `<div class="post-
|
|
23
|
-
el += `<a class="post-card wiki
|
|
22
|
+
el += `<div class="post-card-wrap${scrollreveal(' ')} filter">`
|
|
23
|
+
el += `<a class="post-card wiki" href="${pretty_url(proj.homepage?.path || '/')}">`
|
|
24
24
|
el += partial(proj)
|
|
25
25
|
el += `</a>`
|
|
26
26
|
el += `</div>`
|
|
@@ -32,9 +32,11 @@ function layout_wiki_list(partial) {
|
|
|
32
32
|
function layoutDiv() {
|
|
33
33
|
var el = ''
|
|
34
34
|
el += partial('_partial/main/navbar/nav_tabs_wiki')
|
|
35
|
+
el += `<div class="post-list wiki">`;
|
|
35
36
|
el += layout_wiki_list(function(proj) {
|
|
36
37
|
return partial('_partial/main/post_list/wiki_card', {proj: proj})
|
|
37
38
|
})
|
|
39
|
+
el += `</div>`
|
|
38
40
|
return el
|
|
39
41
|
}
|
|
40
42
|
%>
|
package/layout/layout.ejs
CHANGED
|
@@ -47,7 +47,7 @@ if (theme.style.prefers_theme === 'auto') {
|
|
|
47
47
|
html += partial('_partial/head')
|
|
48
48
|
html += `<body>`
|
|
49
49
|
html += site_background
|
|
50
|
-
html += partial('_partial/cover/index')
|
|
50
|
+
html += `<div id="l_cover">${partial('_partial/cover/index').trim()}</div>`
|
|
51
51
|
html += `<div class="l_body ${page_type}" id="start" layout="${page.layout}" type="${article_type}" ${indent ? 'text-indent' : ''}>`
|
|
52
52
|
html += `<aside class="l_left">`
|
|
53
53
|
html += `<div class="sidebg"></div>`
|
|
@@ -62,7 +62,7 @@ if (theme.style.prefers_theme === 'auto') {
|
|
|
62
62
|
html += `<div class="main-mask" onclick="sidebar.dismiss()"></div>`
|
|
63
63
|
html += `</div>`
|
|
64
64
|
html += `<aside class="l_right">`
|
|
65
|
-
html += partial('_partial/sidebar/index_rightbar')
|
|
65
|
+
html += partial('_partial/sidebar/index_rightbar').trim()
|
|
66
66
|
html += `</aside>`
|
|
67
67
|
html += partial('_partial/menubtn')
|
|
68
68
|
html += `</div>`
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hexo-theme-stellar",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.35.0",
|
|
4
4
|
"description": "Elegant and powerful theme for Hexo.",
|
|
5
5
|
"main": "package.json",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "echo test"
|
|
7
|
+
"test": "echo test",
|
|
8
|
+
"release": "node release.js",
|
|
9
|
+
"release:dry": "node release.js --dry-run"
|
|
8
10
|
},
|
|
9
11
|
"repository": {
|
|
10
12
|
"type": "git",
|
|
@@ -22,6 +24,7 @@
|
|
|
22
24
|
},
|
|
23
25
|
"dependencies": {
|
|
24
26
|
"cheerio": "^1.1.0",
|
|
27
|
+
"glob": "^10.4.0",
|
|
25
28
|
"hexo-renderer-ejs": "^2.0.0",
|
|
26
29
|
"hexo-renderer-stylus": "^3.0.1",
|
|
27
30
|
"probe-image-size": "^7.2.3"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
|
|
4
|
+
hexo.extend.console.register('new-note', 'Create a new note', {
|
|
5
|
+
options: [
|
|
6
|
+
{ name: '-n, --name', desc: 'Note name', required: true },
|
|
7
|
+
{ name: '-bk, --book', desc: 'Notebook name', required: true },
|
|
8
|
+
{ name: '-tag, --tag', desc: 'Tags (comma separated)', required: false }
|
|
9
|
+
]
|
|
10
|
+
}, function(args, callback) {
|
|
11
|
+
const name = args.name || args.n;
|
|
12
|
+
const book = args.book || args.bk || args.k;
|
|
13
|
+
const tagStr = args.tag ||args.g ||'';
|
|
14
|
+
if (!name || !book) {
|
|
15
|
+
console.log('Usage: hexo new-note -n "name" -bk "notebook" -tag "tag1,tag2/subtag1"');
|
|
16
|
+
return callback();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const date = new Date();
|
|
20
|
+
const filename = `${name}.md`;
|
|
21
|
+
const notebookDir = path.join(hexo.source_dir, 'notebooks', book);
|
|
22
|
+
if (!fs.existsSync(notebookDir)) {
|
|
23
|
+
fs.mkdirSync(notebookDir, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
const filePath = path.join(notebookDir, filename);
|
|
26
|
+
if (fs.existsSync(filePath)) {
|
|
27
|
+
console.log(`File ${filename} already exists in ${notebookDir}.`);
|
|
28
|
+
return callback();
|
|
29
|
+
}
|
|
30
|
+
// 处理标签
|
|
31
|
+
let tags = [];
|
|
32
|
+
if (tagStr) {
|
|
33
|
+
tags = tagStr.split(',').map(t => t.trim()).filter(Boolean);
|
|
34
|
+
}
|
|
35
|
+
// 生成表头
|
|
36
|
+
const frontMatter = [
|
|
37
|
+
'---',
|
|
38
|
+
`title: ${name}`,
|
|
39
|
+
`date: ${date.getFullYear()}-${String(date.getMonth()+1).padStart(2,'0')}-${String(date.getDate()).padStart(2,'0')} ${String(date.getHours()).padStart(2,'0')}:${String(date.getMinutes()).padStart(2,'0')}:${String(date.getSeconds()).padStart(2,'0')}`,
|
|
40
|
+
`tags: [${tags.map(t => '"'+t+'"').join(', ')}]`,
|
|
41
|
+
`notebook: ${book}`,
|
|
42
|
+
'---',
|
|
43
|
+
'',
|
|
44
|
+
''
|
|
45
|
+
].join('\n');
|
|
46
|
+
fs.writeFileSync(filePath, frontMatter);
|
|
47
|
+
console.log(`Created: ${filePath}`);
|
|
48
|
+
callback();
|
|
49
|
+
});
|
package/scripts/events/index.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
5
|
hexo.on('generateBefore', () => {
|
|
6
|
+
// 页面路径归一化:xxx.html → xxx/,必须先于所有读取 page.path 的逻辑
|
|
7
|
+
require('./lib/path_normalize')(hexo);
|
|
6
8
|
// Merge config.
|
|
7
9
|
require('./lib/config')(hexo);
|
|
8
10
|
require('./lib/links')(hexo);
|
|
@@ -65,4 +67,3 @@ hexo.extend.filter.register('before_generate', async () => {
|
|
|
65
67
|
}
|
|
66
68
|
});
|
|
67
69
|
|
|
68
|
-
|
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
'use strict';
|
|
6
6
|
|
|
7
|
+
const { normalize_path } = require('../../lib/path_utils');
|
|
8
|
+
|
|
7
9
|
class WikiPage {
|
|
8
10
|
constructor(page) {
|
|
9
11
|
this.id = page._id
|
|
10
12
|
this.wiki = page.wiki
|
|
11
13
|
this.title = page.title
|
|
12
14
|
this.path = page.path
|
|
13
|
-
this.path_key = page.path
|
|
15
|
+
this.path_key = normalize_path(page.path)
|
|
14
16
|
this.layout = page.layout
|
|
15
17
|
this.updated = page.updated
|
|
16
18
|
}
|
|
@@ -107,7 +109,7 @@ module.exports = ctx => {
|
|
|
107
109
|
for (let id of Object.keys(item.tree)) {
|
|
108
110
|
const sec = item.tree[id]
|
|
109
111
|
for (let key of sec) {
|
|
110
|
-
let hs = sub_pages.filter(p => p.path_key == item.base_dir + key)
|
|
112
|
+
let hs = sub_pages.filter(p => p.path_key == normalize_path(item.base_dir + key))
|
|
111
113
|
if (hs.length > 0) {
|
|
112
114
|
homepage = hs[0]
|
|
113
115
|
break
|
|
@@ -134,8 +136,9 @@ module.exports = ctx => {
|
|
|
134
136
|
for (let title of Object.keys(item.tree)) {
|
|
135
137
|
var sec = { title: title, pages: []}
|
|
136
138
|
for (let key of item.tree[title]) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
+
const pagePathKey = normalize_path(item.base_dir + key)
|
|
140
|
+
sec.pages = sec.pages.concat(sub_pages.filter(p => p.path_key == pagePathKey))
|
|
141
|
+
others = others.filter(p => p.path_key != pagePathKey)
|
|
139
142
|
}
|
|
140
143
|
sections.push(sec)
|
|
141
144
|
}
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
+
const { normalize_path } = require('../../lib/path_utils');
|
|
9
|
+
|
|
8
10
|
class RelatedPage {
|
|
9
11
|
constructor(page) {
|
|
10
12
|
this.id = page._id
|
|
@@ -12,7 +14,7 @@ class RelatedPage {
|
|
|
12
14
|
this.topic = page.topic
|
|
13
15
|
this.title = page.title
|
|
14
16
|
this.path = page.path
|
|
15
|
-
this.path_key = page.path
|
|
17
|
+
this.path_key = normalize_path(page.path)
|
|
16
18
|
this.layout = page.layout
|
|
17
19
|
this.date = page.date
|
|
18
20
|
this.updated = page.updated
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
'use strict'
|
|
6
6
|
|
|
7
|
+
const { normalize_path } = require('../../lib/path_utils');
|
|
8
|
+
|
|
7
9
|
class NotePage {
|
|
8
10
|
constructor(page) {
|
|
9
11
|
this.id = page._id
|
|
@@ -11,7 +13,7 @@ class NotePage {
|
|
|
11
13
|
this.title = page.title
|
|
12
14
|
this.tags = page.tags
|
|
13
15
|
this.path = page.path
|
|
14
|
-
this.path_key = page.path
|
|
16
|
+
this.path_key = normalize_path(page.path)
|
|
15
17
|
this.layout = page.layout
|
|
16
18
|
this.date = page.date
|
|
17
19
|
this.updated = page.updated || page.date
|
|
@@ -39,8 +41,8 @@ function prepareNotebook(id, info, ctx) {
|
|
|
39
41
|
if (notebook.base_dir.startsWith('/')) {
|
|
40
42
|
notebook.base_dir = notebook.base_dir.substring(1)
|
|
41
43
|
}
|
|
42
|
-
if (notebook.base_dir.endsWith('/')) {
|
|
43
|
-
notebook.base_dir = notebook.base_dir
|
|
44
|
+
if (notebook.base_dir.length > 1 && !notebook.base_dir.endsWith('/')) {
|
|
45
|
+
notebook.base_dir = notebook.base_dir + '/'
|
|
44
46
|
}
|
|
45
47
|
} else {
|
|
46
48
|
const notebooksBaseDir = ctx.theme.config.site_tree.notebooks.base_dir
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 页面路径归一化:将 xxx.html 归一为目录形式 xxx/
|
|
5
|
+
* 必须在 generateBefore 最早阶段执行(先于 doc_tree / notebooks 等读取 page.path),
|
|
6
|
+
* 直接改写 Page model 存储,保证 page.permalink、JSON-LD、sitemap、search.json
|
|
7
|
+
* 与 canonical(尾斜杠格式)一致。
|
|
8
|
+
*/
|
|
9
|
+
module.exports = ctx => {
|
|
10
|
+
const data = ctx.model('Page').data || {}
|
|
11
|
+
Object.values(data).forEach(page => {
|
|
12
|
+
if (!page || typeof page.path !== 'string') {
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
if (page.layout === false || page.layout === 'false') {
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
if (page.path.endsWith('.html') && !page.path.endsWith('/index.html')) {
|
|
19
|
+
page.path = page.path.replace(/\.html$/, '/')
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* https://github.com/wzpan/hexo-generator-search
|
|
3
3
|
*/
|
|
4
4
|
const { stripHTML } = require('hexo-util')
|
|
5
|
+
const { normalize_path } = require('../lib/path_utils')
|
|
5
6
|
|
|
6
7
|
hexo.extend.generator.register('search_json_generator', function (locals) {
|
|
7
8
|
if (this.theme.config.search.service != 'local_search') { return {} }
|
|
@@ -28,7 +29,8 @@ hexo.extend.generator.register('search_json_generator', function (locals) {
|
|
|
28
29
|
temp_post.title = post.title.trim()
|
|
29
30
|
}
|
|
30
31
|
if (post.path) {
|
|
31
|
-
|
|
32
|
+
const path = normalize_path(root + post.path)
|
|
33
|
+
temp_post.path = path === '/' ? '/' : path + '/'
|
|
32
34
|
}
|
|
33
35
|
if (cfg.content != false && post.content) {
|
|
34
36
|
var content = stripHTML(post.content.replace(/<span class="line">\d+<\/span>/g, '')).trim()
|
|
@@ -6,19 +6,22 @@
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
const util = require('hexo-util');
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
hexo.extend.helper.register('json_ld', function(args) {
|
|
11
11
|
const page = this.page;
|
|
12
12
|
const config = this.config;
|
|
13
13
|
const structured_data = this.theme.structured_data;
|
|
14
14
|
const authorEmail = config.email;
|
|
15
|
-
|
|
15
|
+
let authorImage = config.avatar || (authorEmail ? this.gravatar(authorEmail) : null);
|
|
16
16
|
const isPage = page.layout == 'page';
|
|
17
|
+
if (authorImage && authorImage.startsWith("/")) {
|
|
18
|
+
authorImage = config.url.endsWith("/") ? config.url + authorImage.slice(1) : config.url + authorImage
|
|
19
|
+
}
|
|
17
20
|
|
|
18
21
|
const author = {
|
|
19
22
|
'@type': 'Person',
|
|
20
23
|
name: config.author,
|
|
21
|
-
sameAs: structured_data.
|
|
24
|
+
sameAs: structured_data.links || []
|
|
22
25
|
};
|
|
23
26
|
// Google does not accept `Person` as item type for the publisher property
|
|
24
27
|
const publisher = Object.assign({}, author, {'@type': 'Organization'});
|
|
@@ -69,10 +72,11 @@ hexo.extend.helper.register('json_ld', function(args) {
|
|
|
69
72
|
|
|
70
73
|
schema.thumbnailUrl = page.cover || page.banner;
|
|
71
74
|
schema.image = images;
|
|
72
|
-
|
|
75
|
+
|
|
73
76
|
} else if (isPage || this.is_home()) {
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
|
|
78
|
+
// 首页 URL 归一为带尾斜杠形式,与 canonical 保持一致
|
|
79
|
+
const url = this.is_home() ? config.url.replace(/\/?$/, '/') : this.pretty_url(page.permalink);
|
|
76
80
|
schema = {
|
|
77
81
|
'@context': 'https://schema.org',
|
|
78
82
|
'@type': 'Website',
|
|
@@ -99,9 +103,9 @@ hexo.extend.helper.register('json_ld', function(args) {
|
|
|
99
103
|
}
|
|
100
104
|
|
|
101
105
|
}
|
|
102
|
-
|
|
106
|
+
|
|
103
107
|
} else {
|
|
104
|
-
|
|
108
|
+
|
|
105
109
|
// default to WebPage for other layouts
|
|
106
110
|
schema = {
|
|
107
111
|
'@context': 'https://schema.org',
|
|
@@ -112,8 +116,8 @@ hexo.extend.helper.register('json_ld', function(args) {
|
|
|
112
116
|
description: config.description,
|
|
113
117
|
url: config.url
|
|
114
118
|
};
|
|
115
|
-
|
|
119
|
+
|
|
116
120
|
}
|
|
117
121
|
|
|
118
122
|
return `<script type="application/ld+json">${JSON.stringify(schema)}</script>`;
|
|
119
|
-
});
|
|
123
|
+
});
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { normalize_path } = require('../lib/path_utils');
|
|
4
|
+
|
|
5
|
+
hexo.extend.helper.register('normalize_path', function (path = '') {
|
|
6
|
+
return normalize_path(path);
|
|
7
|
+
});
|
|
8
|
+
|
|
3
9
|
hexo.extend.helper.register('pretty_url', function (path = '') {
|
|
10
|
+
if (path.startsWith('http://') || path.startsWith('https://')) {
|
|
11
|
+
return path;
|
|
12
|
+
}
|
|
4
13
|
let url = this.url_for(path);
|
|
5
14
|
|
|
6
|
-
|
|
7
|
-
url = url.replace(/\/index\.html$/, '/');
|
|
8
|
-
|
|
9
|
-
// 替换 /about.html → /about/
|
|
10
|
-
url = url.replace(/\.html$/, '/');
|
|
15
|
+
url = normalize_path(url);
|
|
11
16
|
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
if (!hasExtension && !url.endsWith('/')) {
|
|
17
|
+
// 添加尾 /(根路径 / 除外)
|
|
18
|
+
if (url !== '/') {
|
|
15
19
|
url += '/';
|
|
16
20
|
}
|
|
17
21
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 统一路径规范化:去除 .html 后缀,输出无尾斜杠的干净路径(path_key 格式)
|
|
5
|
+
* 用于路径比较和匹配
|
|
6
|
+
*/
|
|
7
|
+
function normalize_path(path = '') {
|
|
8
|
+
if (path.startsWith('http://') || path.startsWith('https://')) {
|
|
9
|
+
return path;
|
|
10
|
+
}
|
|
11
|
+
// /index.html → /
|
|
12
|
+
path = path.replace(/\/index\.html$/, '/');
|
|
13
|
+
// 目录首页(无 .html 后缀形式,如 wiki/stellar/index)→ /
|
|
14
|
+
path = path.replace(/\/index$/, '/');
|
|
15
|
+
// /xxx.html → /xxx
|
|
16
|
+
path = path.replace(/\.html$/, '');
|
|
17
|
+
// 去除尾 /
|
|
18
|
+
if (path.length > 1 && path.endsWith('/')) {
|
|
19
|
+
path = path.slice(0, -1);
|
|
20
|
+
}
|
|
21
|
+
return path;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = { normalize_path };
|
package/scripts/tags/lib/chat.js
CHANGED
|
@@ -52,7 +52,11 @@ module.exports = ctx => function(args, content) {
|
|
|
52
52
|
// users
|
|
53
53
|
var arr = content.split(/<!--\s*([\s\S]*?)\s*-->/g).filter(item => item.trim().length > 0)
|
|
54
54
|
if (arr.length > 0) {
|
|
55
|
-
|
|
55
|
+
// 避免用户在没有配置chat_users.yaml时出错
|
|
56
|
+
if (ctx.theme.config.chat_users){
|
|
57
|
+
var users = merge(ctx.theme.config.chat_users, ctx.render.renderSync({ text: (arr[0] || ''), engine: 'yaml' }));
|
|
58
|
+
}
|
|
59
|
+
var users = ctx.render.renderSync({ text: (arr[0] || ''), engine: 'yaml' });
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
|
|
@@ -286,13 +290,16 @@ module.exports = ctx => function(args, content) {
|
|
|
286
290
|
el += '<div class="user-main">'
|
|
287
291
|
|
|
288
292
|
el += '<div class="header">'
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
+
// 防止徽章为空
|
|
294
|
+
if (user['label']){
|
|
295
|
+
if (!args.labelColorStyle || args.labelColorStyle == 'dynamic') {
|
|
296
|
+
el += `<span class="label dynamic-color" style="color:var(--chat-${chatIndex}-label-text-color-${userColorMap[cell['user']]});background:var(--chat-${chatIndex}-label-bg-${userColorMap[cell['user']]});">`
|
|
297
|
+
} else {
|
|
298
|
+
el += `<span class="label hand-color" style="color:${user['label']['textColor']};background:${user['label']['bgColor']};">`
|
|
299
|
+
}
|
|
300
|
+
el += user['label']['text']
|
|
301
|
+
el += '</span>'
|
|
293
302
|
}
|
|
294
|
-
el += user['label']['text']
|
|
295
|
-
el += '</span>'
|
|
296
303
|
el += '<span class="name">'
|
|
297
304
|
el += user['name']
|
|
298
305
|
el += '</span>'
|
|
@@ -429,7 +436,7 @@ module.exports = ctx => function(args, content) {
|
|
|
429
436
|
</a>
|
|
430
437
|
`
|
|
431
438
|
} else if (cell['file']) {
|
|
432
|
-
el += ` file" url="${cell["file"]}">`
|
|
439
|
+
el += ` chat-file" url="${cell["file"]}">`
|
|
433
440
|
el += `
|
|
434
441
|
<div class="content">
|
|
435
442
|
<div class="top">
|
|
@@ -508,4 +515,4 @@ module.exports = ctx => function(args, content) {
|
|
|
508
515
|
el += '</div>' // tag-plugin chat
|
|
509
516
|
|
|
510
517
|
return el
|
|
511
|
-
}
|
|
518
|
+
}
|
|
@@ -9,11 +9,12 @@
|
|
|
9
9
|
|
|
10
10
|
module.exports = ctx => function(args) {
|
|
11
11
|
args = ctx.args.map(args, ['focus', 'img', 'video'], ['device', 'alt'])
|
|
12
|
+
const escapeHTML = require('hexo-util').escapeHTML
|
|
12
13
|
const img = args.img || ''
|
|
13
14
|
const video = args.video || ''
|
|
14
15
|
const device = args.device || ''
|
|
15
16
|
const focus = args.focus || ''
|
|
16
|
-
const alt = args.alt || ''
|
|
17
|
+
const alt = escapeHTML(args.alt || '')
|
|
17
18
|
if ((img.length == 0 && video.length == 0) || device.length == 0) {
|
|
18
19
|
return
|
|
19
20
|
}
|
|
@@ -37,13 +37,14 @@ module.exports = ctx => function(args) {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
var safeAlt = require('hexo-util').escapeHTML(args.alt || '')
|
|
40
41
|
function img(src, alt, style) {
|
|
41
42
|
let a = '<a data-fancybox'
|
|
42
43
|
let img = ''
|
|
43
44
|
img += `<img class="lazy" src="${src}" data-src="${src}"`
|
|
44
|
-
if (
|
|
45
|
-
img += ` alt="${
|
|
46
|
-
a += ` data-caption="${
|
|
45
|
+
if (safeAlt) {
|
|
46
|
+
img += ` alt="${safeAlt}"`
|
|
47
|
+
a += ` data-caption="${safeAlt}"`
|
|
47
48
|
}
|
|
48
49
|
if (fancybox && !fancyboxHref) {
|
|
49
50
|
img += ` data-fancybox="${fancybox}"`
|
|
@@ -101,9 +102,9 @@ module.exports = ctx => function(args) {
|
|
|
101
102
|
}
|
|
102
103
|
el += '</div>'
|
|
103
104
|
|
|
104
|
-
if (
|
|
105
|
+
if (safeAlt) {
|
|
105
106
|
el += '<div class="image-meta">'
|
|
106
|
-
el += '<span class="image-caption center">' +
|
|
107
|
+
el += '<span class="image-caption center">' + safeAlt + '</span>'
|
|
107
108
|
el += '</div>'
|
|
108
109
|
}
|
|
109
110
|
|
package/scripts/tags/lib/link.js
CHANGED
|
@@ -9,11 +9,13 @@
|
|
|
9
9
|
|
|
10
10
|
module.exports = ctx => function(args) {
|
|
11
11
|
const full_url_for = require('hexo-util').full_url_for.bind(ctx)
|
|
12
|
+
const escapeHTML = require('hexo-util').escapeHTML
|
|
12
13
|
args = ctx.args.map(args, ['icon', 'desc'], ['url', 'title'])
|
|
13
14
|
if (args.url == null) {
|
|
14
15
|
return '';
|
|
15
16
|
}
|
|
16
17
|
const url = full_url_for(args.url)
|
|
18
|
+
const safeTitle = escapeHTML(args.title || '')
|
|
17
19
|
args.api = ctx.theme.config.data_services.siteinfo?.api
|
|
18
20
|
if (args.api) {
|
|
19
21
|
args.api = args.api.replace('{href}', url)
|
|
@@ -25,12 +27,12 @@ module.exports = ctx => function(args) {
|
|
|
25
27
|
if (!args.icon) {
|
|
26
28
|
autofill.push('icon')
|
|
27
29
|
}
|
|
28
|
-
if (args.desc) {
|
|
30
|
+
if (args.desc !== 'true' && args.desc !== true && !args.desc) {
|
|
29
31
|
autofill.push('desc')
|
|
30
32
|
}
|
|
31
33
|
var el = ''
|
|
32
34
|
el += '<div class="tag-plugin link dis-select">'
|
|
33
|
-
el += '<a class="link-card' + (args.desc ? ' rich' : ' plain') + '" title="' +
|
|
35
|
+
el += '<a class="link-card' + (args.desc ? ' rich' : ' plain') + '" title="' + safeTitle + '" href="' + args.url + '"'
|
|
34
36
|
if (args.url.includes('://')) {
|
|
35
37
|
el += ' target="_blank" rel="external nofollow noopener noreferrer"'
|
|
36
38
|
}
|
|
@@ -49,10 +51,10 @@ module.exports = ctx => function(args) {
|
|
|
49
51
|
return el
|
|
50
52
|
}
|
|
51
53
|
function loadTitle() {
|
|
52
|
-
return '<span class="title">' + (
|
|
54
|
+
return '<span class="title">' + (safeTitle || args.url) + '</span>'
|
|
53
55
|
}
|
|
54
56
|
function loadDesc() {
|
|
55
|
-
return
|
|
57
|
+
return `<span class="cap desc footnote">${escapeHTML(args.desc)}</span>`
|
|
56
58
|
}
|
|
57
59
|
function loadLink() {
|
|
58
60
|
return '<span class="cap link footnote">' + full_url_for(args.url) + '</span>'
|