hexo-theme-stellar 1.42.0 → 1.43.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 (122) hide show
  1. package/.claude/skills/stellar-theme-dev/SKILL.md +2 -0
  2. package/CHANGELOG.md +42 -0
  3. package/THIRD-PARTY-NOTICES.md +28 -0
  4. package/_config.yml +20 -10
  5. package/_data/icons.yml +10 -9
  6. package/languages/en.yml +11 -0
  7. package/languages/zh-CN.yml +11 -0
  8. package/languages/zh-TW.yml +11 -0
  9. package/layout/_partial/comments/artalk/layout.ejs +1 -1
  10. package/layout/_partial/comments/beaudar/layout.ejs +1 -1
  11. package/layout/_partial/comments/giscus/layout.ejs +1 -1
  12. package/layout/_partial/comments/twikoo/layout.ejs +1 -1
  13. package/layout/_partial/comments/utterances/layout.ejs +1 -1
  14. package/layout/_partial/comments/waline/layout.ejs +1 -1
  15. package/layout/_partial/components/collection-item.ejs +44 -0
  16. package/layout/_partial/components/collection.ejs +26 -0
  17. package/layout/_partial/components/widget-frame.ejs +35 -0
  18. package/layout/_partial/cover/wiki_cover.ejs +82 -26
  19. package/layout/_partial/dropdown.ejs +62 -0
  20. package/layout/_partial/head.ejs +9 -0
  21. package/layout/_partial/main/navbar/breadcrumb/blog.ejs +1 -1
  22. package/layout/_partial/main/post_list/archive_item.ejs +12 -0
  23. package/layout/_partial/main/post_list/topic_card.ejs +21 -9
  24. package/layout/_partial/main/post_list/wiki_card.ejs +49 -10
  25. package/layout/_partial/scripts/bootstrap.ejs +3 -1
  26. package/layout/_partial/scripts/defines.ejs +2 -8
  27. package/layout/_partial/scripts/lazyload.ejs +0 -3
  28. package/layout/_partial/scripts.ejs +1 -0
  29. package/layout/_partial/sidebar/index_leftbar.ejs +12 -2
  30. package/layout/_partial/sidebar/logo.ejs +1 -1
  31. package/layout/_partial/sidebar/menu.ejs +21 -23
  32. package/layout/_partial/widgets/components/linklist.ejs +23 -21
  33. package/layout/_partial/widgets/linklist.ejs +7 -12
  34. package/layout/_partial/widgets/recent.ejs +52 -41
  35. package/layout/_partial/widgets/related.ejs +47 -36
  36. package/layout/_partial/widgets/tagtree.ejs +70 -54
  37. package/layout/_partial/widgets/toc.ejs +37 -62
  38. package/layout/_partial/widgets/tree.ejs +38 -51
  39. package/layout/_plugins/index.ejs +25 -0
  40. package/layout/_plugins/mermaid.ejs +3 -4
  41. package/layout/archive.ejs +11 -20
  42. package/layout/collection-preview.ejs +60 -0
  43. package/layout/index_topic.ejs +12 -6
  44. package/layout/index_wiki.ejs +49 -3
  45. package/layout/layout.ejs +4 -3
  46. package/package.json +1 -1
  47. package/scripts/events/index.js +0 -2
  48. package/scripts/filters/lib/img_lazyload.js +184 -40
  49. package/scripts/generators/stellar-icons.js +1 -1
  50. package/scripts/lib/subtitle.js +5 -1
  51. package/scripts/tags/index.js +1 -0
  52. package/scripts/tags/lib/about.js +1 -1
  53. package/scripts/tags/lib/albums.js +1 -1
  54. package/scripts/tags/lib/banner.js +1 -1
  55. package/scripts/tags/lib/dropdown.js +64 -0
  56. package/scripts/tags/lib/friends.js +1 -1
  57. package/scripts/tags/lib/gallery.js +3 -3
  58. package/scripts/tags/lib/image.js +1 -1
  59. package/scripts/tags/lib/posters.js +1 -1
  60. package/scripts/tags/lib/sites.js +8 -4
  61. package/source/css/_common/base.styl +4 -4
  62. package/source/css/_common/blockquote.styl +2 -2
  63. package/source/css/_common/dropdown.styl +132 -0
  64. package/source/css/_common/html.styl +1 -1
  65. package/source/css/_common/loading.styl +5 -2
  66. package/source/css/_common/title.styl +8 -8
  67. package/source/css/_components/collection.styl +188 -0
  68. package/source/css/_components/layout.styl +1 -1
  69. package/source/css/_components/list.styl +44 -46
  70. package/source/css/_components/md.styl +3 -3
  71. package/source/css/_components/page-transition.styl +22 -0
  72. package/source/css/_components/pages/archives.styl +19 -18
  73. package/source/css/_components/pages/article-indent.styl +2 -2
  74. package/source/css/_components/pages/article-story.styl +9 -5
  75. package/source/css/_components/pages/collection-preview.styl +68 -0
  76. package/source/css/_components/pages/notebook.styl +11 -9
  77. package/source/css/_components/partial/article-footer.styl +7 -4
  78. package/source/css/_components/partial/cover.styl +280 -1
  79. package/source/css/_components/partial/related.styl +10 -6
  80. package/source/css/_components/sidebar/footer.styl +65 -12
  81. package/source/css/_components/sidebar/nav-area.styl +6 -1
  82. package/source/css/_components/sidebar/search.styl +6 -2
  83. package/source/css/_components/tag-plugins/chat.styl +1 -1
  84. package/source/css/_components/tag-plugins/folders.styl +4 -4
  85. package/source/css/_components/tag-plugins/folding.styl +3 -3
  86. package/source/css/_components/tag-plugins/hashtag.styl +2 -3
  87. package/source/css/_components/tag-plugins/mdrender.styl +1 -1
  88. package/source/css/_components/tag-plugins/note.styl +4 -4
  89. package/source/css/_components/tag-plugins/poetry.styl +5 -5
  90. package/source/css/_components/tag-plugins/quot.styl +1 -2
  91. package/source/css/_components/tag-plugins/quote.styl +1 -1
  92. package/source/css/_components/tag-plugins/tabs.styl +2 -2
  93. package/source/css/_components/tag-plugins/timeline.styl +4 -4
  94. package/source/css/_components/widgets/ghrepo.styl +4 -0
  95. package/source/css/_components/widgets/markdown.styl +1 -14
  96. package/source/css/_components/widgets/timeline.styl +2 -2
  97. package/source/css/_components/widgets/toc.styl +22 -20
  98. package/source/css/_components/widgets/widgets.styl +13 -1
  99. package/source/css/_components/wiki-card.styl +209 -0
  100. package/source/css/_custom.styl +22 -20
  101. package/source/css/_plugins/lazyload.styl +8 -5
  102. package/source/css/comments/utterances.styl +2 -2
  103. package/source/css/comments/waline.styl +24 -0
  104. package/source/css/plugins/mermaid.styl +31 -12
  105. package/source/js/icons.js +1 -1
  106. package/source/js/main.js +62 -6
  107. package/source/js/plugins/adaptive-text.js +46 -2
  108. package/source/js/plugins/dropdown.js +435 -0
  109. package/source/js/plugins/galaxy.js +423 -0
  110. package/source/js/search/algolia-search.js +1 -1
  111. package/source/js/search/local-search.js +1 -1
  112. package/source/js/services/ghinfo.js +31 -3
  113. package/source/js/services/siteinfo.js +26 -0
  114. package/source/js/services/sites.js +6 -2
  115. package/source/js/services.js +5 -2
  116. package/source/js/tagtree.js +7 -7
  117. package/source/js/utils.js +1 -1
  118. package/layout/_partial/widgets/components/link.ejs +0 -21
  119. package/source/css/_components/sidebar/menu.styl +0 -77
  120. package/source/css/_components/widgets/components.styl +0 -68
  121. package/source/css/_components/widgets/list.styl +0 -46
  122. package/source/css/_components/widgets/related.styl +0 -40
