hexo-theme-stellar 1.37.0 → 1.38.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 (67) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/_config.yml +13 -8
  3. package/languages/en.yml +37 -0
  4. package/languages/zh-CN.yml +37 -0
  5. package/languages/zh-TW.yml +37 -0
  6. package/layout/_partial/comments/layout.ejs +2 -1
  7. package/layout/_partial/main/navbar/article_banner.ejs +11 -5
  8. package/layout/_partial/main/post_list/post_card.ejs +1 -1
  9. package/layout/_partial/scripts/defines.ejs +2 -1
  10. package/layout/_partial/scripts/utils.ejs +58 -15
  11. package/layout/_partial/sidebar/grad-def.ejs +26 -0
  12. package/layout/_partial/sidebar/index_leftbar.ejs +25 -10
  13. package/layout/_partial/sidebar/logo.ejs +21 -0
  14. package/layout/_partial/sidebar/menu.ejs +11 -5
  15. package/layout/_partial/widgets/toc.ejs +12 -8
  16. package/layout/_plugins/scrollreveal.ejs +17 -2
  17. package/layout/index.ejs +27 -1
  18. package/layout/notebooks.ejs +3 -1
  19. package/layout/notes.ejs +3 -1
  20. package/package.json +1 -1
  21. package/scripts/tags/index.js +1 -0
  22. package/scripts/tags/lib/mbti.js +169 -0
  23. package/source/css/_common/canonical.styl +9 -10
  24. package/source/css/_common/highlight.styl +0 -2
  25. package/source/css/_common/html.styl +2 -1
  26. package/source/css/_common/svg.styl +9 -1
  27. package/source/css/_components/list.styl +36 -16
  28. package/source/css/_components/md.styl +0 -1
  29. package/source/css/_components/pages/article-story.styl +1 -3
  30. package/source/css/_components/partial/article-banner.styl +17 -11
  31. package/source/css/_components/partial/article-footer.styl +0 -1
  32. package/source/css/_components/partial/navbar.styl +3 -3
  33. package/source/css/_components/sidebar/logo.styl +26 -0
  34. package/source/css/_components/sidebar/menu.styl +53 -30
  35. package/source/css/_components/sidebar/nav-area.styl +1 -1
  36. package/source/css/_components/sidebar/search.styl +46 -7
  37. package/source/css/_components/sidebar/sidebar.styl +5 -1
  38. package/source/css/_components/tag-plugins/banner.styl +2 -1
  39. package/source/css/_components/tag-plugins/chat.styl +3 -1
  40. package/source/css/_components/tag-plugins/folding.styl +1 -2
  41. package/source/css/_components/tag-plugins/friends.styl +1 -0
  42. package/source/css/_components/tag-plugins/friends_posts.styl +2 -1
  43. package/source/css/_components/tag-plugins/gallery.styl +1 -2
  44. package/source/css/_components/tag-plugins/grid.styl +1 -2
  45. package/source/css/_components/tag-plugins/image.styl +0 -2
  46. package/source/css/_components/tag-plugins/link.styl +0 -1
  47. package/source/css/_components/tag-plugins/mbti.styl +80 -0
  48. package/source/css/_components/tag-plugins/note.styl +1 -2
  49. package/source/css/_components/tag-plugins/okr.styl +1 -2
  50. package/source/css/_components/tag-plugins/sites.styl +0 -1
  51. package/source/css/_components/tag-plugins/timeline.styl +1 -6
  52. package/source/css/_components/widgets/components.styl +14 -7
  53. package/source/css/_components/widgets/ghuser.styl +2 -1
  54. package/source/css/_components/widgets/list.styl +20 -8
  55. package/source/css/_components/widgets/markdown.styl +4 -1
  56. package/source/css/_components/widgets/related.styl +6 -4
  57. package/source/css/_components/widgets/toc.styl +2 -2
  58. package/source/css/_components/widgets/widgets.styl +6 -9
  59. package/source/css/_custom.styl +2 -1
  60. package/source/css/_defines/const.styl +1 -1
  61. package/source/css/_defines/func.styl +13 -1
  62. package/source/css/_defines/theme_base.styl +22 -1
  63. package/source/css/_plugins/comments/artalk.styl +2 -2
  64. package/source/css/_plugins/comments/twikoo.styl +0 -2
  65. package/source/css/_plugins/tianli_gpt.styl +3 -0
  66. package/source/js/main.js +39 -17
  67. package/source/js/services/memos.js +12 -3
