hexo-theme-stellar 1.34.0 → 1.36.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/_config.yml +25 -19
- package/layout/_partial/comments/artalk/layout.ejs +2 -2
- package/layout/_partial/comments/artalk/script.ejs +3 -9
- package/layout/_partial/comments/beaudar/script.ejs +2 -8
- package/layout/_partial/comments/giscus/script.ejs +2 -8
- package/layout/_partial/comments/twikoo/layout.ejs +2 -2
- package/layout/_partial/comments/twikoo/script.ejs +3 -9
- package/layout/_partial/comments/utterances/script.ejs +2 -8
- package/layout/_partial/comments/waline/layout.ejs +1 -1
- package/layout/_partial/comments/waline/script.ejs +1 -7
- package/layout/_partial/head.ejs +0 -3
- package/layout/_partial/scripts/defines.ejs +118 -1
- package/layout/_partial/scripts/lazyload.ejs +7 -6
- package/layout/_partial/scripts/services.ejs +11 -13
- package/layout/_partial/scripts/utils.ejs +358 -66
- package/layout/_partial/widgets/tagtree.ejs +1 -1
- package/layout/_partial/widgets/toc.ejs +1 -4
- package/layout/_partial/widgets/tree.ejs +1 -1
- package/layout/_plugins/fancybox.ejs +23 -8
- package/layout/_plugins/scrollreveal.ejs +77 -13
- package/layout/layout.ejs +0 -1
- package/package.json +4 -2
- 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_lazyload.js +9 -9
- package/scripts/generators/search.js +3 -1
- package/scripts/helpers/json_ld.js +3 -2
- package/scripts/helpers/pretty_url.js +9 -10
- package/scripts/lib/path_utils.js +24 -0
- package/scripts/tags/lib/emoji.js +17 -2
- package/scripts/tags/lib/image.js +13 -18
- package/source/css/_common/html.styl +0 -2
- package/source/css/_components/widgets/toc.styl +2 -2
- package/source/css/_plugins/index.styl +0 -3
- package/source/css/_plugins/lazyload.styl +40 -26
- package/source/css/_plugins/scrollreveal.styl +5 -1
- package/source/js/main.js +157 -73
- package/source/js/search/algolia-search.js +62 -62
- package/source/js/search/local-search.js +37 -36
- package/source/js/services/artalk_latest_comment.js +5 -7
- package/source/js/services/contributors.js +4 -6
- package/source/js/services/fcircle.js +4 -6
- package/source/js/services/friends.js +4 -6
- package/source/js/services/friends_and_posts.js +4 -6
- package/source/js/services/ghinfo.js +6 -8
- package/source/js/services/giscus_latest_comment.js +4 -6
- package/source/js/services/mdrender.js +2 -2
- package/source/js/services/memos.js +3 -4
- package/source/js/services/rss.js +10 -12
- package/source/js/services/siteinfo.js +24 -26
- package/source/js/services/sites.js +4 -6
- package/source/js/services/timeline.js +4 -6
- package/source/js/services/twikoo_latest_comment.js +5 -7
- package/source/js/services/waline_latest_comment.js +4 -6
- package/source/js/services/weibo.js +4 -6
- package/CLAUDE.md +0 -217
- package/docs/audits/2026-08-08-stellar-analysis.md +0 -267
- package/docs/release-process.md +0 -59
- package/layout/_plugins/pjax.ejs +0 -8
- package/source/css/_plugins/pjax.styl +0 -61
- package/source/js/plugins/pjax.js +0 -646
|
@@ -1,17 +1,81 @@
|
|
|
1
|
-
<script defer src="<%- conf.js %>"></script>
|
|
2
1
|
<script>
|
|
3
2
|
utils.initPlugin(() => {
|
|
4
|
-
const els = document.querySelectorAll('.slide-up');
|
|
5
|
-
if (els.length
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
3
|
+
const els = Array.prototype.slice.call(document.querySelectorAll('.slide-up'));
|
|
4
|
+
if (els.length === 0) return;
|
|
5
|
+
|
|
6
|
+
// 吸顶/固定定位容器内的元素始终在视口内(或由容器控制显隐),
|
|
7
|
+
// 但 ScrollReveal 按文档坐标判断可见性,会误判这类元素为不可见,
|
|
8
|
+
// 导致带锚点/恢复滚动位置打开页面时组件一直不显示。
|
|
9
|
+
// 解决方式:以最近的吸顶/固定祖先作为该组元素的容器,
|
|
10
|
+
// 使可见性判断恒成立,入场动画保留且不再依赖主文档滚动。
|
|
11
|
+
const findPinnedContainer = (el) => {
|
|
12
|
+
let node = el;
|
|
13
|
+
while (node && node.nodeType === 1) {
|
|
14
|
+
const pos = window.getComputedStyle(node).position;
|
|
15
|
+
if (pos === 'sticky' || pos === 'fixed') return node;
|
|
16
|
+
node = node.parentElement;
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const pinnedGroups = new Map(); // 容器 -> 元素列表
|
|
22
|
+
const targets = [];
|
|
23
|
+
els.forEach((el) => {
|
|
24
|
+
const container = findPinnedContainer(el);
|
|
25
|
+
if (container) {
|
|
26
|
+
const group = pinnedGroups.get(container) || [];
|
|
27
|
+
group.push(el);
|
|
28
|
+
pinnedGroups.set(container, group);
|
|
29
|
+
} else {
|
|
30
|
+
targets.push(el);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
if (pinnedGroups.size === 0 && targets.length === 0) return;
|
|
34
|
+
|
|
35
|
+
// 加载/初始化失败时兜底显示内容,避免整页空白
|
|
36
|
+
let fallbackApplied = false;
|
|
37
|
+
const revealFallback = () => {
|
|
38
|
+
if (fallbackApplied) return;
|
|
39
|
+
fallbackApplied = true;
|
|
40
|
+
document.documentElement.classList.add('sr-fallback');
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const slideUp = {
|
|
44
|
+
distance: `<%- conf.distance %>`,
|
|
45
|
+
duration: `<%- conf.duration %>`,
|
|
46
|
+
interval: `<%- conf.interval %>`,
|
|
47
|
+
scale: `<%- conf.scale %>`,
|
|
48
|
+
opacity: 0,
|
|
49
|
+
easing: "ease-out"
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// 看门狗:CDN 长时间无响应时强制显示内容
|
|
53
|
+
const watchdog = setTimeout(() => revealFallback(), 3000);
|
|
54
|
+
|
|
55
|
+
utils.js(`<%- conf.js %>`, { defer: true })
|
|
56
|
+
.then(() => {
|
|
57
|
+
if (fallbackApplied) return;
|
|
58
|
+
if (typeof ScrollReveal !== 'function') {
|
|
59
|
+
revealFallback();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const sr = ScrollReveal();
|
|
64
|
+
if (targets.length > 0) {
|
|
65
|
+
sr.reveal(targets, { ...slideUp });
|
|
66
|
+
}
|
|
67
|
+
pinnedGroups.forEach((group, container) => {
|
|
68
|
+
sr.reveal(group, { ...slideUp, container: container });
|
|
69
|
+
});
|
|
70
|
+
clearTimeout(watchdog);
|
|
71
|
+
} catch (err) {
|
|
72
|
+
console.error('[Plugin scrollreveal] 初始化失败:', err);
|
|
73
|
+
revealFallback();
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
.catch((err) => {
|
|
77
|
+
console.error('[Plugin scrollreveal] 加载失败:', err);
|
|
78
|
+
revealFallback();
|
|
79
|
+
});
|
|
16
80
|
}, 'scrollreveal');
|
|
17
81
|
</script>
|
package/layout/layout.ejs
CHANGED
|
@@ -69,7 +69,6 @@ if (theme.style.prefers_theme === 'auto') {
|
|
|
69
69
|
html += `<div class="scripts">`
|
|
70
70
|
html += partial('_partial/scripts')
|
|
71
71
|
html += `</div>`
|
|
72
|
-
html += `<div class="page-loading-bar"><img src="${theme.default.loading}"></div>`
|
|
73
72
|
html += `</body>`
|
|
74
73
|
html += `</html>`
|
|
75
74
|
%>
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hexo-theme-stellar",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.36.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",
|
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
|
+
}
|
|
@@ -5,22 +5,22 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
// 懒加载强制开启:不再读取 enable 配置,no-lazy 是唯一例外
|
|
10
9
|
function lazyProcess(htmlContent) {
|
|
11
|
-
const cfg = this.theme.config.plugins.lazyload;
|
|
12
|
-
if (cfg == undefined || cfg.enable != true) {
|
|
13
|
-
return htmlContent;
|
|
14
|
-
}
|
|
15
10
|
return htmlContent.replace(/<img(.*?)src="(.*?)"(.*?)>/gi, function(imgTag, src_before, src_value, src_after) {
|
|
16
|
-
//
|
|
17
|
-
if (/data-
|
|
11
|
+
// 已由 tag 插件输出懒加载标记(data-src / data-srcset)的图片不重复处理
|
|
12
|
+
if (/data-src/gi.test(imgTag)) {
|
|
13
|
+
return imgTag;
|
|
14
|
+
}
|
|
15
|
+
// 使用 srcset 的图片交给浏览器原生处理,避免占位图与 srcset 冲突
|
|
16
|
+
if (/srcset=/gi.test(imgTag)) {
|
|
18
17
|
return imgTag;
|
|
19
18
|
}
|
|
20
19
|
if (/src="data:image(.*?)/gi.test(imgTag)) {
|
|
21
20
|
return imgTag;
|
|
22
21
|
}
|
|
23
|
-
|
|
22
|
+
// no-lazy 兼容 `no-lazy` / `no-lazy=""` 两种写法
|
|
23
|
+
if (/\bno-lazy\b/gi.test(imgTag)) {
|
|
24
24
|
return imgTag;
|
|
25
25
|
}
|
|
26
26
|
var newImgTag = imgTag;
|
|
@@ -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()
|
|
@@ -75,7 +75,8 @@ hexo.extend.helper.register('json_ld', function(args) {
|
|
|
75
75
|
|
|
76
76
|
} else if (isPage || this.is_home()) {
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
// 首页 URL 归一为带尾斜杠形式,与 canonical 保持一致
|
|
79
|
+
const url = this.is_home() ? config.url.replace(/\/?$/, '/') : this.pretty_url(page.permalink);
|
|
79
80
|
schema = {
|
|
80
81
|
'@context': 'https://schema.org',
|
|
81
82
|
'@type': 'Website',
|
|
@@ -119,4 +120,4 @@ hexo.extend.helper.register('json_ld', function(args) {
|
|
|
119
120
|
}
|
|
120
121
|
|
|
121
122
|
return `<script type="application/ld+json">${JSON.stringify(schema)}</script>`;
|
|
122
|
-
});
|
|
123
|
+
});
|
|
@@ -1,22 +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 = '') {
|
|
4
10
|
if (path.startsWith('http://') || path.startsWith('https://')) {
|
|
5
|
-
// 如果是绝对 URL,直接返回
|
|
6
11
|
return path;
|
|
7
12
|
}
|
|
8
|
-
|
|
9
13
|
let url = this.url_for(path);
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
url = url.replace(/\/index\.html$/, '/');
|
|
13
|
-
|
|
14
|
-
// 替换 /about.html → /about/
|
|
15
|
-
url = url.replace(/\.html$/, '/');
|
|
15
|
+
url = normalize_path(url);
|
|
16
16
|
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
if (!hasExtension && !url.endsWith('/')) {
|
|
17
|
+
// 添加尾 /(根路径 / 除外)
|
|
18
|
+
if (url !== '/') {
|
|
20
19
|
url += '/';
|
|
21
20
|
}
|
|
22
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 };
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
|
|
4
4
|
*
|
|
5
5
|
* {% emoji [source] name [height:1.75em] %}
|
|
6
|
+
* {% emoji url:https://example.com/emoji.png [name:alt] [height:1.75em] %}
|
|
6
7
|
*
|
|
7
8
|
*/
|
|
8
9
|
|
|
@@ -10,12 +11,26 @@
|
|
|
10
11
|
|
|
11
12
|
module.exports = ctx => function(args) {
|
|
12
13
|
const config = ctx.theme.config.tag_plugins.emoji
|
|
13
|
-
args = ctx.args.map(args, ['height'], ['source', 'name'])
|
|
14
|
+
args = ctx.args.map(args, ['url', 'height', 'name'], ['source', 'name'])
|
|
14
15
|
var el = ''
|
|
16
|
+
el += '<span class="tag-plugin emoji">'
|
|
17
|
+
if (args.url) {
|
|
18
|
+
// 直接引用外部图片,不再走 source 配置查找
|
|
19
|
+
el += '<img no-lazy="" class="inline"'
|
|
20
|
+
el += ' src="' + args.url + '"'
|
|
21
|
+
if (args.name) {
|
|
22
|
+
el += ' alt="' + args.name + '"'
|
|
23
|
+
}
|
|
24
|
+
if (args.height) {
|
|
25
|
+
el += ' style="height:' + args.height + '"'
|
|
26
|
+
}
|
|
27
|
+
el += '/>'
|
|
28
|
+
el += '</span>'
|
|
29
|
+
return el
|
|
30
|
+
}
|
|
15
31
|
if (args.source == undefined) {
|
|
16
32
|
return el
|
|
17
33
|
}
|
|
18
|
-
el += '<span class="tag-plugin emoji">'
|
|
19
34
|
if (args.name == undefined) {
|
|
20
35
|
// 省略了 source
|
|
21
36
|
for (let id in config) {
|
|
@@ -16,32 +16,27 @@ module.exports = ctx => function(args) {
|
|
|
16
16
|
if (args.height) {
|
|
17
17
|
style += 'height:' + args.height + ';'
|
|
18
18
|
}
|
|
19
|
-
// fancybox
|
|
20
|
-
var fancybox =
|
|
19
|
+
// fancybox 默认开启(不再支持全局关闭),单图可用 fancybox:false 关闭
|
|
20
|
+
var fancybox = true
|
|
21
21
|
var fancyboxHref = null
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
fancybox = false
|
|
31
|
-
} else if (args.fancybox === 'true') {
|
|
32
|
-
fancybox = args.fancybox
|
|
33
|
-
} else {
|
|
34
|
-
fancybox = true
|
|
35
|
-
fancyboxHref = args.fancybox
|
|
36
|
-
}
|
|
22
|
+
if (args.fancybox && args.fancybox.length > 0) {
|
|
23
|
+
if (args.fancybox == 'false') {
|
|
24
|
+
fancybox = false
|
|
25
|
+
} else if (args.fancybox === 'true') {
|
|
26
|
+
fancybox = true
|
|
27
|
+
} else {
|
|
28
|
+
fancybox = true
|
|
29
|
+
fancyboxHref = args.fancybox
|
|
37
30
|
}
|
|
38
31
|
}
|
|
39
32
|
|
|
40
33
|
var safeAlt = require('hexo-util').escapeHTML(args.alt || '')
|
|
34
|
+
// 懒加载占位图(1x1 透明 PNG),真实地址放在 data-src
|
|
35
|
+
const loadingImg = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABGdBTUEAALGPC/xhBQAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAAaADAAQAAAABAAAAAQAAAADa6r/EAAAAC0lEQVQIHWNgAAIAAAUAAY27m/MAAAAASUVORK5CYII='
|
|
41
36
|
function img(src, alt, style) {
|
|
42
37
|
let a = '<a data-fancybox'
|
|
43
38
|
let img = ''
|
|
44
|
-
img += `<img class="lazy" src="${
|
|
39
|
+
img += `<img class="lazy" src="${loadingImg}" data-src="${src}"`
|
|
45
40
|
if (safeAlt) {
|
|
46
41
|
img += ` alt="${safeAlt}"`
|
|
47
42
|
a += ` data-caption="${safeAlt}"`
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
.widget-body
|
|
142
142
|
grid-template-rows: 0fr
|
|
143
143
|
|
|
144
|
-
//
|
|
144
|
+
// 操作按钮(回到顶部 / 参与讨论)
|
|
145
145
|
.widget-wrapper.toc .widget-body+.widget-footer:before
|
|
146
146
|
content: ''
|
|
147
147
|
position absolute
|
|
@@ -188,4 +188,4 @@
|
|
|
188
188
|
a
|
|
189
189
|
background: var(--block)
|
|
190
190
|
a+a
|
|
191
|
-
margin-top: 4px
|
|
191
|
+
margin-top: 4px
|
|
@@ -14,9 +14,6 @@ if hexo-config('plugins.copycode.enable')
|
|
|
14
14
|
@import 'copycode'
|
|
15
15
|
if hexo-config('plugins.tianli_gpt.enable')
|
|
16
16
|
@import 'tianli_gpt'
|
|
17
|
-
if hexo-config('plugins.pjax.enable')
|
|
18
|
-
@import 'pjax'
|
|
19
|
-
|
|
20
17
|
if hexo-config('plugins.katex.enable')
|
|
21
18
|
@import 'katex'
|
|
22
19
|
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
$lazyTransitionType = hexo-config('
|
|
1
|
+
$lazyTransitionType = hexo-config('dependencies.lazyload.transition')
|
|
2
2
|
$loadingImageSize = 2rem
|
|
3
3
|
|
|
4
4
|
img:not([src])
|
|
5
5
|
visibility: hidden
|
|
6
6
|
|
|
7
|
+
.lazy
|
|
8
|
+
opacity: 0
|
|
9
|
+
z-index: 1
|
|
10
|
+
|
|
7
11
|
.lazy-box
|
|
8
12
|
position: relative
|
|
9
13
|
overflow: hidden
|
|
@@ -12,26 +16,8 @@ img:not([src])
|
|
|
12
16
|
background: var(--block)
|
|
13
17
|
min-height: 4rem
|
|
14
18
|
|
|
15
|
-
// 懒加载
|
|
16
|
-
img[data-ll-status]
|
|
17
|
-
z-index: 1
|
|
18
|
-
&:not(.loaded)
|
|
19
|
-
opacity: 0
|
|
20
|
-
&.loaded,&.error
|
|
21
|
-
opacity: 1
|
|
22
|
-
if $lazyTransitionType == 'blur'
|
|
23
|
-
trans1 all 0.5s
|
|
24
|
-
&:not(.loaded)
|
|
25
|
-
filter blur(20px)
|
|
26
|
-
-webkit-filter blur(20px)
|
|
27
|
-
&.loaded,&.error
|
|
28
|
-
filter none
|
|
29
|
-
-webkit-filter none
|
|
30
|
-
else
|
|
31
|
-
trans1 all 0.38s
|
|
32
|
-
|
|
33
19
|
// 加载占位动画
|
|
34
|
-
img
|
|
20
|
+
img.lazy.loading + .lazy-icon
|
|
35
21
|
position: absolute
|
|
36
22
|
width: $loadingImageSize
|
|
37
23
|
height: $loadingImageSize
|
|
@@ -45,13 +31,41 @@ img[data-ll-status]+.lazy-icon
|
|
|
45
31
|
background-position: center
|
|
46
32
|
|
|
47
33
|
// 加载完成
|
|
48
|
-
img
|
|
49
|
-
|
|
50
|
-
|
|
34
|
+
img.lazy.loaded + .lazy-icon,
|
|
35
|
+
img.lazy.error + .lazy-icon
|
|
36
|
+
display: none
|
|
51
37
|
|
|
52
38
|
// 加载失败
|
|
53
|
-
img
|
|
39
|
+
img.lazy.error
|
|
54
40
|
width: $loadingImageSize
|
|
55
41
|
height: $loadingImageSize
|
|
56
|
-
|
|
57
|
-
|
|
42
|
+
|
|
43
|
+
// 懒加载完成动画:使用 keyframes 而非 transition,
|
|
44
|
+
// 因为缓存图片可能在 loading 状态被绘制前就完成加载,transition 没有起点会直接闪现
|
|
45
|
+
if $lazyTransitionType == 'blur'
|
|
46
|
+
.lazy
|
|
47
|
+
filter: blur(20px)
|
|
48
|
+
-webkit-filter: blur(20px)
|
|
49
|
+
.lazy.loaded, .lazy.error
|
|
50
|
+
opacity: 1
|
|
51
|
+
filter: none
|
|
52
|
+
-webkit-filter: none
|
|
53
|
+
animation: ll-blur-in 0.5s ease-out
|
|
54
|
+
@keyframes ll-blur-in
|
|
55
|
+
from
|
|
56
|
+
opacity: 0
|
|
57
|
+
filter: blur(20px)
|
|
58
|
+
-webkit-filter: blur(20px)
|
|
59
|
+
to
|
|
60
|
+
opacity: 1
|
|
61
|
+
filter: none
|
|
62
|
+
-webkit-filter: none
|
|
63
|
+
else
|
|
64
|
+
.lazy.loaded, .lazy.error
|
|
65
|
+
opacity: 1
|
|
66
|
+
animation: ll-fade-in 0.5s ease-out
|
|
67
|
+
@keyframes ll-fade-in
|
|
68
|
+
from
|
|
69
|
+
opacity: 0
|
|
70
|
+
to
|
|
71
|
+
opacity: 1
|