hexo-theme-stellar 1.33.0 → 1.34.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/CLAUDE.md +217 -0
- package/README.md +2 -0
- package/_config.yml +35 -26
- package/docs/audits/2026-08-08-stellar-analysis.md +267 -0
- package/docs/release-process.md +59 -0
- 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 -4
- 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 +144 -5
- 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/mathjax.ejs +47 -18
- package/layout/_plugins/mermaid.ejs +28 -24
- package/layout/_plugins/pjax.ejs +8 -0
- 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 +3 -2
- package/package.json +2 -1
- package/scripts/commands/new-note.js +49 -0
- package/scripts/filters/lib/img_onerror.js +0 -2
- package/scripts/helpers/json_ld.js +11 -8
- package/scripts/helpers/pretty_url.js +5 -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 +3 -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 +4 -2
- 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 +6 -3
- package/source/css/_defines/func.styl +24 -8
- package/source/css/_defines/theme_base.styl +1 -1
- package/source/css/_plugins/comments/artalk.styl +6 -2
- package/source/css/_plugins/comments/twikoo.styl +2 -0
- package/source/css/_plugins/index.styl +2 -0
- package/source/css/_plugins/pjax.styl +61 -0
- package/source/js/main.js +80 -26
- package/source/js/plugins/copycode.js +34 -27
- package/source/js/plugins/pjax.js +646 -0
- 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
|
@@ -1,86 +1,114 @@
|
|
|
1
1
|
<script defer>
|
|
2
|
-
|
|
2
|
+
const initServices = () => {
|
|
3
|
+
// 用于存储需要清理的资源
|
|
4
|
+
let timers = [];
|
|
5
|
+
|
|
3
6
|
ctx.services = Object.assign({}, JSON.parse(`<%- JSON.stringify(theme.data_services) %>`));
|
|
4
7
|
for (let id of Object.keys(ctx.services)) {
|
|
5
8
|
const js = ctx.services[id].js;
|
|
6
9
|
if (id == 'siteinfo') {
|
|
7
|
-
|
|
8
|
-
if (
|
|
10
|
+
const cardlinks = document.querySelectorAll('a.link-card[cardlink]');
|
|
11
|
+
if (cardlinks?.length > 0) {
|
|
9
12
|
utils.js(js, { defer: true }).then(function () {
|
|
10
|
-
setCardLink(
|
|
13
|
+
setCardLink(cardlinks);
|
|
11
14
|
});
|
|
12
15
|
}
|
|
16
|
+
} else if (id == 'ghinfo') {
|
|
17
|
+
const els = document.querySelectorAll('.ds-ghinfo');
|
|
18
|
+
if (els.length > 0) {
|
|
19
|
+
utils.js(js, { defer: true });
|
|
20
|
+
}
|
|
13
21
|
} else if (id == 'voice') {
|
|
14
|
-
|
|
15
|
-
if (
|
|
22
|
+
const voiceAudios = document.querySelectorAll('.voice>audio');
|
|
23
|
+
if (voiceAudios?.length > 0) {
|
|
16
24
|
utils.js(js, { defer: true }).then(function () {
|
|
17
|
-
createVoiceDom(
|
|
25
|
+
createVoiceDom(voiceAudios);
|
|
18
26
|
});
|
|
19
27
|
}
|
|
20
28
|
} else if (id == 'video') {
|
|
21
|
-
|
|
22
|
-
if (
|
|
29
|
+
const videos = document.querySelectorAll('.video>video');
|
|
30
|
+
if (videos?.length > 0) {
|
|
23
31
|
utils.js(js, { defer: true }).then(function () {
|
|
24
|
-
videoEvents(
|
|
32
|
+
videoEvents(videos);
|
|
25
33
|
});
|
|
26
34
|
}
|
|
27
35
|
} else if (id == 'download-file') {
|
|
28
|
-
|
|
29
|
-
if (
|
|
36
|
+
const files = document.querySelectorAll('.chat-file');
|
|
37
|
+
if (files?.length > 0) {
|
|
30
38
|
utils.js(js, { defer: true }).then(function () {
|
|
31
|
-
downloadFileEvent(
|
|
39
|
+
downloadFileEvent(files);
|
|
32
40
|
});
|
|
33
41
|
}
|
|
34
42
|
} else {
|
|
35
43
|
const els = document.getElementsByClassName(`ds-${id}`);
|
|
36
44
|
if (els?.length > 0) {
|
|
37
45
|
utils.jq(() => {
|
|
38
|
-
if (id == 'timeline' || 'memos' || 'marked') {
|
|
46
|
+
if (id == 'timeline' || id == 'memos' || id == 'marked') {
|
|
39
47
|
utils.js(deps.marked).then(function () {
|
|
40
48
|
utils.js(js, { defer: true });
|
|
41
49
|
});
|
|
42
50
|
} else {
|
|
43
51
|
utils.js(js, { defer: true });
|
|
44
52
|
}
|
|
45
|
-
});
|
|
53
|
+
}, false);
|
|
46
54
|
}
|
|
47
55
|
}
|
|
48
56
|
}
|
|
49
57
|
|
|
58
|
+
// search
|
|
59
|
+
if (ctx.search && typeof searchFunc === 'function') {
|
|
60
|
+
const $inputArea = $("input#search-input");
|
|
61
|
+
if ($inputArea.length > 0 && !$inputArea[0]._searchInitialized) {
|
|
62
|
+
const path = ctx.search.path.startsWith('/') ? ctx.root + ctx.search.path.substring(1) : ctx.root + ctx.search.path;
|
|
63
|
+
const filter = $inputArea.attr('data-filter') || '';
|
|
64
|
+
searchFunc(path, filter, 'search-wrapper', 'search-input', 'search-result');
|
|
65
|
+
// 标记为已初始化,防止重复绑定
|
|
66
|
+
$inputArea[0]._searchInitialized = true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
50
70
|
// chat iphone time
|
|
51
71
|
let phoneTimes = document.querySelectorAll('.chat .status-bar .time');
|
|
72
|
+
let firstAdjustInterval = null;
|
|
73
|
+
let mainInterval = null;
|
|
52
74
|
|
|
53
75
|
if (phoneTimes.length > 0) {
|
|
54
76
|
NowTime();
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
function firstAdjustTime() {
|
|
61
|
-
NowTime();
|
|
62
|
-
clearInterval(firstAdjustInterval);
|
|
63
|
-
setInterval(NowTime, 1000 * 60);
|
|
64
|
-
}
|
|
77
|
+
const date = new Date();
|
|
78
|
+
const sec = date.getSeconds();
|
|
79
|
+
firstAdjustInterval = setInterval(firstAdjustTime, 1000 * (60 - sec));
|
|
80
|
+
timers.push(firstAdjustInterval);
|
|
65
81
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
82
|
+
function firstAdjustTime() {
|
|
83
|
+
NowTime();
|
|
84
|
+
if (firstAdjustInterval) {
|
|
85
|
+
clearInterval(firstAdjustInterval);
|
|
86
|
+
firstAdjustInterval = null;
|
|
87
|
+
}
|
|
88
|
+
mainInterval = setInterval(NowTime, 1000 * 60);
|
|
89
|
+
timers.push(mainInterval);
|
|
73
90
|
}
|
|
74
|
-
};
|
|
75
91
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
92
|
+
function NowTime() {
|
|
93
|
+
for (let i = 0; i < phoneTimes.length; ++i) {
|
|
94
|
+
const timeSpan = phoneTimes[i];
|
|
95
|
+
const date = new Date();
|
|
96
|
+
const hour = date.getHours();
|
|
97
|
+
const min = date.getMinutes();
|
|
98
|
+
timeSpan.innerHTML = check(hour) + ":" + check(min);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
function check(val) {
|
|
103
|
+
if (val < 10) {
|
|
104
|
+
return ("0" + val);
|
|
105
|
+
}
|
|
106
|
+
return (val);
|
|
79
107
|
}
|
|
80
|
-
return (val);
|
|
81
108
|
}
|
|
82
109
|
|
|
83
|
-
// chat quote
|
|
110
|
+
// chat quote - 存储事件监听器以便清理
|
|
111
|
+
const quoteClickHandlers = new Map();
|
|
84
112
|
const chat_quote_obverser = new IntersectionObserver((entries, observer) => {
|
|
85
113
|
entries.filter((entry) => { return entry.isIntersecting }).sort((a, b) => a.intersectionRect.y !== b.intersectionRect.y ? a.intersectionRect.y - b.intersectionRect.y : a.intersectionRect.x - b.intersectionRect.x).forEach((entry, index) => {
|
|
86
114
|
observer.unobserve(entry.target);
|
|
@@ -95,7 +123,7 @@
|
|
|
95
123
|
|
|
96
124
|
var chatQuotes = document.querySelectorAll(".chat .talk .quote");
|
|
97
125
|
chatQuotes.forEach((quote) => {
|
|
98
|
-
|
|
126
|
+
const handler = function () {
|
|
99
127
|
var chatCellDom = document.getElementById("quote-" + quote.getAttribute("quotedCellTag"));
|
|
100
128
|
if (chatCellDom) {
|
|
101
129
|
var chatDiv = chatCellDom.parentElement;
|
|
@@ -114,7 +142,39 @@
|
|
|
114
142
|
}
|
|
115
143
|
chat_quote_obverser.observe(chatCellDom);
|
|
116
144
|
}
|
|
117
|
-
}
|
|
145
|
+
};
|
|
146
|
+
quote.addEventListener('click', handler);
|
|
147
|
+
quoteClickHandlers.set(quote, handler); // 保存处理器引用
|
|
118
148
|
});
|
|
119
|
-
|
|
149
|
+
|
|
150
|
+
// 返回清理函数,用于清理定时器和观察器
|
|
151
|
+
return () => {
|
|
152
|
+
// 清理所有定时器(包括可能未完成的 firstAdjustInterval)
|
|
153
|
+
timers.forEach(timer => {
|
|
154
|
+
if (timer) clearInterval(timer);
|
|
155
|
+
});
|
|
156
|
+
timers = [];
|
|
157
|
+
if (firstAdjustInterval) {
|
|
158
|
+
clearInterval(firstAdjustInterval);
|
|
159
|
+
firstAdjustInterval = null;
|
|
160
|
+
}
|
|
161
|
+
if (mainInterval) {
|
|
162
|
+
clearInterval(mainInterval);
|
|
163
|
+
mainInterval = null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// 断开观察器
|
|
167
|
+
if (chat_quote_obverser) {
|
|
168
|
+
chat_quote_obverser.disconnect();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// 移除所有 click 事件监听器
|
|
172
|
+
quoteClickHandlers.forEach((handler, quote) => {
|
|
173
|
+
quote.removeEventListener('click', handler);
|
|
174
|
+
});
|
|
175
|
+
quoteClickHandlers.clear();
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
utils.initPlugin(initServices, 'services');
|
|
120
180
|
</script>
|
|
@@ -18,9 +18,10 @@
|
|
|
18
18
|
this.list.push(f);
|
|
19
19
|
};
|
|
20
20
|
this.remove = (name) => {
|
|
21
|
-
|
|
21
|
+
// 倒序遍历避免 splice 后的索引问题
|
|
22
|
+
for (let index = this.list.length - 1; index >= 0; index--) {
|
|
22
23
|
const e = this.list[index];
|
|
23
|
-
if (e.name
|
|
24
|
+
if (e.name === name) {
|
|
24
25
|
this.list.splice(index, 1);
|
|
25
26
|
}
|
|
26
27
|
}
|
|
@@ -41,8 +42,15 @@
|
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
const utils = {
|
|
45
|
+
// 已加载样式缓存
|
|
46
|
+
_loadedStyles: new Set(),
|
|
47
|
+
|
|
44
48
|
// 懒加载 css https://github.com/filamentgroup/loadCSS
|
|
45
49
|
css: (href, before, media, attributes) => {
|
|
50
|
+
// 如果样式已加载,直接返回 null
|
|
51
|
+
if (utils._loadedStyles.has(href)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
46
54
|
var doc = window.document;
|
|
47
55
|
var ss = doc.createElement("link");
|
|
48
56
|
var ref;
|
|
@@ -63,6 +71,8 @@
|
|
|
63
71
|
ss.rel = "stylesheet";
|
|
64
72
|
ss.href = href;
|
|
65
73
|
ss.media = "only x";
|
|
74
|
+
// 标记样式为已加载 (在创建元素后立即标记,防止重复创建)
|
|
75
|
+
utils._loadedStyles.add(href);
|
|
66
76
|
function ready(cb) {
|
|
67
77
|
if (doc.body) {
|
|
68
78
|
return cb();
|
|
@@ -100,11 +110,22 @@
|
|
|
100
110
|
return ss;
|
|
101
111
|
},
|
|
102
112
|
|
|
113
|
+
// 已加载脚本缓存
|
|
114
|
+
_loadedScripts: new Set(),
|
|
115
|
+
|
|
116
|
+
// 已加载元素缓存 (使用 WeakSet 追踪元素实例,避免 PJAX 导航时的状态混淆)
|
|
117
|
+
_loadedElements: new WeakSet(),
|
|
118
|
+
|
|
103
119
|
js: (src, opt) => new Promise((resolve, reject) => {
|
|
104
|
-
var script = document.createElement('script');
|
|
105
120
|
if (src.startsWith('/')) {
|
|
106
121
|
src = ctx.root + src.substring(1);
|
|
107
122
|
}
|
|
123
|
+
// 如果脚本已加载,直接返回
|
|
124
|
+
if (utils._loadedScripts.has(src)) {
|
|
125
|
+
resolve();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
var script = document.createElement('script');
|
|
108
129
|
script.src = src;
|
|
109
130
|
if (opt) {
|
|
110
131
|
for (let key of Object.keys(opt)) {
|
|
@@ -119,22 +140,42 @@
|
|
|
119
140
|
const loadState = this.readyState
|
|
120
141
|
if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
|
|
121
142
|
script.onload = script.onreadystatechange = null
|
|
143
|
+
utils._loadedScripts.add(src);
|
|
122
144
|
resolve()
|
|
123
145
|
}
|
|
124
146
|
document.head.appendChild(script)
|
|
125
147
|
}),
|
|
126
148
|
|
|
127
|
-
|
|
149
|
+
// PJAX 事件监听器管理
|
|
150
|
+
_pjaxListeners: new Set(),
|
|
151
|
+
|
|
152
|
+
jq: (fn, pjax = true) => {
|
|
128
153
|
if (typeof jQuery === 'undefined') {
|
|
129
154
|
utils.js(deps.jquery).then(fn)
|
|
130
155
|
} else {
|
|
131
156
|
fn()
|
|
132
157
|
}
|
|
158
|
+
if (pjax && !fn._pjax_registered) {
|
|
159
|
+
document.addEventListener('pjax:complete', fn);
|
|
160
|
+
fn._pjax_registered = true;
|
|
161
|
+
utils._pjaxListeners.add(fn); // 追踪监听器以便清理
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
// 清理所有 PJAX 事件监听器
|
|
166
|
+
cleanupPjaxListeners: () => {
|
|
167
|
+
utils._pjaxListeners.forEach(fn => {
|
|
168
|
+
document.removeEventListener('pjax:complete', fn);
|
|
169
|
+
fn._pjax_registered = false;
|
|
170
|
+
});
|
|
171
|
+
utils._pjaxListeners.clear();
|
|
133
172
|
},
|
|
134
173
|
|
|
135
174
|
onLoading: (el) => {
|
|
136
175
|
if (el) {
|
|
137
|
-
$(el).
|
|
176
|
+
if ($(el).find('.loading-wrap').length === 0){
|
|
177
|
+
$(el).append('<div class="loading-wrap"><svg xmlns="http://www.w3.org/2000/svg" width="2em" height="2em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="2"><path stroke-dasharray="60" stroke-dashoffset="60" stroke-opacity=".3" d="M12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3Z"><animate fill="freeze" attributeName="stroke-dashoffset" dur="1.3s" values="60;0"/></path><path stroke-dasharray="15" stroke-dashoffset="15" d="M12 3C16.9706 3 21 7.02944 21 12"><animate fill="freeze" attributeName="stroke-dashoffset" dur="0.3s" values="15;0"/><animateTransform attributeName="transform" dur="1.5s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12"/></path></g></svg></div>');
|
|
178
|
+
}
|
|
138
179
|
}
|
|
139
180
|
},
|
|
140
181
|
onLoadSuccess: (el) => {
|
|
@@ -150,6 +191,10 @@
|
|
|
150
191
|
}
|
|
151
192
|
},
|
|
152
193
|
request: (el, url, callback, onFailure) => {
|
|
194
|
+
// 检查元素实例是否已加载 (而不是检查属性)
|
|
195
|
+
if (el && utils._loadedElements.has(el)) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
153
198
|
const maxRetry = 3;
|
|
154
199
|
let retryCount = 0;
|
|
155
200
|
|
|
@@ -179,6 +224,8 @@
|
|
|
179
224
|
return resp;
|
|
180
225
|
}).then(data => {
|
|
181
226
|
if (timedOut) return;
|
|
227
|
+
// 标记元素实例为已加载
|
|
228
|
+
if (el) utils._loadedElements.add(el);
|
|
182
229
|
utils.onLoadSuccess?.(el);
|
|
183
230
|
callback(data);
|
|
184
231
|
resolve(data);
|
|
@@ -237,8 +284,100 @@
|
|
|
237
284
|
window.requestAnimationFrame(fn)
|
|
238
285
|
},
|
|
239
286
|
dark: {},
|
|
287
|
+
|
|
288
|
+
// 插件初始化管理器 - 统一处理 DOMContentLoaded 和 pjax:complete 事件
|
|
289
|
+
_pluginInitializers: [],
|
|
290
|
+
_pluginCleanups: new Map(), // 存储每个插件的清理函数
|
|
291
|
+
|
|
292
|
+
initPlugin: (initFn, name, options = {}) => {
|
|
293
|
+
if (!initFn || typeof initFn !== 'function') return;
|
|
294
|
+
|
|
295
|
+
// 避免重复注册
|
|
296
|
+
const pluginName = name || initFn.name;
|
|
297
|
+
if (utils._pluginInitializers.some(p => p.name === pluginName)) return;
|
|
298
|
+
|
|
299
|
+
// 包装初始化函数,添加防重复执行的保护
|
|
300
|
+
const wrappedInit = () => {
|
|
301
|
+
try {
|
|
302
|
+
// 在执行前先清理旧的资源
|
|
303
|
+
if (utils._pluginCleanups.has(pluginName)) {
|
|
304
|
+
const cleanup = utils._pluginCleanups.get(pluginName);
|
|
305
|
+
if (typeof cleanup === 'function') {
|
|
306
|
+
cleanup();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// 执行初始化,可能返回清理函数
|
|
311
|
+
const cleanup = initFn();
|
|
312
|
+
|
|
313
|
+
// 如果初始化函数返回了清理函数,保存它
|
|
314
|
+
if (typeof cleanup === 'function') {
|
|
315
|
+
utils._pluginCleanups.set(pluginName, cleanup);
|
|
316
|
+
}
|
|
317
|
+
} catch (error) {
|
|
318
|
+
console.error(`[Plugin ${pluginName}] 初始化失败:`, error);
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
utils._pluginInitializers.push({
|
|
323
|
+
fn: wrappedInit,
|
|
324
|
+
name: pluginName,
|
|
325
|
+
options: options
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// 如果 DOM 已经加载完成,立即执行
|
|
329
|
+
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
|
330
|
+
wrappedInit();
|
|
331
|
+
} else {
|
|
332
|
+
// 否则等待 DOMContentLoaded
|
|
333
|
+
window.addEventListener('DOMContentLoaded', wrappedInit, { once: true });
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
|
|
337
|
+
// 重新初始化所有插件(用于 PJAX 等场景)
|
|
338
|
+
reinitPlugins: () => {
|
|
339
|
+
utils._pluginInitializers.forEach(plugin => {
|
|
340
|
+
// 跳过标记为不需要重新初始化的插件
|
|
341
|
+
if (plugin.options.skipReinit) return;
|
|
342
|
+
|
|
343
|
+
try {
|
|
344
|
+
plugin.fn();
|
|
345
|
+
} catch (error) {
|
|
346
|
+
console.error(`[Plugin ${plugin.name}] 重新初始化失败:`, error);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
},
|
|
350
|
+
|
|
351
|
+
// 清理所有插件资源
|
|
352
|
+
cleanupPlugins: () => {
|
|
353
|
+
utils._pluginCleanups.forEach((cleanup, name) => {
|
|
354
|
+
try {
|
|
355
|
+
cleanup();
|
|
356
|
+
} catch (error) {
|
|
357
|
+
console.error(`[Plugin ${name}] 清理失败:`, error);
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
utils._pluginCleanups.clear();
|
|
361
|
+
},
|
|
362
|
+
|
|
363
|
+
// 清理所有资源(用于页面卸载或重置)
|
|
364
|
+
cleanupAll: () => {
|
|
365
|
+
utils.cleanupPlugins();
|
|
366
|
+
utils.cleanupPjaxListeners();
|
|
367
|
+
},
|
|
240
368
|
};
|
|
241
369
|
|
|
370
|
+
// 自动处理 PJAX 事件
|
|
371
|
+
document.addEventListener('pjax:before', () => {
|
|
372
|
+
// 在导航前清理旧的 PJAX 监听器(如果需要重新注册)
|
|
373
|
+
// 注意:这里不清理,因为监听器应该是持久的
|
|
374
|
+
// utils.cleanupPjaxListeners();
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
document.addEventListener('pjax:complete', () => {
|
|
378
|
+
utils.reinitPlugins();
|
|
379
|
+
});
|
|
380
|
+
|
|
242
381
|
// utils.dark.mode 当前模式 dark or light
|
|
243
382
|
// utils.dark.toggle() 暗黑模式触发器
|
|
244
383
|
// utils.dark.push(callBack[,"callBackName"]) 传入触发器回调函数
|
|
@@ -13,6 +13,9 @@ if (item.placeholder == null && item.filter == 'auto') {
|
|
|
13
13
|
}
|
|
14
14
|
function layoutDiv() {
|
|
15
15
|
var el = ''
|
|
16
|
+
if (page.search == false) {
|
|
17
|
+
return el
|
|
18
|
+
}
|
|
16
19
|
el += `<div class="search-wrapper" id="search-wrapper">`
|
|
17
20
|
el += `<form class="search-form">`
|
|
18
21
|
var filter = ''
|
|
@@ -21,8 +21,20 @@ function layoutDiv() {
|
|
|
21
21
|
if (item.username == undefined || item.username.length == 0) {
|
|
22
22
|
return el;
|
|
23
23
|
}
|
|
24
|
+
if (item.follow == null) {
|
|
25
|
+
item.follow = true;
|
|
26
|
+
}
|
|
24
27
|
|
|
25
28
|
el += `<widget class="widget-wrapper${scrollreveal(' ')} user-card ghuser">`
|
|
29
|
+
// header
|
|
30
|
+
if (item.header) {
|
|
31
|
+
el += `<div class="widget-header">`
|
|
32
|
+
el += `<a class="gh-url" href="https://github.com/${item.username}">
|
|
33
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from TDesign Icons by TDesign - https://github.com/Tencent/tdesign-icons/blob/main/LICENSE --><path fill="currentColor" d="M12 .999c-6.074 0-11 5.05-11 11.278c0 4.983 3.152 9.21 7.523 10.702c.55.104.727-.246.727-.543v-2.1c-3.06.683-3.697-1.33-3.697-1.33c-.5-1.304-1.222-1.65-1.222-1.65c-.998-.7.076-.686.076-.686c1.105.08 1.686 1.163 1.686 1.163c.98 1.724 2.573 1.226 3.201.937c.098-.728.383-1.226.698-1.508c-2.442-.286-5.01-1.253-5.01-5.574c0-1.232.429-2.237 1.132-3.027c-.114-.285-.49-1.432.107-2.985c0 0 .924-.303 3.026 1.156c.877-.25 1.818-.375 2.753-.38c.935.005 1.876.13 2.755.38c2.1-1.459 3.023-1.156 3.023-1.156c.598 1.554.222 2.701.108 2.985c.706.79 1.132 1.796 1.132 3.027c0 4.332-2.573 5.286-5.022 5.565c.394.35.754 1.036.754 2.088v3.095c0 .3.176.652.734.542C19.852 21.484 23 17.258 23 12.277C23 6.048 18.075.999 12 .999"/></svg>
|
|
34
|
+
<span>github/${item.username}</span>
|
|
35
|
+
</a>`
|
|
36
|
+
el += `</div>`
|
|
37
|
+
}
|
|
26
38
|
// body
|
|
27
39
|
el += '<div class="widget-body data-service ds-ghinfo" data-api="https://' + theme.api_host.ghapi + '/users/' + item.username + '">';
|
|
28
40
|
if (item.avatar) {
|
|
@@ -41,10 +53,12 @@ function layoutDiv() {
|
|
|
41
53
|
});
|
|
42
54
|
el += '</div>';
|
|
43
55
|
// follow
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
56
|
+
if (item.follow) {
|
|
57
|
+
el += '<a class="follow" href="https://github.com/' + item.username + '">';
|
|
58
|
+
el += icon('github:logo')
|
|
59
|
+
el += 'Follow';
|
|
60
|
+
el += '</a>';
|
|
61
|
+
}
|
|
48
62
|
el += '</div>';
|
|
49
63
|
el += '</widget>';
|
|
50
64
|
return el;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<%
|
|
2
2
|
function layoutDiv() {
|
|
3
3
|
var el = '';
|
|
4
|
-
el += `<widget class="widget-wrapper
|
|
4
|
+
el += `<widget class="widget-wrapper linklist">`
|
|
5
5
|
if (item.title?.length > 0) {
|
|
6
6
|
el += '<div class="widget-header dis-select">';
|
|
7
7
|
el += '<span class="name">' + item.title + '</span>';
|
|
@@ -1,33 +1,36 @@
|
|
|
1
|
-
<%
|
|
2
|
-
function layoutDiv() {
|
|
3
|
-
var el = '';
|
|
4
|
-
if (item.api == undefined) {
|
|
5
|
-
return el;
|
|
6
|
-
}
|
|
7
|
-
el += `<widget class="widget-wrapper${scrollreveal(' ')} timeline">`
|
|
8
|
-
if (item.title) {
|
|
9
|
-
el += '<div class="widget-header dis-select">';
|
|
10
|
-
el += '<span class="name">' + item.title + '</span>';
|
|
11
|
-
el += '</div>';
|
|
12
|
-
}
|
|
13
|
-
el += '<div class="widget-body fs14">';
|
|
14
|
-
el += `<div class="tag-plugin timeline data-service ds-${(item.type || 'timeline')}"`;
|
|
15
|
-
//
|
|
16
|
-
['
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
1
|
+
<%
|
|
2
|
+
function layoutDiv() {
|
|
3
|
+
var el = '';
|
|
4
|
+
if (item.api == undefined) {
|
|
5
|
+
return el;
|
|
6
|
+
}
|
|
7
|
+
el += `<widget class="widget-wrapper${scrollreveal(' ')} timeline">`
|
|
8
|
+
if (item.title) {
|
|
9
|
+
el += '<div class="widget-header dis-select">';
|
|
10
|
+
el += '<span class="name">' + item.title + '</span>';
|
|
11
|
+
el += '</div>';
|
|
12
|
+
}
|
|
13
|
+
el += '<div class="widget-body fs14">';
|
|
14
|
+
el += `<div class="tag-plugin timeline data-service ds-${(item.type || 'timeline')}"`;
|
|
15
|
+
// 动态遍历所有属性
|
|
16
|
+
const excludeKeys = ['title', 'type']; // 已用于其他用途的属性
|
|
17
|
+
const dataKeys = ['api']; // 需要添加 data- 前缀的属性
|
|
18
|
+
|
|
19
|
+
Object.keys(item).forEach(key => {
|
|
20
|
+
if (excludeKeys.includes(key) || !item[key]) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (dataKeys.includes(key)) {
|
|
24
|
+
el += ' data-' + key + '="' + item[key] + '"';
|
|
25
|
+
} else {
|
|
26
|
+
el += ' ' + key + '="' + item[key] + '"';
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
el += '>';
|
|
30
|
+
el += '</div>';
|
|
31
|
+
el += '</div>';
|
|
32
|
+
el += '</widget>';
|
|
33
|
+
return el;
|
|
34
|
+
}
|
|
35
|
+
%>
|
|
36
|
+
<%- layoutDiv() %>
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
if (
|
|
2
|
+
utils.initPlugin(() => {
|
|
3
|
+
const codeElements = document.querySelectorAll('.code');
|
|
4
|
+
if (codeElements.length > 0) {
|
|
5
5
|
ctx.copycode = {
|
|
6
6
|
default_text: `<%- conf.default_text %>`,
|
|
7
7
|
success_text: `<%- conf.success_text %>`,
|
|
8
8
|
toast: `<%- conf.toast %>`,
|
|
9
9
|
};
|
|
10
|
-
utils.js('/js/plugins/copycode.js')
|
|
10
|
+
utils.js('/js/plugins/copycode.js', { defer: true }).then(() => {
|
|
11
|
+
if (typeof createCopyButtons === 'function') {
|
|
12
|
+
createCopyButtons(codeElements);
|
|
13
|
+
}
|
|
14
|
+
});
|
|
11
15
|
}
|
|
12
|
-
});
|
|
16
|
+
}, 'copycode');
|
|
13
17
|
</script>
|
|
@@ -1,20 +1,49 @@
|
|
|
1
|
-
|
|
1
|
+
<% if (conf.v3) { %>
|
|
2
|
+
<script>
|
|
3
|
+
window.MathJax = {
|
|
4
|
+
tex: {
|
|
5
|
+
inlineMath: [
|
|
6
|
+
["$", "$"],
|
|
7
|
+
["\\(", "\\)"],
|
|
8
|
+
],
|
|
9
|
+
processEscapes: true,
|
|
10
|
+
skipTags: ["script", "noscript", "style", "textarea", "pre", "code"],
|
|
11
|
+
},
|
|
12
|
+
startup: {
|
|
13
|
+
ready() {
|
|
14
|
+
MathJax.startup.defaultReady();
|
|
15
|
+
MathJax.typesetPromise().then(() => {
|
|
16
|
+
const math = document.querySelectorAll("mjx-container");
|
|
17
|
+
math.forEach((element) => {
|
|
18
|
+
if (element.parentNode) {
|
|
19
|
+
element.parentNode.classList.add("has-jax");
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
</script>
|
|
27
|
+
<% } %>
|
|
28
|
+
<script id="MathJax-script" src="<%- conf.js %>" async></script>
|
|
29
|
+
<% if (!conf.v3) { %>
|
|
2
30
|
<script type="text/x-mathjax-config">
|
|
3
|
-
MathJax.Hub.Config({
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
});
|
|
9
|
-
MathJax.Hub.Config({
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
});
|
|
14
|
-
MathJax.Hub.Queue(function() {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
});
|
|
31
|
+
MathJax.Hub.Config({
|
|
32
|
+
tex2jax: {
|
|
33
|
+
inlineMath: [ ['$','$'], ["\\(","\\)"] ],
|
|
34
|
+
processEscapes: true
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
MathJax.Hub.Config({
|
|
38
|
+
tex2jax: {
|
|
39
|
+
skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
MathJax.Hub.Queue(function() {
|
|
43
|
+
var all = MathJax.Hub.getAllJax(), i;
|
|
44
|
+
for(i=0; i < all.length; i += 1) {
|
|
45
|
+
all[i].SourceElement().parentNode.className += ' has-jax';
|
|
46
|
+
}
|
|
47
|
+
});
|
|
20
48
|
</script>
|
|
49
|
+
<% } %>
|