@@ -8,25 +8,24 @@ $ff-codeblock = convert(hexo-config('style.font-family.codeblock'))
8
8
 
9
9
  // font size
10
10
  $fs-root = convert(hexo-config('style.font-size.root'))
11
- $fs-15 = .9375rem
12
- $fs-14 = .875rem
13
- $fs-13 = .8125rem
14
- $fs-12 = .75rem
11
+ $fs-15 = 'calc(%s - 1px)' % var(--fs-content-base)
12
+ $fs-14 = 'calc(%s - 2px)' % var(--fs-content-base)
13
+ $fs-13 = 'calc(%s - 3px)' % var(--fs-content-base)
14
+ $fs-12 = 'calc(%s - 4px)' % var(--fs-content-base)
15
15
 
16
- $fs-body = convert(hexo-config('style.font-size.body'))
17
16
  $fs-code = convert(hexo-config('style.font-size.code'))
18
17
  $fs-codeblock = convert(hexo-config('style.font-size.codeblock'))
19
18
 
20
- $fsh1 = 'calc(%s + 9px)' % $fs-body
21
- $fsh2 = 'calc(%s + 11px)' % $fs-body
22
- $fsh3 = 'calc(%s + 7px)' % $fs-body
23
- $fsh4 = 'calc(%s + 4px)' % $fs-body
24
- $fsh5 = 'calc(%s + 2px)' % $fs-body
19
+ $fsh1 = 'calc(%s + 9px)' % var(--fs-content-base)
20
+ $fsh2 = 'calc(%s + 11px)' % var(--fs-content-base)
21
+ $fsh3 = 'calc(%s + 7px)' % var(--fs-content-base)
22
+ $fsh4 = 'calc(%s + 4px)' % var(--fs-content-base)
23
+ $fsh5 = 'calc(%s + 2px)' % var(--fs-content-base)
25
24
 
