hexo-theme-gnix 8.0.0 → 10.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.
Files changed (66) hide show
  1. package/README.md +4 -2
  2. package/include/hexo/feed.js +6 -5
  3. package/include/hexo/filter.js +25 -1
  4. package/include/hexo/generator/archive.js +116 -0
  5. package/include/hexo/generator/home.js +64 -0
  6. package/include/hexo/generator/index.js +82 -0
  7. package/include/hexo/generator/md_generator.js +87 -0
  8. package/include/hexo/generator/page.js +55 -0
  9. package/include/hexo/generator/tag.js +84 -0
  10. package/include/hexo/helper.js +38 -0
  11. package/include/hexo/i18n.js +183 -0
  12. package/include/util/article_font.js +132 -0
  13. package/include/util/i18n.js +280 -0
  14. package/include/util/theme.js +84 -0
  15. package/languages/en.yml +28 -0
  16. package/languages/zh-CN.yml +28 -0
  17. package/layout/archive.jsx +131 -127
  18. package/layout/common/article.jsx +316 -34
  19. package/layout/common/article_info.jsx +339 -0
  20. package/layout/common/article_media.jsx +11 -4
  21. package/layout/common/comment.jsx +15 -7
  22. package/layout/common/footer.jsx +6 -5
  23. package/layout/common/head.jsx +122 -33
  24. package/layout/common/navbar.jsx +195 -65
  25. package/layout/common/theme_selector.jsx +16 -14
  26. package/layout/layout.jsx +43 -5
  27. package/layout/misc/open_graph.jsx +162 -66
  28. package/layout/misc/paginator.jsx +2 -8
  29. package/layout/plugin/cookie_consent.jsx +252 -53
  30. package/layout/plugin/swup.jsx +1 -1
  31. package/layout/search/insight.jsx +1 -1
  32. package/layout/tag.jsx +3 -2
  33. package/layout/tags.jsx +81 -73
  34. package/package.json +5 -5
  35. package/scripts/index.js +1 -0
  36. package/source/css/archive.css +225 -180
  37. package/source/css/default.css +1223 -126
  38. package/source/css/responsive.css +426 -0
  39. package/source/css/shiki/shiki.css +12 -2081
  40. package/source/css/tags.css +183 -0
  41. package/source/css/twikoo.css +1053 -1049
  42. package/source/img/favicon.svg +1 -6
  43. package/source/img/og_image.webp +0 -0
  44. package/source/js/article-font-utils.js +99 -0
  45. package/source/js/busuanzi.js +91 -24
  46. package/source/js/components/chat.js +169 -50
  47. package/source/js/components/image-carousel.js +152 -108
  48. package/source/js/components/sidenote.js +210 -0
  49. package/source/js/components/text-image-section.js +78 -90
  50. package/source/js/components/theme-stacked.js +65 -33
  51. package/source/js/components/tree.js +30 -16
  52. package/source/js/decrypt.js +7 -2
  53. package/source/js/main.js +428 -5
  54. package/source/js/swup.js +39 -0
  55. package/source/js/theme-selector.js +26 -16
  56. package/include/hexo/generator.js +0 -53
  57. package/layout/misc/article_licensing.jsx +0 -99
  58. package/source/css/responsive/desktop.css +0 -36
  59. package/source/css/responsive/mobile.css +0 -38
  60. package/source/css/responsive/tablet.css +0 -43
  61. package/source/css/responsive/touch.css +0 -155
  62. package/source/img/logo.svg +0 -9
  63. package/source/js/archive-breadcrumb.js +0 -132
  64. package/source/js/host/cookieconsent/3.1.1/build/cookieconsent.min.css +0 -6
  65. package/source/js/host/cookieconsent/3.1.1/build/cookieconsent.min.js +0 -1
  66. package/source/js/swup.bundle.js +0 -1
