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
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
container = document.querySelector(container);
|
|
31
31
|
}
|
|
32
32
|
if (!container) return;
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
const images = container.querySelectorAll('img');
|
|
35
35
|
images.forEach((img) => {
|
|
36
36
|
if (img.classList.contains('lazy')) return;
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
window.lazyLoadInstance.update();
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
|
-
|
|
66
|
+
|
|
67
67
|
</script>
|
|
@@ -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,6 +140,7 @@
|
|
|
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)
|
|
@@ -134,7 +156,9 @@
|
|
|
134
156
|
|
|
135
157
|
onLoading: (el) => {
|
|
136
158
|
if (el) {
|
|
137
|
-
$(el).
|
|
159
|
+
if ($(el).find('.loading-wrap').length === 0){
|
|
160
|
+
$(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>');
|
|
161
|
+
}
|
|
138
162
|
}
|
|
139
163
|
},
|
|
140
164
|
onLoadSuccess: (el) => {
|
|
@@ -150,6 +174,10 @@
|
|
|
150
174
|
}
|
|
151
175
|
},
|
|
152
176
|
request: (el, url, callback, onFailure) => {
|
|
177
|
+
// 检查元素实例是否已加载 (而不是检查属性)
|
|
178
|
+
if (el && utils._loadedElements.has(el)) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
153
181
|
const maxRetry = 3;
|
|
154
182
|
let retryCount = 0;
|
|
155
183
|
|
|
@@ -179,6 +207,8 @@
|
|
|
179
207
|
return resp;
|
|
180
208
|
}).then(data => {
|
|
181
209
|
if (timedOut) return;
|
|
210
|
+
// 标记元素实例为已加载
|
|
211
|
+
if (el) utils._loadedElements.add(el);
|
|
182
212
|
utils.onLoadSuccess?.(el);
|
|
183
213
|
callback(data);
|
|
184
214
|
resolve(data);
|
|
@@ -237,6 +267,72 @@
|
|
|
237
267
|
window.requestAnimationFrame(fn)
|
|
238
268
|
},
|
|
239
269
|
dark: {},
|
|
270
|
+
|
|
271
|
+
// 插件初始化管理器 - 统一处理 DOMContentLoaded 和 pjax:complete 事件
|
|
272
|
+
_pluginInitializers: [],
|
|
273
|
+
_pluginCleanups: new Map(), // 存储每个插件的清理函数
|
|
274
|
+
|
|
275
|
+
initPlugin: (initFn, name, options = {}) => {
|
|
276
|
+
if (!initFn || typeof initFn !== 'function') return;
|
|
277
|
+
|
|
278
|
+
// 避免重复注册
|
|
279
|
+
const pluginName = name || initFn.name;
|
|
280
|
+
if (utils._pluginInitializers.some(p => p.name === pluginName)) return;
|
|
281
|
+
|
|
282
|
+
// 包装初始化函数,添加防重复执行的保护
|
|
283
|
+
const wrappedInit = () => {
|
|
284
|
+
try {
|
|
285
|
+
// 在执行前先清理旧的资源
|
|
286
|
+
if (utils._pluginCleanups.has(pluginName)) {
|
|
287
|
+
const cleanup = utils._pluginCleanups.get(pluginName);
|
|
288
|
+
if (typeof cleanup === 'function') {
|
|
289
|
+
cleanup();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// 执行初始化,可能返回清理函数
|
|
294
|
+
const cleanup = initFn();
|
|
295
|
+
|
|
296
|
+
// 如果初始化函数返回了清理函数,保存它
|
|
297
|
+
if (typeof cleanup === 'function') {
|
|
298
|
+
utils._pluginCleanups.set(pluginName, cleanup);
|
|
299
|
+
}
|
|
300
|
+
} catch (error) {
|
|
301
|
+
console.error(`[Plugin ${pluginName}] 初始化失败:`, error);
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
utils._pluginInitializers.push({
|
|
306
|
+
fn: wrappedInit,
|
|
307
|
+
name: pluginName,
|
|
308
|
+
options: options
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// 如果 DOM 已经加载完成,立即执行
|
|
312
|
+
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
|
313
|
+
wrappedInit();
|
|
314
|
+
} else {
|
|
315
|
+
// 否则等待 DOMContentLoaded
|
|
316
|
+
window.addEventListener('DOMContentLoaded', wrappedInit, { once: true });
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
// 清理所有插件资源
|
|
321
|
+
cleanupPlugins: () => {
|
|
322
|
+
utils._pluginCleanups.forEach((cleanup, name) => {
|
|
323
|
+
try {
|
|
324
|
+
cleanup();
|
|
325
|
+
} catch (error) {
|
|
326
|
+
console.error(`[Plugin ${name}] 清理失败:`, error);
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
utils._pluginCleanups.clear();
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
// 清理所有资源(用于页面卸载或重置)
|
|
333
|
+
cleanupAll: () => {
|
|
334
|
+
utils.cleanupPlugins();
|
|
335
|
+
},
|
|
240
336
|
};
|
|
241
337
|
|
|
242
338
|
// utils.dark.mode 当前模式 dark or light
|
|
@@ -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,29 +1,33 @@
|
|
|
1
1
|
<script defer type="text/javascript" src="<%- conf.js %>"></script>
|
|
2
2
|
<script>
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
3
|
+
utils.initPlugin(() => {
|
|
4
|
+
const els = document.querySelectorAll('.mermaid');
|
|
5
|
+
if (els.length > 0) {
|
|
6
|
+
const mermaid_config = {
|
|
7
|
+
startOnLoad: false,
|
|
8
|
+
theme:
|
|
9
|
+
"<%- theme.style.prefers_theme %>" == "auto" &&
|
|
10
|
+
window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
11
|
+
? "dark"
|
|
12
|
+
: "<%- conf.theme %>",
|
|
13
|
+
logLevel: 3,
|
|
14
|
+
themeVariables: {
|
|
15
|
+
darkMode: true
|
|
16
|
+
},
|
|
17
|
+
flowchart: {
|
|
18
|
+
useMaxWidth: false,
|
|
19
|
+
htmlLabels: true,
|
|
20
|
+
curve: "linear"
|
|
21
|
+
},
|
|
22
|
+
gantt: {
|
|
23
|
+
axisFormat: "%Y/%m/%d"
|
|
24
|
+
},
|
|
25
|
+
sequence: {
|
|
26
|
+
actorMargin: 50
|
|
27
|
+
}
|
|
25
28
|
}
|
|
29
|
+
mermaid.initialize(mermaid_config);
|
|
30
|
+
mermaid.run();
|
|
26
31
|
}
|
|
27
|
-
|
|
28
|
-
});
|
|
32
|
+
}, 'mermaid');
|
|
29
33
|
</script>
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
<script defer src="<%- conf.js %>"></script>
|
|
2
2
|
<script>
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
3
|
+
utils.initPlugin(() => {
|
|
4
|
+
const els = document.querySelectorAll('.slide-up');
|
|
5
|
+
if (els.length > 0) {
|
|
6
|
+
const slideUp = {
|
|
7
|
+
distance: `<%- conf.distance %>`,
|
|
8
|
+
duration: `<%- conf.duration %>`,
|
|
9
|
+
interval: `<%- conf.interval %>`,
|
|
10
|
+
scale: `<%- conf.scale %>`,
|
|
11
|
+
opacity: 0,
|
|
12
|
+
easing: "ease-out"
|
|
13
|
+
};
|
|
14
|
+
ScrollReveal().reveal('.slide-up', { ...slideUp });
|
|
15
|
+
}
|
|
16
|
+
}, 'scrollreveal');
|
|
16
17
|
</script>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
|
|
2
|
+
utils.initPlugin(() => {
|
|
3
3
|
const swiper_api = document.getElementById('swiper-api');
|
|
4
4
|
if (swiper_api != undefined) {
|
|
5
5
|
utils.css(`<%- conf.css %>`);
|
|
@@ -22,5 +22,5 @@
|
|
|
22
22
|
});
|
|
23
23
|
})
|
|
24
24
|
}
|
|
25
|
-
});
|
|
25
|
+
}, 'swiper');
|
|
26
26
|
</script>
|