26
- $fsp0 = 'calc(%s - 0px)' % $fs-body
27
- $fsp1 = 'calc(%s - 1px)' % $fs-body
28
- $fsp2 = 'calc(%s - 2px)' % $fs-body
29
- $fsp3 = 'calc(%s - 3px)' % $fs-body
25
+ $fs-content-0 = 'calc(%s - 0px)' % var(--fs-content-base)
26
+ $fs-content-1 = 'calc(%s - 1px)' % var(--fs-content-base)
27
+ $fs-content-2 = 'calc(%s - 2px)' % var(--fs-content-base)
28
+ $fs-content-3 = 'calc(%s - 3px)' % var(--fs-content-base)
30
29
 
31
30
  // site main
32
31
  $site-background-image = hexo-config('style.site.background-image')
@@ -56,11 +55,13 @@ $corner-shape = convert(hexo-config('style.corner-shape'))
56
55
 
57
56
  // 可以动态变化的属性
58
57
  :root
58
+ --fs-root: $fs-root
59
+ --fs-content-base: var(--fs-root)
60
+ --fs-content: var(--fs-content-base)
59
61
  --width-main: 720px
60
- --fsp: $fs-body
61
- --fsh2: 'calc(%s + 11px)' % var(--fsp)
62
- --fsh3: 'calc(%s + 7px)' % var(--fsp)
63
- --fsh4: 'calc(%s + 4px)' % var(--fsp)
62
+ --fsh2: 'calc(%s + 11px)' % var(--fs-content)
63
+ --fsh3: 'calc(%s + 7px)' % var(--fs-content)
64
+ --fsh4: 'calc(%s + 4px)' % var(--fs-content)
64
65
 
65
66
  // 侧边栏文字内容宽度
66
67
  --side-content-width: 224px
@@ -73,8 +74,8 @@ $corner-shape = convert(hexo-config('style.corner-shape'))
73
74
  // 右栏宽度增量(在 --width-sidebar 基础上加宽)
74
75
  --rightbar-width-extra: calc(var(--gap-base) * 2)
75
76
 
76
- --gap-p: 'calc(%s + 4px)' % $fs-body // gap for paragraph
77
- --gap-p-compact: 'calc(%s * 0.75)' % $fs-body
77
+ --gap-p: 'calc(%s + 4px)' % var(--fs-content-base) // gap for paragraph
78
+ --gap-p-compact: 'calc(%s * 0.75)' % var(--fs-content-base)
78
79
  // desktop 2k or larger
79
80
  @media screen and (min-width: $device-2k)
80
81
  --width-main: 780px
@@ -85,6 +86,7 @@ $corner-shape = convert(hexo-config('style.corner-shape'))
85
86
  @media screen and (max-width: $device-tablet)
86
87
  --side-content-width: 188px
87
88
  @media screen and (max-width: $device-mobile-max)
89
+ --fs-root: 'calc(%s + 2px)' % $fs-root
88
90
  --side-content-width: 224px
89
91
 
90
92
  // shadow
@@ -18,18 +18,21 @@ img:not([src])
18
18
  min-height: 4rem
19
19
 
20
20
  // 加载占位动画
21
- img.lazy.loading + .lazy-icon
22
- position: absolute
21
+ .lazy-icon
23
22
  width: $loadingImageSize