@@ -62,6 +62,25 @@ x-set-text-colors($scheme, $p0 = 1, $p1 = 0.8, $p2 = 0.7, $p3 = 0.5, $p4 = 0.4)
62
62
  --block-border: x-hsla($hue, $sat, $light, $alpha * 1.5)
63
63
 
64
64
 
65
+ // ========== 通用主题色渐变 ==========
66
+ // 约定:渐变颜色通过 --item-theme 提供,未设置时回退到站点主题色 var(--theme);
67
+ // 淡色变体为 --item-theme-light(默认与白色混合 50%);角度默认取 style.gradient.angle。
68
+ // 注意:局部覆盖 --item-theme 的元素需同时设置 --item-theme-light(如 x-theme-light()),
69
+ // 否则淡色变体仍沿用 :root 上的主题色。
70
+ // 用法示例:
71
+ // .foo
72
+ // --item-theme: #1BCDFC // 可选,默认使用站点主题色
73
+ // background: x-theme-gradient() // 右上角淡色 → 左下角主色,停止点 20% / 60%
74
+ // background: x-theme-gradient(30%, 70%, 25%) // 自定义停止点与淡色比例
75
+ // 图标渐变(SVG):元素内放置 <svg class="grad-def">…<linearGradient …/></svg>,
76
+ // 并设置 --item-grad:url(#渐变id),然后对子元素使用 fill:var(--item-grad)。
77
+ x-theme-light($color = unquote('var(--item-theme)'), $ratio = 50%)
78
+ return unquote('color-mix(in srgb, %s %s, #fff)' % ($color $ratio))
79
+
80
+ x-theme-gradient($color = unquote('var(--item-theme)'), $start = 20%, $end = 60%, $light-ratio = 50%, $angle = convert(hexo-config('style.gradient.angle')) or 225deg)
81
+ $light = x-theme-light($color, $light-ratio)
82
+ return unquote('linear-gradient(%s, %s %s, %s %s)' % ($angle $light $start $color $end))
83
+
65
84
  // ---------------- apply theme ----------------
66
85
  // 设置浅色模式
67
86
  dynamic-theme-light()
@@ -92,6 +111,9 @@ dynamic-theme-dark()
92
111
  --accent: $c-accent
93
112
  // 链接
94
113
  x-set-link-with-color($c-link)
114
+ // 通用渐变颜色(局部可用 --item-theme 覆盖)
115
+ --item-theme: var(--theme)
116
+ --item-theme-light: x-theme-light()
95
117
 
96
118
  // dynamic colors
97
119
  dynamic-theme-light()
@@ -103,4 +125,3 @@ dynamic-theme-dark()
103
125
  dynamic-theme-light()
104
126
  :root[data-theme="dark"]
105
127
  dynamic-theme-dark()
106
-
@@ -48,6 +48,7 @@
48
48
  padding: 0
49
49
  .atk-avatar img
50
50
  border-radius: 50%
51
+ corner-shape: round
51
52
  width: 36px
52
53
  height: 36px
53
54
  .atk-comment-wrap>.atk-comment>.atk-main
@@ -158,7 +159,6 @@
158
159
  >img
159
160
  margin: var(--gap-p) auto
160
161
  border-radius: $border-image-s
161
- corner-shape: superellipse(1.2)
162
162
  a>img
163
163
  display: inline
164
164
  img[atk-emoticon]
@@ -180,4 +180,4 @@
180
180
  a
181
181
  --at-color-main: var(--link)
182
182
  pre>code
183
- padding: 0
183
+ padding: 0
@@ -6,7 +6,6 @@
6
6
  .vemoji, .tk-owo-emotion
7
7
  width: unset
8
8
  border-radius: $border-image-s
9
- corner-shape: superellipse(1.2)
10
9
  max-height: 24px
11
10
  max-width: 100px
12
11
  display: inline
@@ -15,7 +14,6 @@
15
14
  max-width: 400px
16
15
  max-height: 400px
17
16
  border-radius: $border-image-s
18
- corner-shape: superellipse(1.2)
19
17
 
20
18
  .OwO
21
19
  .OwO-body