@@ -1,6 +1 @@
1
- <svg
2
- xmlns="http://www.w3.org/2000/svg" version="1.1" width="256" height="256" viewbox="0 0 949 256">
3
- <path fill="#2366d1" d="M110.85125168440814 128L221.70250336881628 192L110.85125168440814 256L0 192Z"/>
4
- <path fill="#609dff" d="M110.85125168440814 64L221.70250336881628 128L110.85125168440814 192L0 128Z"/>
5
- <path fill="#a4c7ff" d="M110.85125168440814 0L221.70250336881628 64L110.85125168440814 128L0 64Z"/>
6
- </svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256"> <circle fill="#0821A8" cx="128" cy="128" r="128" /> </svg>
Binary file
@@ -0,0 +1,99 @@
1
+ (() => {
2
+ function normalizeCustomFontImport(value) {
3
+ if (typeof value !== "string") return null;
4
+
5
+ var href = value.trim();
6
+ if (!href || /[\s<>"']/.test(href) || /^javascript:/i.test(href)) return null;
7
+ if (!/^(https?:)?\/\//i.test(href) && href.charAt(0) !== "/") return null;
8
+
9
+ return href;
10
+ }
11
+
12
+ function normalizeCustomFontFamily(value) {
13
+ if (typeof value !== "string") return null;
14
+
15
+ var family = value.trim();
16
+ if (!family || /[{};<>]/.test(family)) return null;
17
+
18
+ return family;
19
+ }
20
+
21
+ function normalizeCustomFonts(value, familyOptions, importLimit) {
22
+ var customFonts = value && typeof value === "object" ? value : {};
23
+ var sourceFamilies = customFonts.families && typeof customFonts.families === "object" ? customFonts.families : {};
24
+ var importSource = customFonts.imports;
25
+ var importValues = Array.isArray(importSource) ? importSource : typeof importSource === "string" ? importSource.split(/\r?\n/) : [];
26
+ var imports = [];
27
+ var families = {};
28
+ var limit = Number(importLimit);
29
+
30
+ if (!Number.isFinite(limit) || limit < 1) limit = 6;
31
+
32
+ importValues.forEach((item) => {
33
+ var href = normalizeCustomFontImport(item);
34
+ if (href && imports.indexOf(href) === -1 && imports.length < limit) {
35
+ imports.push(href);
36
+ }
37
+ });
38
+
39
+ Object.keys(familyOptions || {}).forEach((key) => {
40
+ var family = normalizeCustomFontFamily(sourceFamilies[key]);
41
+ if (family) families[key] = family;
42
+ });
43
+
44
+ return {
45
+ imports: imports,
46
+ families: families,
47
+ };
48
+ }
49
+
50
+ function applyCustomFontImports(imports, selector) {
51
+ var signature = Array.isArray(imports) ? imports.join("\n") : "";
52
+ var sel = selector || 'link[data-gnix-custom-font="true"]';
53
+ var currentLinks = [];
54
+
55
+ if (typeof document !== "undefined") {
56
+ currentLinks = Array.prototype.slice.call(document.querySelectorAll(sel));
57
+ }
58
+
59
+ var currentHrefs = currentLinks.map((link) => link.getAttribute("href") || link.href);
60
+
61
+ if (signature === applyCustomFontImports._signature && currentHrefs.length === (imports ? imports.length : 0) && (!imports || imports.every((href) => currentHrefs.indexOf(href) !== -1))) {
62
+ return;
63
+ }
64
+
65
+ currentLinks.forEach((link) => {
66
+ link.remove();
67
+ });
68
+ applyCustomFontImports._signature = signature;
69
+
70
+ if (!document || !document.head || !Array.isArray(imports)) return;
71
+
72
+ imports.forEach((href, index) => {
73
+ var link = document.createElement("link");
74
+ link.rel = "stylesheet";
75
+ link.href = href;
76
+ link.setAttribute("data-gnix-custom-font", "true");
77
+ link.setAttribute("data-gnix-custom-font-index", String(index));
78
+ document.head.appendChild(link);
79
+ });
80
+ }
81
+
82
+ function applyCustomFontFamilies(html, families, familyOptions) {
83
+ Object.keys(familyOptions || {}).forEach((key) => {
84
+ if (families?.[key]) {
85
+ html.style.setProperty(familyOptions[key], families[key]);
86
+ } else {
87
+ html.style.removeProperty(familyOptions[key]);
88
+ }
89
+ });
90
+ }
91
+
92
+ window.__GNIX_ARTICLE_FONT_UTILS__ = {
93
+ normalizeCustomFontImport: normalizeCustomFontImport,
94
+ normalizeCustomFontFamily: normalizeCustomFontFamily,
95
+ normalizeCustomFonts: normalizeCustomFonts,
96
+ applyCustomFontImports: applyCustomFontImports,
97
+ applyCustomFontFamilies: applyCustomFontFamilies,
98
+ };
99
+ })();
@@ -1,33 +1,100 @@
1
1
  !(() => {
2
2
  const TYPES = ["site_pv", "site_uv", "page_pv", "page_uv"];
3
+ const PAGE_TYPES = ["page_pv", "page_uv"];
3
4
  const STORAGE_KEY = "bsz-id";
5
+ const API = "https://bsz.dusays.com:9001/api";
4
6
  const BASE = { site_pv: 12801, site_uv: 2450 };
5
7
 
8
+ const getPagePathVariants = () => {
9
+ const path = location.pathname || "/";
10
+ let base = path;
11
+
12
+ if (path.endsWith("/index.html")) {
13
+ base = path.slice(0, -"/index.html".length) || "/";
14
+ } else if (path !== "/" && path.endsWith("/")) {
15
+ base = path.slice(0, -1);
16
+ } else if (/\/[^/]+\.[^/]+$/.test(path)) {
17
+ return [path];
18
+ }
19
+
20
+ if (base === "/") return ["/", "/index.html"];
21
+ return [base, `${base}/`, `${base}/index.html`];
22
+ };
23
+
24
+ const toReferer = (path) => `${location.origin}${path}`;
25
+
26
+ const unique = (values) => [...new Set(values)];
27
+
28
+ const request = (method, referer) =>
29
+ new Promise((resolve, reject) => {
30
+ const xhr = new XMLHttpRequest();
31
+ xhr.open(method, API, true);
32
+
33
+ const token = localStorage.getItem(STORAGE_KEY);
34
+ token && xhr.setRequestHeader("Authorization", `Bearer ${token}`);
35
+ xhr.setRequestHeader("x-bsz-referer", referer);
36
+
37
+ xhr.onload = () => {
38
+ if (xhr.status !== 200) {
39
+ reject(new Error(`Busuanzi responded with ${xhr.status}`));
40
+ return;
41
+ }
42
+
43
+ let response;
44
+ try {
45
+ response = JSON.parse(xhr.responseText);
46
+ } catch (error) {
47
+ reject(error);
48
+ return;
49
+ }
50
+
51
+ const { success, data } = response;
52
+ if (!success) {
53
+ reject(new Error("Busuanzi request failed"));
54
+ return;
55
+ }
56
+
57
+ const newToken = xhr.getResponseHeader("Set-Bsz-Identity");
58
+ if (newToken) localStorage.setItem(STORAGE_KEY, newToken);
59
+
60
+ resolve(data || {});
61
+ };
62
+ xhr.onerror = () => reject(new Error("Busuanzi request failed"));
63
+ xhr.send();
64
+ });
65
+
66
+ const render = (data) => {
67
+ TYPES.forEach((type) => {
68
+ const el = document.getElementById(`busuanzi_${type}`);
69
+ if (el) el.innerHTML = (BASE[type] || 0) + (data[type] || 0);
70
+
71
+ const container = document.getElementById(`busuanzi_container_${type}`);
72
+ if (container) container.style.display = "inline";
73
+ });
74
+ };
75
+
6
76
  const update = () => {
7
- const xhr = new XMLHttpRequest();
8
- xhr.open("POST", "https://bsz.dusays.com:9001/api", true);
9
-
10
- const token = localStorage.getItem(STORAGE_KEY);
11
- token && xhr.setRequestHeader("Authorization", `Bearer ${token}`);
12
- xhr.setRequestHeader("x-bsz-referer", location.href);
13
-
14
- xhr.onload = () => {
15
- if (xhr.status !== 200) return;
16
- const { success, data } = JSON.parse(xhr.responseText);
17
- if (!success) return;
18
-
19
- TYPES.forEach((type) => {
20
- const el = document.getElementById(`busuanzi_${type}`);
21
- if (el) el.innerHTML = (BASE[type] || 0) + (data[type] || 0);
22
-
23
- const container = document.getElementById(`busuanzi_container_${type}`);
24
- if (container) container.style.display = "inline";
25
- });
26
-
27
- const newToken = xhr.getResponseHeader("Set-Bsz-Identity");
28
- if (newToken) localStorage.setItem(STORAGE_KEY, newToken);
29
- };
30
- xhr.send();
77
+ request("POST", location.href)
78
+ .then((data) => {
79
+ const currentPath = location.pathname || "/";
80
+ const referers = unique(getPagePathVariants().map(toReferer)).filter((referer) => new URL(referer).pathname !== currentPath);
81
+ if (!referers.length) return data;
82
+
83
+ return Promise.all(referers.map((referer) => request("GET", referer).catch(() => null))).then((results) => {
84
+ const merged = { ...data };
85
+
86
+ results.forEach((result) => {
87
+ if (!result) return;
88
+ PAGE_TYPES.forEach((type) => {
89
+ merged[type] = (merged[type] || 0) + (result[type] || 0);
90
+ });
91
+ });
92
+
93
+ return merged;
94
+ });
95
+ })
96
+ .then(render)
97
+ .catch(() => {});
31
98
  };
32
99
 
33
100
  update();
@@ -24,74 +24,77 @@
24
24
  const CHAT_STYLES = `
25
25
  :host {
26
26
  display: block;
27
- font-family: var(--font-sans, sans-serif);
27
+ font-family: var(--article-font-family, var(--font-sans-serif, system-ui, sans-serif));
28
+ color: var(--text, #cdd6f4);
28
29
  }
29
30
 
30
31
  .chat-container {
31
32
  display: flex;
32
33
  flex-direction: column;
33
- gap: 16px;
34
- padding: 16px;
35
- background: var(--base, #1e1e2e);
36
- border-radius: 12px;
37
- max-height: 500px;
34
+ gap: 22px;
35
+ padding: 4px 0;
36
+ max-height: 640px;
38
37
  overflow-y: auto;
39
- scrollbar-width: none;
40
- -ms-overflow-style: none;
38
+ overscroll-behavior: contain;
39
+ scrollbar-width: thin;
40
+ scrollbar-color: hsl(from var(--text, #cdd6f4) h s l / 0.18) transparent;
41
41
  }
42
42
 
43
- .chat-container::-webkit-scrollbar {
44
- display: none;
43
+ .chat-container::-webkit-scrollbar { width: 6px; }
44
+ .chat-container::-webkit-scrollbar-thumb {
45
+ background: hsl(from var(--text, #cdd6f4) h s l / 0.18);
46
+ border-radius: 3px;
45
47
  }
48
+ .chat-container::-webkit-scrollbar-track { background: transparent; }
46
49
 
47
50
  .chat-message {
48
- display: flex;
49
- gap: 12px;
50
- align-items: flex-start;
51
+ display: grid;
52
+ grid-template-columns: auto minmax(0, 1fr);
53
+ grid-template-areas:
54
+ "avatar header"
55
+ "avatar bubble";
56
+ column-gap: 12px;
57
+ row-gap: 6px;
58
+ align-items: start;
51
59
  }
52
60
 
53
61
  .chat-message.is-me {
54
- flex-direction: row-reverse;
62
+ grid-template-columns: minmax(0, 1fr) auto;
63
+ grid-template-areas:
64
+ "header avatar"
65
+ "bubble avatar";
55
66
  }
56
67
 
57
- .avatar {
58
- width: 40px;
59
- height: 40px;
68
+ .avatar,
69
+ .avatar-placeholder {
70
+ grid-area: avatar;
71
+ width: 36px;
72
+ height: 36px;
60
73
  border-radius: 50%;
61
- object-fit: cover;
62
- flex-shrink: 0;
74
+ align-self: start;
63
75
  }
64
76
 
77
+ .avatar { object-fit: cover; }
78
+
65
79
  .avatar-placeholder {
66
- width: 40px;
67
- height: 40px;
68
- border-radius: 50%;
69
80
  display: flex;
70
81
  align-items: center;
71
82
  justify-content: center;
72
- font-size: 18px;
83
+ font-size: 14px;
73
84
  font-weight: 600;
74
85
  color: var(--text, #cdd6f4);
75
- flex-shrink: 0;
76
86
  background: var(--surface1, #45475a);
77
87
  }
78
88
 
79
- .message-content {
80
- display: flex;
81
- flex-direction: column;
82
- gap: 4px;
83
- max-width: 70%;
84
- }
85
-
86
- .chat-message.is-me .message-content {
87
- align-items: flex-end;
88
- }
89
-
90
89
  .message-header {
90
+ grid-area: header;
91
91
  display: flex;
92
- align-items: center;
92
+ align-items: baseline;
93
93
  gap: 8px;
94
+ padding: 2px 4px 0;
94
95
  font-size: 12px;
96
+ line-height: 1;
97
+ min-width: 0;
95
98
  }
96
99
 
97
100
  .chat-message.is-me .message-header {
@@ -101,39 +104,114 @@ const CHAT_STYLES = `
101
104
  .sender-name {
102
105
  font-weight: 600;
103
106
  color: var(--subtext1, #bac2de);
107
+ letter-spacing: 0.01em;
108
+ overflow: hidden;
109
+ text-overflow: ellipsis;
110
+ white-space: nowrap;
104
111
  }
105
112
 
106
113
  .timestamp {
107
114
  color: var(--subtext0, #a6adc8);
108
115
  font-size: 11px;
116
+ font-variant-numeric: tabular-nums;
117
+ white-space: nowrap;
109
118
  }
110
119
 
111
120
  .message-bubble {
121
+ grid-area: bubble;
122
+ max-width: min(620px, 100%);
123
+ justify-self: start;
112
124
  padding: 10px 14px;
113
- border-radius: 16px;
114
- background: var(--surface1, #45475a);
115
- color: var(--text, #cdd6f4);
116
- line-height: 1.5;
125
+ border-radius: 14px;
126
+ border-top-left-radius: 4px;
127
+ background: var(--surface0, #313244);
128
+ color: inherit;
117
129
  font-size: 14px;
118
- word-wrap: break-word;
130
+ line-height: 1.65;
131
+ overflow-wrap: anywhere;
132
+ word-break: break-word;
119
133
  }
120
134
 
121
135
  .chat-message.is-me .message-bubble {
136
+ justify-self: end;
122
137
  background: var(--blue, #89b4fa);
123
138
  color: var(--base, #1e1e2e);
139
+ border-radius: 14px;
140
+ border-top-right-radius: 4px;
124
141
  }
125
142
 
143
+ .message-bubble > :first-child { margin-top: 0; }
144
+ .message-bubble > :last-child { margin-bottom: 0; }
145
+
146
+ .message-bubble p { margin: 0 0 8px; }
147
+
148
+ .message-bubble ul,
149
+ .message-bubble ol {
150
+ margin: 6px 0;
151
+ padding-left: 22px;
152
+ }
153
+
154
+ .message-bubble li { margin: 4px 0; }
155
+ .message-bubble li::marker { color: hsl(from currentColor h s l / 0.55); }
156
+
126
157
  .message-bubble a {
127
158
  color: var(--blue, #89b4fa);
128
159
  text-decoration: underline;
160
+ text-underline-offset: 2px;
161
+ text-decoration-thickness: 1px;
162
+ }
163
+
164
+ .chat-message.is-me .message-bubble a {
165
+ color: inherit;
166
+ text-decoration-color: hsl(from currentColor h s l / 0.4);
129
167
  }
130
168
 
131
169
  .message-bubble code {
132
170
  font-family: var(--font-mono, 'Maple Mono', 'Fira Code', monospace);
133
- font-size: 13px;
134
- background: var(--mantle, #181825);
135
- padding: 2px 6px;
171
+ font-size: 0.875em;
172
+ padding: 1px 6px;
136
173
  border-radius: 4px;
174
+ background: hsl(from var(--mantle, #181825) h s l / 0.55);
175
+ }
176
+
177
+ .chat-message.is-me .message-bubble code {
178
+ background: hsl(from var(--base, #1e1e2e) h s l / 0.18);
179
+ }
180
+
181
+ .message-bubble hr {
182
+ margin: 12px 0;
183
+ border: 0;
184
+ height: 1px;
185
+ background: hsl(from currentColor h s l / 0.12);
186
+ }
187
+
188
+ .message-bubble kbd {
189
+ display: inline-block;
190
+ padding: 1px 6px;
191
+ font-family: var(--font-mono, monospace);
192
+ font-size: 0.78em;
193
+ border: 1px solid hsl(from currentColor h s l / 0.3);
194
+ border-radius: 4px;
195
+ background: hsl(from currentColor h s l / 0.05);
196
+ vertical-align: baseline;
197
+ }
198
+
199
+ .message-bubble strong { font-weight: 600; }
200
+
201
+ .message-bubble .chat-heading {
202
+ display: block;
203
+ margin: 14px 0 6px;
204
+ font-size: 15px;
205
+ font-weight: 700;
206
+ letter-spacing: 0.01em;
207
+ line-height: 1.4;
208
+ }
209
+
210
+ .message-bubble .chat-heading:first-child { margin-top: 0; }
211
+
212
+ .message-bubble .chat-heading code {
213
+ font-size: 13px;
214
+ padding: 1px 5px;
137
215
  }
138
216
 
139
217
  .empty-state {
@@ -141,6 +219,49 @@ const CHAT_STYLES = `
141
219
  padding: 40px 20px;
142
220
  color: var(--subtext0, #a6adc8);
143
221
  }
222
+
223
+ @media (max-width: 640px) {
224
+ .chat-container {
225
+ gap: 16px;
226
+ max-height: 75vh;
227
+ }
228
+
229
+ .chat-message {
230
+ grid-template-areas:
231
+ "avatar header"
232
+ "bubble bubble";
233
+ column-gap: 8px;
234
+ row-gap: 4px;
235
+ }
236
+
237
+ .chat-message.is-me {
238
+ grid-template-areas:
239
+ "header avatar"
240
+ "bubble bubble";
241
+ }
242
+
243
+ .avatar,
244
+ .avatar-placeholder {
245
+ width: 26px;
246
+ height: 26px;
247
+ align-self: center;
248
+ }
249
+
250
+ .avatar-placeholder { font-size: 11px; }
251
+
252
+ .message-header {
253
+ align-items: center;
254
+ padding-top: 0;
255
+ }
256
+
257
+ .message-bubble,
258
+ .chat-message.is-me .message-bubble {
259
+ max-width: 100%;
260
+ justify-self: stretch;
261
+ border-radius: 12px;
262
+ padding: 9px 12px;
263
+ }
264
+ }
144
265
  `;
145
266
 
146
267
  class Chat extends HTMLElement {
@@ -195,13 +316,11 @@ class Chat extends HTMLElement {
195
316
  return `
196
317
  <div class="chat-message ${msg.isMe ? "is-me" : ""}">
197
318
  ${avatarHTML}
198
- <div class="message-content">
199
- <div class="message-header">
200
- <span class="sender-name">${msg.name}</span>
201
- <span class="timestamp">${msg.timestamp}</span>
202
- </div>
203
- <div class="message-bubble">${msg.content}</div>
319
+ <div class="message-header">
320
+ <span class="sender-name">${msg.name}</span>
321
+ <span class="timestamp">${msg.timestamp}</span>
204
322
  </div>
323
+ <div class="message-bubble">${msg.content}</div>
205
324
  </div>
206
325
  `;
207
326
  }