24
23
  height: $loadingImageSize
25
24
  max-width: 100%
26
25
  max-height: 100%
26
+ // 蒙版上色:SVG 作为 mask(只取形状),颜色由 background-color 提供,
27
+ // 避免 currentColor 在 SVG-as-image 场景(Safari)退化为黑色
28
+ background-color: var(--theme)
29
+ svg-mask-icon(var(--icon-loading, url("https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd")))
30
+
31
+ img.lazy.loading + .lazy-icon
32
+ position: absolute
27
33
  top: 50%
28
34
  left: 50%
29
35
  transform: translateX(-50%) translateY(-50%)
30
- background-size: contain
31
- background-repeat: no-repeat
32
- background-position: center
33
36
 
34
37
  // 加载完成
35
38
  img.lazy.loaded + .lazy-icon,
@@ -2,5 +2,5 @@
2
2
  margin: 0 -4px
3
3
  width: 'calc(100% + %s)' % 8px
4
4
 
5
- .related-wrap#comments .cmt-body svg.loading
6
- top: 62px
5
+ .related-wrap#comments .cmt-body .lazy-icon
6
+ top: 62px
@@ -1,3 +1,7 @@
1
+ @import '../_defines/const'
2
+ @import '../_custom'
3
+ @import '../_defines/func'
4
+
1
5
  .cmt-body
2
6
  .wl-panel
3
7
  margin: 0.5em 0 !important
@@ -66,3 +70,23 @@
66
70
  :root:not([data-theme]) &
67
71
  @media (prefers-color-scheme: dark)
68
72
  ondark()
73
+
74
+ // override
75
+ .md-text .cmt-body.waline
76
+ p
77
+ font-size: var(--waline-font-size)
78
+ line-height: 1.5
79
+ margin: 0
80
+ .wl-reply-to *
81
+ line-height: 1.5
82
+ .wl-header label, .wl-input
83
+ font-size: 0.875em
84
+ .wl-editor
85
+ font-size: var(--waline-font-size)
86
+ font-family: $ff-body
87
+ .wl-content
88
+ .wl-emoji, .vemoji
89
+ height: 2.5em
90
+ img:not(.wl-emoji):not(.vemoji)
91
+ margin-top: 0.5em
92
+ margin-bottom: 0.5em
@@ -7,17 +7,36 @@ pre.mermaid
7
7
  justify-content: center;
8
8
  font-family: $ff-body
9
9
 
10
- .mermaid span,text
10
+ .mermaid span,
11
+ .mermaid text
11
12
  font-family: $ff-body
12
13
 
13
14
  .mermaid .edgeLabel
14
15
  padding: 4px
15
16
  background-color: var(--background) !important
17
+ color: var(--text-p1) !important
18
+
19
+ .mermaid .edgeLabel text,
20
+ .mermaid .label text,
21
+ .mermaid .nodeLabel,
22
+ .mermaid .cluster-label text,
23
+ .mermaid .cluster-label span
24
+ fill: var(--text-p1) !important
25
+ color: var(--text-p1) !important
16
26
 
17
27
  .mermaid rect.basic.label-container
18
28
  stroke: var(--block-border) !important
19
29
  fill: var(--block) !important
20
30
 
31
+ .mermaid .node rect,
32
+ .mermaid .node circle,
33
+ .mermaid .node ellipse,
34
+ .mermaid .node polygon,
35
+ .mermaid .node path,
36
+ .mermaid .cluster rect
37
+ stroke: var(--block-border) !important
38
+ fill: var(--block) !important
39
+
21
40
  _dark_mermaid()
22
41
  .mermaid
23
42
  fill: #ccc !important
@@ -42,21 +61,21 @@ _dark_mermaid()
42
61
 
43
62
 
44
63
  .mermaid .label
45
- color: #ccc !important
64
+ color: var(--text-p1) !important
46
65
 
47
66
 
48
67
  .mermaid .cluster-label text
49
- fill: #F9FFFE !important
68
+ fill: var(--text-p1) !important
50
69
 
51
70
 
52
71
  .mermaid .cluster-label span
53
- color: #F9FFFE !important
72
+ color: var(--text-p1) !important
54
73
 
55
74
 
56
75
  .mermaid .label text,
57
76
  .mermaid span
58
- fill: #ccc !important
59
- color: #ccc !important
77
+ fill: var(--text-p1) !important
78
+ color: var(--text-p1) !important
60
79
 