@@ -64,6 +64,7 @@
64
64
  #post-ai .ai-cursor
65
65
  display inline-block
66
66
  border-radius 50%
67
+ corner-shape round
67
68
  background var(--ai-cursor)
68
69
  width: 12px
69
70
  height: 12px
@@ -167,6 +168,7 @@
167
168
  background var(--ai-font-color)
168
169
  margin-left 7px
169
170
  border-radius 50%
171
+ corner-shape round
170
172
  display flex
171
173
  flex-direction row
172
174
  flex-wrap wrap
@@ -186,6 +188,7 @@
186
188
  background var(--ai-speech-content)
187
189
  height 8px
188
190
  border-radius 50%
191
+ corner-shape round
189
192
  -webkit-transition .3s
190
193
  -moz-transition .3s
191
194
  -o-transition .3s
package/source/js/main.js CHANGED
@@ -207,6 +207,22 @@ const init = {
207
207
  sidebar.dismiss();
208
208
  });
209
209
  },
210
+ wikiStart: () => {
211
+ utils.dom('#l_cover .l_cover.wiki .start-wrap a.button.start').click(function (e) {
212
+ const href = this.getAttribute("href");
213
+ const id = href && href.indexOf("#") === 0 ? decodeURIComponent(href.slice(1)) : null;
214
+ const target = id && document.getElementById(id);
215
+ if (target) {
216
+ e.preventDefault();
217
+ // #start 锚点贴顶滚动,不预留 offset
218
+ const offset = 0;
219
+ smoothScrollTo(target.getBoundingClientRect().top + window.scrollY - offset);
220
+ if (window.history && window.history.pushState) {
221
+ window.history.pushState(null, "", href);
222
+ }
223
+ }
224
+ });
225
+ },
210
226
  leftbarScroll: () => {
211
227
  const container = document.querySelector('.l_left .widgets');
212
228
  if (container == null) {
@@ -301,13 +317,21 @@ const init = {
301
317
 
302
318
  canonicalCheck: () => {
303
319
  const canonical = window.canonical;
320
+ // 真实主站域名优先从 encoded(base64)反解,避免被「批量替换域名」的克隆站把提示指向自己
321
+ const getOriginalHost = () => {
322
+ try {
323
+ return atob(canonical.encoded || '') || canonical.originalHost || '';
324
+ } catch (e) {
325
+ return canonical.originalHost || '';
326
+ }
327
+ };
304
328
  function originStatusCheck() {
305
329
  return new Promise((resolve) => {
306
- if (window.canonical.originalHost === window.location.hostname) {
330
+ if (getOriginalHost() === window.location.hostname) {
307
331
  resolve(true);
308
332
  return;
309
333
  }
310
- const scriptUrl = `https://${window.canonical.originalHost}${window.canonical.param.checklink}`;
334
+ const scriptUrl = `https://${getOriginalHost()}${window.canonical.param.checklink}`;
311
335
  const script = document.createElement('script');
312
336
  script.src = scriptUrl;
313
337
  script.type = 'text/javascript';
@@ -322,18 +346,23 @@ const init = {
322
346
  meta.content = 'noindex, nofollow';
323
347
  document.head.appendChild(meta);
324
348
  const notice = document.createElement('div');
325
- const originalURL = `https://${canonical.originalHost}`;
326
- const currentURL = canonical.param.permalink.startsWith("http") ? canonical.param.permalink : originalURL;
349
+ const originalURL = `https://${getOriginalHost()}`;
350
+ let currentURL = originalURL;
351
+ if (canonical.param.permalink && canonical.param.permalink.startsWith("http")) {
352
+ try {
353
+ const permalinkURL = new URL(canonical.param.permalink);
354
+ currentURL = `${originalURL}${permalinkURL.pathname}${permalinkURL.search}`;
355
+ } catch (e) {
356
+ // permalink 异常时退回源站首页
357
+ }
358
+ }
327
359
  if (isOfficial) {
328
- const closeEnable = window.localStorage.getItem('Stellar.canonical.closeEnable') === 'true'
329
- const closedToday = window.localStorage.getItem('Stellar.canonical.closeTime') === new Date().toDateString()
330
- if ((closeEnable && closedToday) || !(await originStatusCheck())) return;
360
+ if (!(await originStatusCheck())) return;
331
361
  notice.className = 'canonical-tip official';
332
362
  notice.innerHTML = `
333
363
  <a href="${currentURL}" target="_self" rel="noopener noreferrer">
334
364
  本站为官方备用站,仅供应急。点击移步主站<br>${originalURL}
335
365
  </a>
336
- ${canonical.closeEnable ? '<button id="canonical-close">' + canonical.closeText || '关闭提示' + '</button>' : ''}
337
366
  `;
338
367
  } else {
339
368
  notice.className = 'canonical-tip unofficial';
@@ -346,16 +375,8 @@ const init = {
346
375
  `;
347
376
  }
348
377
  document.body.appendChild(notice);
349
- const closeBtn = notice.querySelector('#canonical-close');
350
- if (closeBtn) {
351
- closeBtn.addEventListener('click', function () {
352
- window.localStorage.setItem('Stellar.canonical.closeEnable', "true")
353
- window.localStorage.setItem('Stellar.canonical.closeTime', new Date().toDateString())
354
- notice.style.display = 'none';
355
- });
356
- }
357
378
  }
358
- if (!canonical.originalHost) return;
379
+ if (!getOriginalHost()) return;
359
380
  const currentURL = new URL(window.location.href);
360
381
  const currentHost = currentURL.hostname.replace(/^www\./, '');
361
382
  if (currentHost == 'localhost') return;
@@ -395,6 +416,7 @@ window.stellar = window.stellar || {};
395
416
  stellar.initPage = function () {
396
417
  init.toc();
397
418
  init.sidebar();
419
+ init.wikiStart();
398
420
  init.leftbarScroll();
399
421
  init.relativeDate(document.querySelectorAll('#post-meta time'));
400
422
  init.registerTabsTag();
@@ -48,7 +48,8 @@
48
48
  let user = memos.users.find(user => user.id === parseInt(creatorId));
49
49
  if (!user) {
50
50
  if (!memos.requests[creatorId]) {
51
- memos.requests[creatorId] = fetch(`${memos.site}/api/v1/users/${creatorId}`)
51
+ // 走统一请求入口,用户详情同样纳入本地缓存(service: memos-user)
52
+ memos.requests[creatorId] = utils.requestWithoutLoading(`${memos.site}/api/v1/users/${creatorId}`, { service: 'memos-user' })
52
53
  .then(response => response.json())
53
54
  .then(data => {
54
55
  if (data.username) {
@@ -58,6 +59,10 @@
58
59
  user = null;
59
60
  }
60
61
  })
62
+ .catch(() => {
63
+ // 用户详情失败时回退默认昵称/头像,不阻塞 memo 渲染
64
+ user = null;
65
+ })
61
66
  .finally(() => delete memos.requests[creatorId]);
62
67
  }
63
68
  await memos.requests[creatorId];
@@ -78,7 +83,8 @@
78
83
  let user = memos.users.find(user => user.name.split('/')[1] === creatorId);
79
84
  if (!user) {
80
85
  if (!memos.requests[creatorId]) {
81
- memos.requests[creatorId] = fetch(`${memos.site}/api/v1/users/${creatorId}`)
86
+ // 走统一请求入口,用户详情同样纳入本地缓存(service: memos-user)
87
+ memos.requests[creatorId] = utils.requestWithoutLoading(`${memos.site}/api/v1/users/${creatorId}`, { service: 'memos-user' })
82
88
  .then(response => response.json())
83
89
  .then(data => {
84
90
  if (data.username) {
@@ -88,12 +94,15 @@
88
94
  user = null;
89
95
  }
90
96
  })
97
+ .catch(() => {
98
+ // 用户详情失败时回退默认昵称/头像,不阻塞 memo 渲染
99
+ user = null;
100
+ })
91
101
  .finally(() => delete memos.requests[creatorId]);
92
102
  }
93
103
  await memos.requests[creatorId];
94
104
  user = memos.users.find(user => user.name.split('/')[1] === creatorId);
95
105
  }
96
- console.log(JSON.stringify(user));
97
106
  const name = user ? user.displayName || user.username : 'memos';
98
107
  const avatarUrl = user?.avatarUrl ? `${memos.site}${user.avatarUrl}` : default_avatar || '';
99
108
  return `<div class="user-info">${avatarUrl ? `<img src="${avatarUrl}">` : ''}<span>${name}</span></div>`;