hexo-theme-solitude 3.0.21 → 4.0.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/.github/FUNDING.yml +3 -3
- package/README.md +11 -1
- package/README_en-US.md +11 -1
- package/_config.yml +16 -7
- package/languages/default.yml +56 -36
- package/languages/en.yml +90 -69
- package/languages/es.yml +94 -73
- package/languages/zh-CN.yml +56 -34
- package/languages/zh-TW.yml +80 -57
- package/layout/archive.pug +60 -14
- package/layout/category.pug +13 -11
- package/layout/includes/body/gadsense.pug +2 -2
- package/layout/includes/console.pug +71 -72
- package/layout/includes/footer.pug +24 -9
- package/layout/includes/head/config.pug +83 -39
- package/layout/includes/head/mode.pug +7 -7
- package/layout/includes/head/page_config.pug +20 -11
- package/layout/includes/inject/body.pug +14 -36
- package/layout/includes/keyboard.pug +3 -3
- package/layout/includes/loading.pug +1 -37
- package/layout/includes/mixins/articleSort.pug +3 -3
- package/layout/includes/mixins/common.pug +8 -4
- package/layout/includes/mixins/pace.pug +1 -1
- package/layout/includes/mixins/pagination.pug +3 -3
- package/layout/includes/nav.pug +4 -3
- package/layout/includes/page/about.pug +9 -8
- package/layout/includes/page/brevity.pug +2 -2
- package/layout/includes/page/links.pug +23 -11
- package/layout/includes/page/music.pug +0 -5
- package/layout/includes/rightmenu.pug +20 -20
- package/layout/includes/sidebar.pug +2 -2
- package/layout/includes/widgets/aside/asideInfoCard.pug +2 -2
- package/layout/includes/widgets/aside/asideTag.pug +2 -2
- package/layout/includes/widgets/home/bbTimeList.pug +3 -3
- package/layout/includes/widgets/home/categoryBar.pug +30 -15
- package/layout/includes/widgets/home/hometop.pug +113 -8
- package/layout/includes/widgets/home/postList.pug +1 -2
- package/layout/includes/widgets/home/topGroup.pug +3 -7
- package/layout/includes/widgets/nav/group.pug +4 -2
- package/layout/includes/widgets/nav/menu.pug +6 -4
- package/layout/includes/widgets/nav/right.pug +7 -6
- package/layout/includes/widgets/page/about/award.pug +67 -38
- package/layout/includes/widgets/page/about/hobbies.pug +12 -3
- package/layout/includes/widgets/page/banner.pug +1 -1
- package/layout/includes/widgets/page/kit/content.pug +2 -2
- package/layout/includes/widgets/page/links/banner.pug +30 -19
- package/layout/includes/widgets/page/message/artalk.pug +3 -3
- package/layout/includes/widgets/page/message/content.pug +3 -3
- package/layout/includes/widgets/page/message/twikoo.pug +3 -3
- package/layout/includes/widgets/page/message/valine.pug +4 -4
- package/layout/includes/widgets/page/message/waline.pug +3 -3
- package/layout/includes/widgets/page/recentcomment/artalk.pug +5 -9
- package/layout/includes/widgets/page/recentcomment/twikoo.pug +5 -9
- package/layout/includes/widgets/page/recentcomment/valine.pug +6 -10
- package/layout/includes/widgets/page/recentcomment/waline.pug +5 -9
- package/layout/includes/widgets/post/award.pug +1 -28
- package/layout/includes/widgets/post/copyright.pug +10 -10
- package/layout/includes/widgets/post/postInfo.pug +10 -11
- package/layout/includes/widgets/post/postMeta.pug +3 -2
- package/layout/includes/widgets/rightside/hide.pug +5 -5
- package/layout/includes/widgets/rightside/show.pug +7 -7
- package/layout/includes/widgets/third-party/comments/artalk.pug +9 -9
- package/layout/includes/widgets/third-party/comments/giscus.pug +2 -2
- package/layout/includes/widgets/third-party/comments/twikoo.pug +5 -5
- package/layout/includes/widgets/third-party/comments/valine.pug +6 -6
- package/layout/includes/widgets/third-party/comments/waline.pug +6 -6
- package/layout/includes/widgets/third-party/hot/twikoo.pug +1 -1
- package/layout/includes/widgets/third-party/music.pug +9 -5
- package/layout/includes/widgets/third-party/news-comment/artalk.pug +6 -6
- package/layout/includes/widgets/third-party/news-comment/twikoo.pug +7 -7
- package/layout/includes/widgets/third-party/news-comment/valine.pug +8 -8
- package/layout/includes/widgets/third-party/news-comment/waline.pug +7 -7
- package/layout/includes/widgets/third-party/pjax.pug +16 -10
- package/layout/includes/widgets/third-party/search/algolia-search.pug +2 -2
- package/layout/includes/widgets/third-party/search/docsearch.pug +3 -3
- package/layout/includes/widgets/third-party/search/index.pug +1 -3
- package/layout/includes/widgets/third-party/search/local-search.pug +9 -9
- package/layout/index.pug +8 -6
- package/package.json +8 -7
- package/plugins.yml +22 -21
- package/scripts/event/cdn.js +11 -6
- package/scripts/event/friend_links.js +30 -0
- package/scripts/event/merge_config.js +15 -7
- package/scripts/filter/comment.js +2 -2
- package/scripts/filter/default.js +1 -1
- package/scripts/helper/inject_head_js.js +79 -33
- package/scripts/helper/related_post.js +16 -21
- package/scripts/tags/chart.js +1 -1
- package/scripts/tags/typeit.js +1 -1
- package/source/css/_components/capsule.styl +259 -168
- package/source/css/_components/rightside.styl +98 -242
- package/source/css/_global/animation.styl +1 -10
- package/source/css/_global/index.styl +27 -1
- package/source/css/_layout/aside.styl +7 -1
- package/source/css/_layout/console.styl +145 -165
- package/source/css/_layout/header.styl +238 -47
- package/source/css/_layout/pagination.styl +176 -132
- package/source/css/_layout/rightmenu.styl +11 -2
- package/source/css/_layout/sidebar.styl +39 -17
- package/source/css/_mode/index.styl +7 -1
- package/source/css/_page/_about/about.styl +18 -2
- package/source/css/_page/_about/buff.styl +1 -1
- package/source/css/_page/_about/contentinfo.styl +2 -1
- package/source/css/_page/_about/game.styl +57 -1
- package/source/css/_page/_about/like.styl +7 -2
- package/source/css/_page/_about/maxim.styl +1 -1
- package/source/css/_page/_about/reward.styl +226 -108
- package/source/css/_page/_about/skills.styl +5 -3
- package/source/css/_page/_home/category-bar.styl +162 -88
- package/source/css/_page/_home/home-top.styl +309 -263
- package/source/css/_page/_home/home.styl +34 -7
- package/source/css/_page/archive.styl +412 -0
- package/source/css/_page/category.styl +27 -1
- package/source/css/_page/index.styl +2 -0
- package/source/css/_page/links.styl +190 -0
- package/source/css/_page/other.styl +37 -0
- package/source/css/_post/index.styl +2 -1
- package/source/css/_post/meta.styl +245 -217
- package/source/css/_search/local-search.styl +158 -82
- package/source/css/var.styl +2 -2
- package/source/js/archive-page.js +191 -0
- package/source/js/core/actions.js +48 -0
- package/source/js/core/api.js +32 -0
- package/source/js/core/config.js +13 -0
- package/source/js/core/lifecycle.js +52 -0
- package/source/js/core/preloader.js +34 -0
- package/source/js/core/resources.js +65 -0
- package/source/js/covercolor/api.js +13 -103
- package/source/js/covercolor/ave.js +14 -77
- package/source/js/covercolor/local.js +22 -116
- package/source/js/covercolor/shared.js +102 -0
- package/source/js/friend_links.js +502 -0
- package/source/js/main.js +793 -177
- package/source/js/music.js +24 -14
- package/source/js/post_ai.js +13 -2
- package/source/js/right_menu.js +44 -24
- package/source/js/search/algolia.js +29 -21
- package/source/js/search/local.js +231 -266
- package/source/js/tw_cn.js +37 -12
- package/source/js/utils.js +37 -25
- package/layout/includes/widgets/randomlink.pug +0 -43
- package/scripts/filter/randomPosts.js +0 -16
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { Solitude } from "../core/api.js";
|
|
2
|
+
|
|
3
|
+
(() => {
|
|
1
4
|
class LocalSearch {
|
|
2
5
|
constructor() {
|
|
3
6
|
this.store = [];
|
|
@@ -5,61 +8,65 @@ class LocalSearch {
|
|
|
5
8
|
this.currentPage = 0;
|
|
6
9
|
this.resultsPerPage = 10;
|
|
7
10
|
this.currentResults = [];
|
|
11
|
+
this.lastSearchTime = null;
|
|
8
12
|
this.isLoading = false;
|
|
9
13
|
this.searchTimeout = null;
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
this.boundElements = new WeakSet();
|
|
15
|
+
this.keyboardBound = false;
|
|
16
|
+
this.pjaxBound = false;
|
|
17
|
+
this.fixSafariHeight = this.fixSafariHeight.bind(this);
|
|
18
|
+
this.handleKeydown = this.handleKeydown.bind(this);
|
|
19
|
+
this.handlePjaxComplete = this.handlePjaxComplete.bind(this);
|
|
20
|
+
this.handleSearchInputDebounced = this.debounce((event) => {
|
|
21
|
+
this.handleSearchInput(event.target.value.trim());
|
|
22
|
+
}, 300);
|
|
12
23
|
this.elements = this.cacheElements();
|
|
13
|
-
|
|
14
|
-
// 初始化
|
|
15
24
|
this.init();
|
|
16
25
|
}
|
|
17
26
|
|
|
18
|
-
/**
|
|
19
|
-
* 缓存常用的DOM元素
|
|
20
|
-
*/
|
|
21
27
|
cacheElements() {
|
|
22
28
|
return {
|
|
23
|
-
searchMask: document.getElementById(
|
|
24
|
-
searchDialog: document.querySelector(
|
|
25
|
-
searchInput: document.getElementById(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
searchMask: document.getElementById('search-mask'),
|
|
30
|
+
searchDialog: document.querySelector('#local-search .search-dialog'),
|
|
31
|
+
searchInput: document.getElementById('search-input'),
|
|
32
|
+
searchSuggestions: document.getElementById('search-suggestions'),
|
|
33
|
+
searchResults: document.getElementById('search-results'),
|
|
34
|
+
searchPagination: document.getElementById('search-pagination'),
|
|
35
|
+
searchTips: document.getElementById('search-tips'),
|
|
36
|
+
searchButton: document.querySelector('#search-button > .search'),
|
|
37
|
+
closeButton: document.querySelector('#local-search .search-close-button'),
|
|
38
|
+
menuSearch: document.getElementById('menu-search')
|
|
32
39
|
};
|
|
33
40
|
}
|
|
34
41
|
|
|
35
|
-
/**
|
|
36
|
-
* 初始化搜索功能
|
|
37
|
-
*/
|
|
38
42
|
async init() {
|
|
43
|
+
this.bindEvents();
|
|
44
|
+
this.bindKeyboardShortcuts();
|
|
45
|
+
this.bindPjaxEvents();
|
|
46
|
+
this.syncSearchState();
|
|
47
|
+
Solitude.openSearch = () => this.openSearch();
|
|
48
|
+
|
|
39
49
|
try {
|
|
40
50
|
await this.loadSearchData();
|
|
41
|
-
this.
|
|
42
|
-
this.
|
|
51
|
+
const query = this.elements.searchInput?.value.trim();
|
|
52
|
+
if (query) this.handleSearchInput(query);
|
|
43
53
|
} catch (error) {
|
|
44
54
|
console.error('Search initialization failed:', error);
|
|
45
55
|
}
|
|
46
56
|
}
|
|
47
57
|
|
|
48
|
-
/**
|
|
49
|
-
* 加载搜索数据
|
|
50
|
-
*/
|
|
51
58
|
async loadSearchData() {
|
|
52
|
-
if (!
|
|
59
|
+
if (!Solitude.config?.localsearch?.path) {
|
|
53
60
|
throw new Error('Search data path not configured');
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
this.isLoading = true;
|
|
57
64
|
try {
|
|
58
|
-
const response = await fetch(
|
|
65
|
+
const response = await fetch(Solitude.config.localsearch.path);
|
|
59
66
|
if (!response.ok) {
|
|
60
67
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
61
68
|
}
|
|
62
|
-
|
|
69
|
+
|
|
63
70
|
const data = await response.text();
|
|
64
71
|
this.parseSearchData(data);
|
|
65
72
|
} catch (error) {
|
|
@@ -69,25 +76,22 @@ class LocalSearch {
|
|
|
69
76
|
}
|
|
70
77
|
}
|
|
71
78
|
|
|
72
|
-
/**
|
|
73
|
-
* 解析搜索数据
|
|
74
|
-
*/
|
|
75
79
|
parseSearchData(xmlData) {
|
|
76
80
|
try {
|
|
77
81
|
const parser = new DOMParser();
|
|
78
|
-
const xmlDoc = parser.parseFromString(xmlData,
|
|
79
|
-
const entries = xmlDoc.getElementsByTagName(
|
|
80
|
-
|
|
82
|
+
const xmlDoc = parser.parseFromString(xmlData, 'text/xml');
|
|
83
|
+
const entries = xmlDoc.getElementsByTagName('entry');
|
|
84
|
+
|
|
81
85
|
this.store = Array.from(entries).map(entry => {
|
|
82
86
|
const getTextContent = (tagName) => {
|
|
83
87
|
const element = entry.getElementsByTagName(tagName)[0];
|
|
84
88
|
return element ? element.textContent.trim() : '';
|
|
85
89
|
};
|
|
86
|
-
|
|
90
|
+
|
|
87
91
|
return {
|
|
88
|
-
title: getTextContent(
|
|
89
|
-
link: getTextContent(
|
|
90
|
-
content: getTextContent(
|
|
92
|
+
title: getTextContent('title'),
|
|
93
|
+
link: getTextContent('url'),
|
|
94
|
+
content: getTextContent('content')
|
|
91
95
|
};
|
|
92
96
|
}).filter(item => item.title && item.link);
|
|
93
97
|
} catch (error) {
|
|
@@ -95,144 +99,140 @@ class LocalSearch {
|
|
|
95
99
|
}
|
|
96
100
|
}
|
|
97
101
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
102
|
+
bindOnce(element, eventName, handler) {
|
|
103
|
+
if (!element || this.boundElements.has(element)) return;
|
|
104
|
+
element.addEventListener(eventName, handler);
|
|
105
|
+
this.boundElements.add(element);
|
|
106
|
+
}
|
|
107
|
+
|
|
101
108
|
bindEvents() {
|
|
102
|
-
|
|
103
|
-
this.elements.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
109
|
+
this.bindOnce(this.elements.searchInput, 'input', this.handleSearchInputDebounced);
|
|
110
|
+
this.bindOnce(this.elements.searchButton, 'click', () => this.openSearch());
|
|
111
|
+
this.bindOnce(this.elements.closeButton, 'click', () => this.closeSearch());
|
|
112
|
+
this.bindOnce(this.elements.searchMask, 'click', () => this.closeSearch());
|
|
113
|
+
|
|
114
|
+
document.querySelectorAll('#local-search .tag-list').forEach(button => {
|
|
115
|
+
this.bindOnce(button, 'click', () => {
|
|
116
|
+
const query = button.dataset.query?.trim();
|
|
117
|
+
if (!query || !this.elements.searchInput) return;
|
|
118
|
+
this.elements.searchInput.value = query;
|
|
119
|
+
this.handleSearchInput(query);
|
|
120
|
+
this.elements.searchInput.focus();
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (Solitude.config.right_menu && this.elements.menuSearch) {
|
|
125
|
+
this.bindOnce(this.elements.menuSearch, 'click', () => {
|
|
126
|
+
Solitude.hideRightMenu?.();
|
|
119
127
|
this.openSearch();
|
|
120
|
-
if (
|
|
121
|
-
this.elements.searchInput.value =
|
|
122
|
-
this.handleSearchInput(
|
|
128
|
+
if (Solitude.selectedText && this.elements.searchInput) {
|
|
129
|
+
this.elements.searchInput.value = Solitude.selectedText;
|
|
130
|
+
this.handleSearchInput(Solitude.selectedText.trim());
|
|
123
131
|
}
|
|
124
132
|
});
|
|
125
133
|
}
|
|
134
|
+
}
|
|
126
135
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
});
|
|
136
|
+
bindKeyboardShortcuts() {
|
|
137
|
+
if (this.keyboardBound) return;
|
|
138
|
+
document.addEventListener('keydown', this.handleKeydown);
|
|
139
|
+
this.keyboardBound = true;
|
|
132
140
|
}
|
|
133
141
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const tagLists = document.querySelectorAll("#local-search .tag-list");
|
|
139
|
-
tagLists.forEach(el => {
|
|
140
|
-
el.addEventListener("click", () => this.closeSearch());
|
|
141
|
-
});
|
|
142
|
+
bindPjaxEvents() {
|
|
143
|
+
if (this.pjaxBound) return;
|
|
144
|
+
window.addEventListener('pjax:complete', this.handlePjaxComplete);
|
|
145
|
+
this.pjaxBound = true;
|
|
142
146
|
}
|
|
143
147
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (event.ctrlKey && event.key === "k") {
|
|
151
|
-
event.preventDefault();
|
|
152
|
-
this.openSearch();
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
148
|
+
handleKeydown(event) {
|
|
149
|
+
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'k') {
|
|
150
|
+
event.preventDefault();
|
|
151
|
+
this.openSearch();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
155
154
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
155
|
+
if (event.code === 'Escape' && this.isSearchOpen()) {
|
|
156
|
+
this.closeSearch();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
handlePjaxComplete() {
|
|
161
|
+
this.elements = this.cacheElements();
|
|
162
|
+
this.bindEvents();
|
|
163
|
+
this.syncSearchState();
|
|
161
164
|
}
|
|
162
165
|
|
|
163
|
-
/**
|
|
164
|
-
* 打开搜索框
|
|
165
|
-
*/
|
|
166
166
|
openSearch() {
|
|
167
167
|
if (!this.elements.searchMask || !this.elements.searchDialog) return;
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
this.elements.searchDialog.style.display =
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
setTimeout(() => {
|
|
174
|
-
this.elements.searchInput?.focus();
|
|
175
|
-
}, 100);
|
|
176
|
-
|
|
169
|
+
Solitude.animateIn(this.elements.searchMask, 'to_show 0.5s');
|
|
170
|
+
this.elements.searchDialog.style.display = 'flex';
|
|
171
|
+
document.documentElement.classList.add('search-open');
|
|
172
|
+
this.syncSearchState();
|
|
177
173
|
this.fixSafariHeight();
|
|
178
|
-
window.addEventListener(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
window.openSearch = () => this.openSearch();
|
|
174
|
+
window.addEventListener('resize', this.fixSafariHeight);
|
|
175
|
+
|
|
176
|
+
setTimeout(() => this.elements.searchInput?.focus(), 100);
|
|
182
177
|
}
|
|
183
178
|
|
|
184
|
-
/**
|
|
185
|
-
* 关闭搜索框
|
|
186
|
-
*/
|
|
187
179
|
closeSearch() {
|
|
188
180
|
if (!this.elements.searchMask || !this.elements.searchDialog) return;
|
|
189
181
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
// 清理搜索状态
|
|
195
|
-
this.currentQuery = '';
|
|
196
|
-
this.currentPage = 0;
|
|
182
|
+
Solitude.animateOut(this.elements.searchDialog, 'search_close .5s');
|
|
183
|
+
Solitude.animateOut(this.elements.searchMask, 'to_hide 0.5s');
|
|
184
|
+
document.documentElement.classList.remove('search-open');
|
|
185
|
+
window.removeEventListener('resize', this.fixSafariHeight);
|
|
197
186
|
}
|
|
198
187
|
|
|
199
|
-
/**
|
|
200
|
-
* 检查搜索框是否打开
|
|
201
|
-
*/
|
|
202
188
|
isSearchOpen() {
|
|
203
|
-
return this.elements.searchDialog?.style.display ===
|
|
189
|
+
return this.elements.searchDialog?.style.display === 'flex';
|
|
204
190
|
}
|
|
205
191
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
this.elements.searchDialog.style.
|
|
192
|
+
fixSafariHeight() {
|
|
193
|
+
if (!this.elements.searchDialog) return;
|
|
194
|
+
if (window.innerWidth < 768) {
|
|
195
|
+
this.elements.searchDialog.style.setProperty('--search-height', `${window.innerHeight}px`);
|
|
196
|
+
} else {
|
|
197
|
+
this.elements.searchDialog.style.removeProperty('--search-height');
|
|
212
198
|
}
|
|
213
|
-
}
|
|
199
|
+
}
|
|
214
200
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if (this.isLoading) return;
|
|
201
|
+
syncSearchState() {
|
|
202
|
+
const query = this.elements.searchInput?.value.trim() || '';
|
|
203
|
+
this.setQueryState(Boolean(query));
|
|
204
|
+
}
|
|
220
205
|
|
|
206
|
+
setQueryState(hasQuery) {
|
|
207
|
+
if (this.elements.searchSuggestions) {
|
|
208
|
+
this.elements.searchSuggestions.hidden = hasQuery;
|
|
209
|
+
}
|
|
210
|
+
if (this.elements.searchResults) {
|
|
211
|
+
this.elements.searchResults.hidden = !hasQuery;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
handleSearchInput(query) {
|
|
221
216
|
this.currentQuery = query;
|
|
222
217
|
this.currentPage = 0;
|
|
223
218
|
|
|
224
|
-
if (query
|
|
219
|
+
if (!query) {
|
|
225
220
|
this.clearSearchResults();
|
|
226
221
|
return;
|
|
227
222
|
}
|
|
228
223
|
|
|
224
|
+
this.setQueryState(true);
|
|
225
|
+
|
|
226
|
+
if (this.isLoading) {
|
|
227
|
+
this.showStatusMessage(Solitude.config.lang?.search?.loading || 'Searching...', 'search-result-loading');
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
229
231
|
try {
|
|
230
232
|
const startTime = performance.now();
|
|
231
233
|
this.currentResults = this.performSearch(query);
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
this.renderResults(this.currentResults, this.currentPage, searchTime);
|
|
234
|
+
this.lastSearchTime = (performance.now() - startTime).toFixed(2);
|
|
235
|
+
this.renderResults(this.currentResults, this.currentPage, this.lastSearchTime);
|
|
236
236
|
this.renderPagination(this.currentResults.length);
|
|
237
237
|
} catch (error) {
|
|
238
238
|
console.error('Search error:', error);
|
|
@@ -240,57 +240,41 @@ class LocalSearch {
|
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
-
/**
|
|
244
|
-
* 执行搜索
|
|
245
|
-
*/
|
|
246
243
|
performSearch(query) {
|
|
247
244
|
if (!query || !this.store.length) return [];
|
|
248
245
|
|
|
249
|
-
const keywords = query.toLowerCase().split(/\s+/).filter(
|
|
250
|
-
if (keywords.length
|
|
246
|
+
const keywords = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
247
|
+
if (!keywords.length) return [];
|
|
251
248
|
|
|
252
249
|
return this.store.filter(item => {
|
|
253
250
|
const titleLower = item.title.toLowerCase();
|
|
254
251
|
const contentLower = item.content.toLowerCase();
|
|
255
|
-
|
|
256
|
-
// 所有关键词都必须在标题或内容中找到
|
|
257
|
-
return keywords.every(keyword =>
|
|
252
|
+
return keywords.every(keyword =>
|
|
258
253
|
titleLower.includes(keyword) || contentLower.includes(keyword)
|
|
259
254
|
);
|
|
260
|
-
}).sort((a, b) =>
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const bScore = this.calculateRelevanceScore(b, keywords);
|
|
264
|
-
return bScore - aScore;
|
|
265
|
-
});
|
|
255
|
+
}).sort((a, b) =>
|
|
256
|
+
this.calculateRelevanceScore(b, keywords) - this.calculateRelevanceScore(a, keywords)
|
|
257
|
+
);
|
|
266
258
|
}
|
|
267
259
|
|
|
268
|
-
/**
|
|
269
|
-
* 计算相关性评分
|
|
270
|
-
*/
|
|
271
260
|
calculateRelevanceScore(item, keywords) {
|
|
272
|
-
let score = 0;
|
|
273
261
|
const titleLower = item.title.toLowerCase();
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (titleLower === keyword) score
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
return score;
|
|
262
|
+
const contentLower = item.content.toLowerCase();
|
|
263
|
+
|
|
264
|
+
return keywords.reduce((score, keyword) => {
|
|
265
|
+
if (titleLower === keyword) return score + 10;
|
|
266
|
+
if (titleLower.includes(keyword)) return score + 5;
|
|
267
|
+
if (contentLower.includes(keyword)) return score + 1;
|
|
268
|
+
return score;
|
|
269
|
+
}, 0);
|
|
284
270
|
}
|
|
285
271
|
|
|
286
|
-
|
|
287
|
-
* 渲染搜索结果
|
|
288
|
-
*/
|
|
289
|
-
renderResults(results, page, searchTime = null) {
|
|
272
|
+
renderResults(results, page, searchTime = this.lastSearchTime) {
|
|
290
273
|
if (!this.elements.searchResults || !this.elements.searchTips) return;
|
|
291
274
|
|
|
292
275
|
this.elements.searchResults.innerHTML = '';
|
|
293
276
|
this.elements.searchTips.innerHTML = '';
|
|
277
|
+
this.setQueryState(true);
|
|
294
278
|
|
|
295
279
|
const start = page * this.resultsPerPage;
|
|
296
280
|
const end = start + this.resultsPerPage;
|
|
@@ -300,158 +284,133 @@ class LocalSearch {
|
|
|
300
284
|
return;
|
|
301
285
|
}
|
|
302
286
|
|
|
303
|
-
// 创建结果列表
|
|
304
287
|
const fragment = document.createDocumentFragment();
|
|
305
288
|
results.slice(start, end).forEach(result => {
|
|
306
|
-
|
|
307
|
-
fragment.appendChild(resultElement);
|
|
289
|
+
fragment.appendChild(this.createResultElement(result));
|
|
308
290
|
});
|
|
309
291
|
this.elements.searchResults.appendChild(fragment);
|
|
310
|
-
|
|
311
|
-
// 显示结果计数
|
|
312
292
|
this.showResultCount(results.length, searchTime);
|
|
313
293
|
}
|
|
314
294
|
|
|
315
|
-
/**
|
|
316
|
-
* 创建单个结果元素
|
|
317
|
-
*/
|
|
318
295
|
createResultElement(result) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
* 高亮关键词
|
|
333
|
-
*/
|
|
296
|
+
const resultItem = document.createElement('li');
|
|
297
|
+
resultItem.className = 'search-result-item';
|
|
298
|
+
|
|
299
|
+
const link = document.createElement('a');
|
|
300
|
+
link.className = 'search-result-title';
|
|
301
|
+
link.href = result.link;
|
|
302
|
+
link.innerHTML = this.highlightKeywords(result.title, this.currentQuery);
|
|
303
|
+
link.addEventListener('click', () => this.closeSearch());
|
|
304
|
+
|
|
305
|
+
resultItem.appendChild(link);
|
|
306
|
+
return resultItem;
|
|
307
|
+
}
|
|
308
|
+
|
|
334
309
|
highlightKeywords(text, query) {
|
|
335
310
|
if (!query) return text;
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
let highlightedText = text;
|
|
339
|
-
|
|
340
|
-
keywords.forEach(keyword => {
|
|
311
|
+
|
|
312
|
+
return query.split(/\s+/).filter(Boolean).reduce((highlightedText, keyword) => {
|
|
341
313
|
const regex = new RegExp(`(${this.escapeRegExp(keyword)})`, 'gi');
|
|
342
|
-
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
return highlightedText;
|
|
314
|
+
return highlightedText.replace(regex, '<em>$1</em>');
|
|
315
|
+
}, text);
|
|
346
316
|
}
|
|
347
317
|
|
|
348
|
-
/**
|
|
349
|
-
* 转义正则表达式特殊字符
|
|
350
|
-
*/
|
|
351
318
|
escapeRegExp(string) {
|
|
352
319
|
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
353
320
|
}
|
|
354
321
|
|
|
355
|
-
/**
|
|
356
|
-
* 渲染分页
|
|
357
|
-
*/
|
|
358
322
|
renderPagination(totalResults) {
|
|
359
323
|
if (!this.elements.searchPagination) return;
|
|
360
324
|
|
|
361
325
|
const totalPages = Math.ceil(totalResults / this.resultsPerPage);
|
|
362
326
|
this.elements.searchPagination.innerHTML = '';
|
|
363
|
-
|
|
364
327
|
if (totalPages <= 1) return;
|
|
365
328
|
|
|
366
|
-
const paginationList = document.createElement(
|
|
367
|
-
paginationList.className =
|
|
329
|
+
const paginationList = document.createElement('ul');
|
|
330
|
+
paginationList.className = 'pagination-list';
|
|
368
331
|
|
|
369
|
-
for (let
|
|
370
|
-
|
|
371
|
-
button.addEventListener('click', () => this.goToPage(i));
|
|
372
|
-
paginationList.appendChild(button);
|
|
332
|
+
for (let page = 0; page < totalPages; page++) {
|
|
333
|
+
paginationList.appendChild(this.createPaginationItem(page));
|
|
373
334
|
}
|
|
374
335
|
|
|
375
336
|
this.elements.searchPagination.appendChild(paginationList);
|
|
376
337
|
}
|
|
377
338
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
const button = document.createElement(
|
|
383
|
-
button.
|
|
384
|
-
button.
|
|
385
|
-
|
|
386
|
-
|
|
339
|
+
createPaginationItem(page) {
|
|
340
|
+
const item = document.createElement('li');
|
|
341
|
+
item.className = 'pagination-item';
|
|
342
|
+
|
|
343
|
+
const button = document.createElement('button');
|
|
344
|
+
button.type = 'button';
|
|
345
|
+
button.className = 'pagination-button';
|
|
346
|
+
button.textContent = page + 1;
|
|
347
|
+
button.setAttribute('aria-label', `${page + 1}`);
|
|
348
|
+
|
|
349
|
+
if (page === this.currentPage) {
|
|
387
350
|
button.classList.add('select');
|
|
351
|
+
button.setAttribute('aria-current', 'page');
|
|
352
|
+
} else {
|
|
353
|
+
button.addEventListener('click', () => this.goToPage(page));
|
|
388
354
|
}
|
|
389
|
-
|
|
390
|
-
|
|
355
|
+
|
|
356
|
+
item.appendChild(button);
|
|
357
|
+
return item;
|
|
391
358
|
}
|
|
392
359
|
|
|
393
|
-
/**
|
|
394
|
-
* 跳转到指定页面
|
|
395
|
-
*/
|
|
396
360
|
goToPage(page) {
|
|
397
361
|
this.currentPage = page;
|
|
398
|
-
this.renderResults(this.currentResults, page);
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
document.querySelectorAll(".pagination-item").forEach((btn, index) => {
|
|
402
|
-
btn.classList.toggle('select', index === page);
|
|
403
|
-
});
|
|
362
|
+
this.renderResults(this.currentResults, page, this.lastSearchTime);
|
|
363
|
+
this.renderPagination(this.currentResults.length);
|
|
364
|
+
if (this.elements.searchResults) this.elements.searchResults.scrollTop = 0;
|
|
404
365
|
}
|
|
405
366
|
|
|
406
|
-
/**
|
|
407
|
-
* 显示空结果消息
|
|
408
|
-
*/
|
|
409
367
|
showEmptyMessage() {
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
this.elements.searchResults.appendChild(
|
|
368
|
+
const empty = document.createElement('span');
|
|
369
|
+
empty.className = 'search-result-empty';
|
|
370
|
+
empty.textContent = Solitude.config.lang?.search?.empty?.replace(/\$\{query}/g, this.currentQuery) ||
|
|
371
|
+
`没有找到与 "${this.currentQuery}" 相关的内容`;
|
|
372
|
+
this.elements.searchResults.appendChild(empty);
|
|
415
373
|
}
|
|
416
374
|
|
|
417
|
-
/**
|
|
418
|
-
* 显示结果计数
|
|
419
|
-
*/
|
|
420
375
|
showResultCount(count, time) {
|
|
421
|
-
const countElement = document.createElement(
|
|
422
|
-
countElement.className =
|
|
423
|
-
|
|
376
|
+
const countElement = document.createElement('span');
|
|
377
|
+
countElement.className = 'search-result-count';
|
|
378
|
+
const template = Solitude.config.lang?.search?.hit || 'Found ${hits} results in ${time} ms';
|
|
379
|
+
countElement.innerHTML = template
|
|
380
|
+
.replace(/\$\{hits}/g, count)
|
|
381
|
+
.replace(/\$\{query}/g, count)
|
|
382
|
+
.replace(/\$\{time}/g, time || '0.00');
|
|
424
383
|
this.elements.searchTips.appendChild(countElement);
|
|
425
384
|
}
|
|
426
385
|
|
|
427
|
-
|
|
428
|
-
* 显示错误消息
|
|
429
|
-
*/
|
|
430
|
-
showErrorMessage(message) {
|
|
386
|
+
showStatusMessage(message, className) {
|
|
431
387
|
if (!this.elements.searchResults) return;
|
|
432
|
-
|
|
433
388
|
this.elements.searchResults.innerHTML = '';
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
389
|
+
this.elements.searchTips.innerHTML = '';
|
|
390
|
+
this.elements.searchPagination.innerHTML = '';
|
|
391
|
+
this.setQueryState(true);
|
|
392
|
+
|
|
393
|
+
const status = document.createElement('span');
|
|
394
|
+
status.className = className;
|
|
395
|
+
status.textContent = message;
|
|
396
|
+
this.elements.searchResults.appendChild(status);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
showErrorMessage(message) {
|
|
400
|
+
this.showStatusMessage(message, 'search-result-error');
|
|
438
401
|
}
|
|
439
402
|
|
|
440
|
-
/**
|
|
441
|
-
* 清空搜索结果
|
|
442
|
-
*/
|
|
443
403
|
clearSearchResults() {
|
|
444
404
|
if (this.elements.searchResults) this.elements.searchResults.innerHTML = '';
|
|
445
405
|
if (this.elements.searchPagination) this.elements.searchPagination.innerHTML = '';
|
|
446
406
|
if (this.elements.searchTips) this.elements.searchTips.innerHTML = '';
|
|
447
|
-
|
|
407
|
+
|
|
448
408
|
this.currentResults = [];
|
|
449
409
|
this.currentPage = 0;
|
|
410
|
+
this.lastSearchTime = null;
|
|
411
|
+
this.setQueryState(false);
|
|
450
412
|
}
|
|
451
413
|
|
|
452
|
-
/**
|
|
453
|
-
* 防抖函数
|
|
454
|
-
*/
|
|
455
414
|
debounce(func, wait) {
|
|
456
415
|
return (...args) => {
|
|
457
416
|
clearTimeout(this.searchTimeout);
|
|
@@ -460,7 +419,13 @@ class LocalSearch {
|
|
|
460
419
|
}
|
|
461
420
|
}
|
|
462
421
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
422
|
+
const initializeLocalSearch = () => {
|
|
423
|
+
if (!Solitude.localSearch) Solitude.localSearch = new LocalSearch();
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
if (document.readyState === 'loading') {
|
|
427
|
+
document.addEventListener('DOMContentLoaded', initializeLocalSearch, { once: true });
|
|
428
|
+
} else {
|
|
429
|
+
initializeLocalSearch();
|
|
430
|
+
}
|
|
431
|
+
})();
|