hexo-theme-stellar 1.40.0 → 1.41.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/CHANGELOG.md +41 -0
- package/_config.yml +33 -10
- package/_data/icons.yml +250 -37
- package/languages/en.yml +6 -0
- package/languages/zh-CN.yml +6 -0
- package/languages/zh-TW.yml +6 -0
- package/layout/_partial/comments/artalk/script.ejs +25 -8
- package/layout/_partial/comments/beaudar/script.ejs +1 -0
- package/layout/_partial/comments/twikoo/script.ejs +1 -0
- package/layout/_partial/comments/utterances/script.ejs +1 -0
- package/layout/_partial/comments/waline/script.ejs +1 -0
- package/layout/_partial/head.ejs +43 -7
- package/layout/_partial/main/article/article_tags.ejs +16 -0
- package/layout/_partial/main/navbar/article_banner.ejs +11 -1
- package/layout/_partial/main/notebook/note_card.ejs +1 -1
- package/layout/_partial/main/notebook/paginator.ejs +2 -2
- package/layout/_partial/main/pin_slider.ejs +2 -2
- package/layout/_partial/main/post_list/paginator.ejs +2 -2
- package/layout/_partial/menubtn.ejs +2 -2
- package/layout/_partial/scripts/defines.ejs +26 -2
- package/layout/_partial/scripts.ejs +30 -7
- package/layout/_partial/sidebar/logo.ejs +1 -1
- package/layout/_partial/sidebar/menu.ejs +1 -1
- package/layout/_partial/sidebar/search.ejs +1 -1
- package/layout/_partial/widgets/author.ejs +1 -1
- package/layout/_partial/widgets/components/link.ejs +2 -2
- package/layout/_partial/widgets/ghuser.ejs +1 -1
- package/layout/_partial/widgets/related.ejs +1 -1
- package/layout/_partial/widgets/toc.ejs +1 -1
- package/layout/_partial/widgets/tree.ejs +1 -1
- package/layout/_plugins/fancybox.ejs +4 -2
- package/layout/_plugins/mermaid.ejs +2 -1
- package/layout/_plugins/search/local_search.ejs +4 -3
- package/layout/_plugins/swiper.ejs +1 -0
- package/layout/categories.ejs +1 -1
- package/layout/page.ejs +3 -0
- package/layout/tags.ejs +1 -0
- package/package.json +1 -1
- package/scripts/events/lib/doc_tree.js +12 -208
- package/scripts/events/lib/notebooks.js +8 -148
- package/scripts/events/lib/utils.js +10 -1
- package/scripts/filters/index.js +1 -0
- package/scripts/filters/lib/img_lazyload.js +4 -0
- package/scripts/filters/lib/img_onerror.js +4 -0
- package/scripts/filters/lib/md_table.js +46 -0
- package/scripts/generators/search.js +12 -18
- package/scripts/generators/stellar-icons.js +47 -0
- package/scripts/helpers/ai_label.js +29 -0
- package/scripts/helpers/icon.js +2 -2
- package/scripts/helpers/json_ld.js +10 -13
- package/scripts/helpers/related_posts.js +0 -7
- package/scripts/helpers/seo.js +9 -0
- package/scripts/lib/ai_label.js +55 -0
- package/scripts/lib/doc_tree.js +267 -0
- package/scripts/lib/notebooks.js +181 -0
- package/scripts/lib/search_index.js +82 -0
- package/scripts/lib/seo.js +79 -0
- package/scripts/tags/lib/about.js +2 -2
- package/scripts/tags/lib/albums.js +1 -1
- package/scripts/tags/lib/banner.js +1 -1
- package/scripts/tags/lib/chat.js +49 -49
- package/scripts/tags/lib/copy.js +1 -1
- package/scripts/tags/lib/friends.js +1 -1
- package/scripts/tags/lib/gallery.js +1 -1
- package/scripts/tags/lib/hashtag.js +1 -1
- package/scripts/tags/lib/image.js +3 -3
- package/scripts/tags/lib/posters.js +1 -1
- package/scripts/tags/lib/sites.js +2 -2
- package/source/css/_common/base.styl +5 -9
- package/source/css/_common/device.styl +3 -3
- package/source/css/_components/md-table.styl +23 -0
- package/source/css/_components/pages/archives.styl +2 -22
- package/source/css/_components/pages/article-story.styl +4 -4
- package/source/css/_components/partial/article-tags.styl +10 -0
- package/source/css/_components/partial/bread-nav.styl +14 -0
- package/source/css/_components/partial/navbar.styl +10 -2
- package/source/css/_components/sidebar/search.styl +24 -4
- package/source/css/_components/tag-plugins/table.styl +16 -11
- package/source/css/_components/widgets/components.styl +6 -0
- package/source/css/_components/widgets/list.styl +6 -0
- package/source/css/_components/widgets/toc.styl +1 -1
- package/source/css/_custom.styl +1 -12
- package/source/css/_defines/func.styl +28 -3
- package/source/css/_plugins/index.styl +0 -18
- package/source/css/{_plugins/comments → comments}/artalk.styl +4 -0
- package/source/css/{_plugins/comments → comments}/twikoo.styl +4 -0
- package/source/css/{_plugins → plugins}/mermaid.styl +3 -0
- package/source/css/{_plugins → plugins}/swiper.styl +5 -1
- package/source/js/icons.js +60 -0
- package/source/js/main.js +114 -0
- package/source/js/search/highlight.js +82 -0
- package/source/js/search/local-search.js +294 -103
- package/source/js/services/timeline.js +1 -1
- package/source/js/services/weibo.js +3 -3
- package/source/js/services.js +179 -0
- package/source/js/tagtree.js +29 -0
- package/source/js/theme.js +61 -0
- package/{layout/_partial/scripts/utils.ejs → source/js/utils.js} +13 -10
- package/layout/_partial/scripts/services.ejs +0 -178
- package/layout/_partial/scripts/sidebar.ejs +0 -25
- package/layout/_partial/scripts/tagtree.ejs +0 -29
- package/layout/_partial/scripts/theme.ejs +0 -67
- /package/source/css/{_plugins/comments → comments}/beaudar.styl +0 -0
- /package/source/css/{_plugins/comments → comments}/utterances.styl +0 -0
- /package/source/css/{_plugins/comments → comments}/waline.styl +0 -0
- /package/source/css/{_plugins → plugins}/fancybox.styl +0 -0
package/source/js/main.js
CHANGED
|
@@ -97,6 +97,30 @@ const hud = {
|
|
|
97
97
|
|
|
98
98
|
const l_body = document.querySelector('.l_body');
|
|
99
99
|
|
|
100
|
+
const sidebar = {
|
|
101
|
+
leftbar: () => {
|
|
102
|
+
if (l_body) {
|
|
103
|
+
l_body.toggleAttribute('leftbar');
|
|
104
|
+
l_body.removeAttribute('rightbar');
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
rightbar: () => {
|
|
108
|
+
if (l_body) {
|
|
109
|
+
l_body.toggleAttribute('rightbar');
|
|
110
|
+
l_body.removeAttribute('leftbar');
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
dismiss: () => {
|
|
114
|
+
if (l_body) {
|
|
115
|
+
l_body.removeAttribute('leftbar');
|
|
116
|
+
l_body.removeAttribute('rightbar');
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
toggleTOC: () => {
|
|
120
|
+
document.querySelector('#data-toc').classList.toggle('collapse');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
100
124
|
// 通用平滑滚动(自定义动画,TOC / 回到顶部 / 参与讨论共用)
|
|
101
125
|
let scrollAnim = null;
|
|
102
126
|
function cancelSmoothScroll() {
|
|
@@ -217,6 +241,40 @@ function bindTocClick(widget) {
|
|
|
217
241
|
});
|
|
218
242
|
}
|
|
219
243
|
|
|
244
|
+
// 通用页内锚点平滑滚动(标题左侧 headerlink、{% navbar %} 页内导航、脚注回链等)
|
|
245
|
+
// 已被其他处理器拦截的点击(TOC、tabs、wiki #start 等)通过 defaultPrevented 跳过,避免重复滚动
|
|
246
|
+
function bindAnchorClick() {
|
|
247
|
+
document.addEventListener('click', function (e) {
|
|
248
|
+
const link = e.target.closest('a[href^="#"]');
|
|
249
|
+
if (!link || e.defaultPrevented) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const href = link.getAttribute('href');
|
|
253
|
+
if (!href || href.indexOf('#') !== 0) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
let id = href.slice(1);
|
|
257
|
+
try {
|
|
258
|
+
id = decodeURIComponent(id);
|
|
259
|
+
} catch (err) {
|
|
260
|
+
// 片段含非法编码时按原样查找
|
|
261
|
+
}
|
|
262
|
+
const target = id && document.getElementById(id);
|
|
263
|
+
if (!target) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
e.preventDefault();
|
|
267
|
+
// #start 锚点贴顶滚动,不预留 offset;其余锚点与 TOC 点击滚动保持一致(32px)
|
|
268
|
+
const offset = id === 'start' ? 0 : 32;
|
|
269
|
+
const targetY = target.getBoundingClientRect().top + window.scrollY - offset;
|
|
270
|
+
smoothScrollTo(targetY);
|
|
271
|
+
if (window.history && window.history.pushState) {
|
|
272
|
+
window.history.pushState(null, '', href);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
bindAnchorClick();
|
|
277
|
+
|
|
220
278
|
// 远程 md 渲染完成后由页面层重建右栏 TOC
|
|
221
279
|
document.addEventListener('stellar:mdrender', function (e) {
|
|
222
280
|
rebuildToc(e.detail && e.detail.target);
|
|
@@ -367,6 +425,61 @@ const init = {
|
|
|
367
425
|
}
|
|
368
426
|
} catch (e) {}
|
|
369
427
|
},
|
|
428
|
+
navbarPin: () => {
|
|
429
|
+
// 列表页 navbar top 背景条状态切换:未吸顶为卡片样式(var(--card) + 文章卡片同款阴影),
|
|
430
|
+
// 吸顶后恢复玻璃效果。在吸顶边界切换 .pinned 类,视觉由 CSS 控制。
|
|
431
|
+
// 吸顶判定直接测 navbar 的实际视口位置,而非用 scrollY 推算:
|
|
432
|
+
// 移动端浏览器顶栏伸缩会改变 scrollY(展开顶栏时 scrollY 减小),
|
|
433
|
+
// 即使 navbar 仍吸顶也可能跌破阈值,导致玻璃效果误消失。
|
|
434
|
+
const navbars = document.querySelectorAll('.navbar.top');
|
|
435
|
+
if (navbars.length === 0) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
// 视口顶部允许的偏差(px),吸收亚像素/取整误差
|
|
439
|
+
const TOLERANCE = 2;
|
|
440
|
+
let states = [];
|
|
441
|
+
function update() {
|
|
442
|
+
states.forEach((state) => {
|
|
443
|
+
const top = state.navbar.getBoundingClientRect().top;
|
|
444
|
+
state.bar.classList.toggle('pinned', top <= state.stickyTop + TOLERANCE);
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
function measure() {
|
|
448
|
+
states = [];
|
|
449
|
+
navbars.forEach((navbar) => {
|
|
450
|
+
const bar = navbar.querySelector('.navbar-blur');
|
|
451
|
+
if (bar == null) {
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
// getComputedStyle().top 自动兼容桌面 var(--gap-margin) 与移动端 8pt
|
|
455
|
+
const stickyTop = parseFloat(getComputedStyle(navbar).top) || 16;
|
|
456
|
+
states.push({
|
|
457
|
+
navbar: navbar,
|
|
458
|
+
bar: bar,
|
|
459
|
+
stickyTop: stickyTop
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
update();
|
|
463
|
+
}
|
|
464
|
+
let ticking = false;
|
|
465
|
+
window.addEventListener('scroll', () => {
|
|
466
|
+
if (ticking) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
ticking = true;
|
|
470
|
+
utils.requestAnimationFrame(() => {
|
|
471
|
+
update();
|
|
472
|
+
ticking = false;
|
|
473
|
+
});
|
|
474
|
+
}, { passive: true });
|
|
475
|
+
// 顶栏伸缩不一定触发 scroll,兜底监听 visualViewport 尺寸变化
|
|
476
|
+
if (window.visualViewport) {
|
|
477
|
+
window.visualViewport.addEventListener('resize', update);
|
|
478
|
+
}
|
|
479
|
+
window.addEventListener('resize', measure);
|
|
480
|
+
window.addEventListener('pageshow', measure);
|
|
481
|
+
measure();
|
|
482
|
+
},
|
|
370
483
|
relativeDate: (selector) => {
|
|
371
484
|
selector.forEach(item => {
|
|
372
485
|
const $this = item
|
|
@@ -509,6 +622,7 @@ stellar.initPage = function () {
|
|
|
509
622
|
init.sidebar();
|
|
510
623
|
init.wikiStart();
|
|
511
624
|
init.leftbarScroll();
|
|
625
|
+
init.navbarPin();
|
|
512
626
|
init.relativeDate(document.querySelectorAll('#post-meta time'));
|
|
513
627
|
init.registerTabsTag();
|
|
514
628
|
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// 搜索跳转高亮:URL 带 ?kw= 时在正文中高亮匹配词
|
|
2
|
+
(function () {
|
|
3
|
+
var params;
|
|
4
|
+
try {
|
|
5
|
+
params = new URLSearchParams(window.location.search);
|
|
6
|
+
} catch (e) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
var keyword = (params.get('kw') || '').trim();
|
|
10
|
+
if (!keyword) return;
|
|
11
|
+
|
|
12
|
+
function escapeRegExp(string) {
|
|
13
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function highlightKeyword() {
|
|
17
|
+
// 优先主栏正文(避免命中 wiki 封面等其它 md-text 容器)
|
|
18
|
+
var root = document.querySelector('.l_main .md-text') || document.querySelector('.md-text');
|
|
19
|
+
if (!root) return;
|
|
20
|
+
|
|
21
|
+
var re = new RegExp(escapeRegExp(keyword), 'gi');
|
|
22
|
+
var textNodes = [];
|
|
23
|
+
var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
|
|
24
|
+
acceptNode: function (node) {
|
|
25
|
+
var parent = node.parentElement;
|
|
26
|
+
if (!parent) return NodeFilter.FILTER_REJECT;
|
|
27
|
+
// 跳过脚本/样式内容与已存在的 mark 高亮
|
|
28
|
+
if (parent.closest('script, style, noscript, .tag-plugin.mark')) {
|
|
29
|
+
return NodeFilter.FILTER_REJECT;
|
|
30
|
+
}
|
|
31
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
var node;
|
|
35
|
+
while ((node = walker.nextNode())) {
|
|
36
|
+
textNodes.push(node);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
var limit = 50;
|
|
40
|
+
var count = 0;
|
|
41
|
+
for (var i = 0; i < textNodes.length && count < limit; i++) {
|
|
42
|
+
var textNode = textNodes[i];
|
|
43
|
+
var text = textNode.nodeValue || '';
|
|
44
|
+
var frag = document.createDocumentFragment();
|
|
45
|
+
var lastIndex = 0;
|
|
46
|
+
var m;
|
|
47
|
+
re.lastIndex = 0;
|
|
48
|
+
while (count < limit && (m = re.exec(text)) !== null) {
|
|
49
|
+
if (m.index > lastIndex) {
|
|
50
|
+
frag.appendChild(document.createTextNode(text.slice(lastIndex, m.index)));
|
|
51
|
+
}
|
|
52
|
+
var mark = document.createElement('mark');
|
|
53
|
+
mark.className = 'tag-plugin colorful mark';
|
|
54
|
+
mark.setAttribute('color', 'yellow');
|
|
55
|
+
mark.textContent = m[0];
|
|
56
|
+
frag.appendChild(mark);
|
|
57
|
+
count += 1;
|
|
58
|
+
lastIndex = m.index + m[0].length;
|
|
59
|
+
}
|
|
60
|
+
if (lastIndex === 0) continue;
|
|
61
|
+
if (lastIndex < text.length) {
|
|
62
|
+
frag.appendChild(document.createTextNode(text.slice(lastIndex)));
|
|
63
|
+
}
|
|
64
|
+
textNode.parentNode.replaceChild(frag, textNode);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 无锚点时(intro / 仅标题命中)滚动到第一个匹配词,实现定位
|
|
68
|
+
if (!window.location.hash) {
|
|
69
|
+
var first = root.querySelector('mark.tag-plugin.mark[color="yellow"]');
|
|
70
|
+
if (first) {
|
|
71
|
+
var top = first.getBoundingClientRect().top + window.scrollY - 32;
|
|
72
|
+
window.scrollTo({ top: Math.max(0, top), behavior: 'instant' });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (document.readyState !== 'loading') {
|
|
78
|
+
highlightKeyword();
|
|
79
|
+
} else {
|
|
80
|
+
document.addEventListener('DOMContentLoaded', highlightKeyword);
|
|
81
|
+
}
|
|
82
|
+
})();
|