61
80
 
62
81
  .mermaid .node rect,
@@ -64,8 +83,8 @@ _dark_mermaid()
64
83
  .mermaid .node ellipse,
65
84
  .mermaid .node polygon,
66
85
  .mermaid .node path
67
- fill: #1f2020 !important
68
- stroke: #81B1DB !important
86
+ fill: var(--block) !important
87
+ stroke: var(--block-border) !important
69
88
 
70
89
 
71
90
  .mermaid .arrowheadPath
@@ -90,16 +109,16 @@ _dark_mermaid()
90
109
 
91
110
 
92
111
  .mermaid .cluster rect
93
- fill: hsl(180, 1.5873015873%, 28.3529411765%) !important
94
- stroke: rgba(255, 255, 255, 0.25) !important
112
+ fill: var(--block) !important
113
+ stroke: var(--block-border) !important
95
114
 
96
115
 
97
116
  .mermaid .cluster text
98
- fill: #F9FFFE !important
117
+ fill: var(--text-p1) !important
99
118
 
100
119
 
101
120
  .mermaid .cluster span
102
- color: #F9FFFE !important
121
+ color: var(--text-p1) !important
103
122
 
104
123
 
105
124
  .mermaid div.mermaidTooltip
@@ -4,7 +4,7 @@
4
4
  * 服务端 icon() 对非首屏 SVG 图标输出 <svg class="icon" data-icon="ns:key"> 占位符,
5
5
  * 本脚本按命名空间拉取构建期生成的 js/icons/{ns}.json,成功后原位替换为完整内联 SVG,
6
6
  * 最终 DOM 与全量内联一致,现有 CSS 钩子(搜索三态、菜单渐变、chat 着色等)不受影响。
7
- * 首屏关键图标(搜索、菜单、leftbar/rightbar、goback)由模板内联,不经过本脚本。
7
+ * 首屏关键图标(搜索、菜单、leftbar/rightbar、arrow-left)由模板内联,不经过本脚本。
8
8
  */
9
9
  (function () {
10
10
  'use strict';
package/source/js/main.js CHANGED
@@ -177,7 +177,7 @@ function rebuildToc(scope) {
177
177
  return;
178
178
  }
179
179
  const ol = document.createElement('ol');
180
- ol.className = 'toc';
180
+ ol.className = 'toc ui-collection-adapter';
181
181
  const stack = [];
182
182
  headings.forEach(function (h) {
183
183
  const id = h.id;
@@ -372,6 +372,56 @@ const init = {
372
372
  }
373
373
  });
374
374
  },
375
+ wikiCover: () => {
376
+ document.querySelectorAll('.wiki-cover-terminal').forEach(function (terminal) {
377
+ const code = terminal.querySelector('pre code');
378
+ const tabs = terminal.querySelectorAll('[role="tab"]');
379
+ function renderCodes(value) {
380
+ const lines = value.split(/\r?\n/).filter(function (line) {
381
+ return line.trim().length > 0;
382
+ });
383
+ terminal.dataset.codes = lines.join('\n');
384
+ code.innerHTML = '';
385
+ lines.forEach(function (line) {
386
+ const row = document.createElement('span');
387
+ row.textContent = line;
388
+ code.appendChild(row);
389
+ });
390
+ }
391
+ if (tabs.length > 0) {
392
+ renderCodes(tabs[0].getAttribute('data-codes') || '');
393
+ }
394
+ tabs.forEach(function (tab) {
395
+ tab.addEventListener('click', function () {
396
+ tabs.forEach(function (item) {
397
+ const active = item === tab;
398
+ item.classList.toggle('active', active);
399
+ item.setAttribute('aria-selected', active ? 'true' : 'false');
400
+ });
401
+ renderCodes(tab.getAttribute('data-codes') || '');
402
+ });
403
+ });
404
+ const copy = terminal.querySelector('.wiki-cover-copy');
405
+ if (copy) {
406
+ copy.addEventListener('click', function () {
407
+ const value = terminal.dataset.codes || '';
408
+ if (!value || !navigator.clipboard) return;
409
+ navigator.clipboard.writeText(value).then(function () {
410
+ hud.toast(copy.getAttribute('data-copy-message') || 'Copied', 2500);
411
+ }).catch(function () {});
412
+ });
413
+ }
414
+ });
415
+
416
+ const galaxyCanvases = document.querySelectorAll('.wiki-cover-background.galaxy canvas');
417
+ const reduceMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
418
+ if (reduceMotion || galaxyCanvases.length === 0) return;
419
+ utils.js('/js/plugins/galaxy.js').then(function () {
420
+ if (stellar.galaxy && typeof stellar.galaxy.mountAll === 'function') {
421
+ stellar.galaxy.mountAll(galaxyCanvases);
422
+ }
423
+ }).catch(function () {});
424
+ },
375
425
  leftbarScroll: () => {
376
426
  const container = document.querySelector('.l_left .widgets');
377
427
  if (container == null) {
@@ -380,7 +430,7 @@ const init = {
380
430
  const PREFIX = 'Stellar.leftbarScroll.';
381
431
  const encode = (s) => encodeURIComponent(String(s || ''));
382
432
  function scope() {
383
- const wikiEl = document.querySelector('.doc-tree[data-wiki]');
433
+ const wikiEl = document.querySelector('.doc-tree-widget[data-wiki]');
384
434
  if (wikiEl != null) {
385
435
  return 'wiki:' + encode(wikiEl.getAttribute('data-wiki'));
386
436
  }
@@ -409,7 +459,7 @@ const init = {
409
459
  return;
410
460
  }
411
461
  container.scrollTop = parseInt(value, 10) || 0;
412
- const link = container.querySelector('a.link.active');
462
+ const link = container.querySelector('.ui-collection__item.is-active');
413
463
  if (link == null) {
414
464
  return;
415
465
  }
@@ -426,22 +476,27 @@ const init = {
426
476
  } catch (e) {}
427
477
  },
428
478
  navbarPin: () => {
429
- // 列表页 navbar top 背景条状态切换:未吸顶为卡片样式(var(--card) + 文章卡片同款阴影),
430
- // 吸顶后恢复玻璃效果。在吸顶边界切换 .pinned 类,视觉由 CSS 控制。
479
+ // 列表页 navbar top 背景条状态切换:未滚动/未吸顶为卡片样式(var(--card) + 文章卡片同款阴影),
480
+ // 页面滚动达到阈值且吸顶后恢复玻璃效果。在吸顶边界切换 .pinned 类,视觉由 CSS 控制。
431
481
  // 吸顶判定直接测 navbar 的实际视口位置,而非用 scrollY 推算:
432
482
  // 移动端浏览器顶栏伸缩会改变 scrollY(展开顶栏时 scrollY 减小),
433
483
  // 即使 navbar 仍吸顶也可能跌破阈值,导致玻璃效果误消失。
484
+ // 无轮播区页面(如 wiki)的 navbar 在页面顶部即已吸顶,需额外要求页面实际滚动达到阈值,
485
+ // 否则默认保持卡片样式;回到顶部(滚动小于阈值)恢复卡片。
434
486
  const navbars = document.querySelectorAll('.navbar.top');
435
487
  if (navbars.length === 0) {
436
488
  return;
437
489
  }
438
490
  // 视口顶部允许的偏差(px),吸收亚像素/取整误差
439
491
  const TOLERANCE = 2;
492
+ // 页面滚动阈值(px):未滚动时保持卡片样式,滚动达到该值后才切换玻璃效果
493
+ const SCROLL_THRESHOLD = 2;
440
494
  let states = [];
441
495
  function update() {
496
+ const scrolled = window.scrollY >= SCROLL_THRESHOLD;
442
497
  states.forEach((state) => {
443
498
  const top = state.navbar.getBoundingClientRect().top;
444
- state.bar.classList.toggle('pinned', top <= state.stickyTop + TOLERANCE);
499
+ state.bar.classList.toggle('pinned', scrolled && top <= state.stickyTop + TOLERANCE);
445
500
  });
446
501
  }
447
502
  function measure() {
@@ -621,6 +676,7 @@ stellar.initPage = function () {
621
676
  init.toc();
622
677
  init.sidebar();
623
678
  init.wikiStart();
679
+ init.wikiCover();
624
680
  init.leftbarScroll();
625
681
  init.navbarPin();
626
682
  init.relativeDate(document.querySelectorAll('#post-meta time'));
@@ -1,7 +1,7 @@
1
1
  // 文字自适应颜色 DOM 插件
2
- // 扫描 [data-text-adaptive] 元素,解析背景来源(--cover-url → --bg-url →
2
+ // 扫描 [data-text-adaptive] 元素,解析背景来源(--adaptive-background → --cover-url → --bg-url →
3
3
  // background-image → background-color),调用 stellar.color 计算文字颜色,
4
- // 写入内联 --text-banner 与 --text-banner-theme。
4
+ // 写入内联 --text-banner 与 --text-banner-theme;Wiki 封面另写入主题色蒙版变量。
5
5
  // 属性值:
6
6
  // theme(默认) 背景图平均色 lighten/darken(两种变量同色)
7
7
  // contrast 黑白对比(两种变量同色)
@@ -19,6 +19,10 @@ function parseCssUrl(value) {
19
19
 
20
20
  function resolveBackground(el) {
21
21
  var style = window.getComputedStyle(el);
22
+ var adaptiveBackground = stellar.color.parse(style.getPropertyValue('--adaptive-background'));
23
+ if (adaptiveBackground) {
24
+ return { type: 'color', rgb: adaptiveBackground };
25
+ }
22
26
  var cover = parseCssUrl(style.getPropertyValue('--cover-url'));
23
27
  if (cover) {
24
28
  return { type: 'image', url: cover };
@@ -85,6 +89,39 @@ function setSplitTextColors(el, rgb) {
85
89
  }
86
90
  }
87
91
 
92
+ // Wiki 卡片的 Today 风格内容层使用封面平均色:保留色相,增强弱饱和度后压低明度,
93
+ // 让满不透明蒙版下的白色文字始终可读。变量写到 .wiki-card:蒙版用深色版本,
94
+ // hover 边框使用同源但提高 20 个明度点的版本。
95
+ function setWikiOverlayColor(el, rgb) {
96
+ var node = el.parentElement;
97
+ while (node && node.nodeType === 1) {
98
+ if ((' ' + node.className + ' ').indexOf(' wiki-card ') !== -1) {
99
+ var themed = stellar.color.enhanceSaturation(rgb, { minSaturation: 0.45, boostBelow: 0.35 });
100
+ var overlay = stellar.color.darken(themed, 0.26);
101
+ var border = stellar.color.lighten(themed, 0.46);
102
+ if (overlay) {
103
+ node.style.setProperty('--wiki-overlay-color', overlay);
104
+ }
105
+ if (border) {
106
+ node.style.setProperty('--wiki-border-color', border);
107
+ }
108
+ return;
109
+ }
110
+ node = node.parentElement;
111
+ }
112
+ }
113
+
114
+ // Wiki 卡片主题色确定后(包括提取失败时的 CSS 回退)通知索引页。
115
+ // 覆盖层会同时等待原图 load,避免先用默认主题色绘制、再切换为平均色。
116
+ function notifyWikiOverlayReady(el) {
117
+ if (!el || !el.dispatchEvent || !document.createEvent) {
118
+ return;
119
+ }
120
+ var event = document.createEvent('Event');
121
+ event.initEvent('wiki-overlay-ready', true, false);
122
+ el.dispatchEvent(event);
123
+ }
124
+
88
125
  function applyToElement(el) {
89
126
  // 用户显式覆盖优先:首次处理时元素已有内联 --text-banner 或内联 color 则跳过
90
127
  // (插件自身写入的变量不视为用户覆盖,主题切换重算时需重新应用)
@@ -103,6 +140,10 @@ function applyToElement(el) {
103
140
  } else {
104
141
  setTextColor(el, styleName, rgb);
105
142
  }
143
+ if (styleName === 'split' && el.parentElement && (' ' + el.parentElement.className + ' ').indexOf(' wiki-card-cover ') !== -1) {
144
+ setWikiOverlayColor(el, rgb);
145
+ notifyWikiOverlayReady(el);
146
+ }
106
147
  appliedElements.add(el);
107
148
  };
108
149
  if (bg.type === 'color') {
@@ -112,6 +153,9 @@ function applyToElement(el) {
112
153
  stellar.color.getAverageColor(bg.url, { background: backdrop }).then(function (rgb) {
113
154
  if (rgb) {
114
155
  apply(rgb);
156
+ } else if (styleName === 'split' && el.parentElement && (' ' + el.parentElement.className + ' ').indexOf(' wiki-card-cover ') !== -1) {
157
+ // 平均色提取失败时不写变量,保留 CSS 回退色;但仍须解除 Wiki 覆盖层等待。
158
+ notifyWikiOverlayReady(el);
115
159
  }
116
160
  });
117
161
  }