hexo-theme-stellar 1.40.0 → 1.41.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 (106) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/_config.yml +33 -10
  3. package/_data/icons.yml +250 -37
  4. package/languages/en.yml +6 -0
  5. package/languages/zh-CN.yml +6 -0
  6. package/languages/zh-TW.yml +6 -0
  7. package/layout/_partial/comments/artalk/script.ejs +25 -8
  8. package/layout/_partial/comments/beaudar/script.ejs +1 -0
  9. package/layout/_partial/comments/twikoo/script.ejs +1 -0
  10. package/layout/_partial/comments/utterances/script.ejs +1 -0
  11. package/layout/_partial/comments/waline/script.ejs +1 -0
  12. package/layout/_partial/head.ejs +43 -7
  13. package/layout/_partial/main/article/article_tags.ejs +16 -0
  14. package/layout/_partial/main/navbar/article_banner.ejs +11 -1
  15. package/layout/_partial/main/notebook/note_card.ejs +1 -1
  16. package/layout/_partial/main/notebook/paginator.ejs +2 -2
  17. package/layout/_partial/main/pin_slider.ejs +2 -2
  18. package/layout/_partial/main/post_list/paginator.ejs +2 -2
  19. package/layout/_partial/menubtn.ejs +2 -2
  20. package/layout/_partial/scripts/defines.ejs +26 -2
  21. package/layout/_partial/scripts.ejs +30 -7
  22. package/layout/_partial/sidebar/logo.ejs +1 -1
  23. package/layout/_partial/sidebar/menu.ejs +1 -1
  24. package/layout/_partial/sidebar/search.ejs +1 -1
  25. package/layout/_partial/widgets/author.ejs +1 -1
  26. package/layout/_partial/widgets/components/link.ejs +2 -2
  27. package/layout/_partial/widgets/ghuser.ejs +1 -1
  28. package/layout/_partial/widgets/related.ejs +1 -1
  29. package/layout/_partial/widgets/toc.ejs +1 -1
  30. package/layout/_partial/widgets/tree.ejs +1 -1
  31. package/layout/_plugins/fancybox.ejs +4 -2
  32. package/layout/_plugins/mermaid.ejs +2 -1
  33. package/layout/_plugins/search/local_search.ejs +4 -3
  34. package/layout/_plugins/swiper.ejs +1 -0
  35. package/layout/categories.ejs +1 -1
  36. package/layout/page.ejs +3 -0
  37. package/layout/tags.ejs +1 -0
  38. package/package.json +1 -1
  39. package/scripts/events/lib/doc_tree.js +12 -208
  40. package/scripts/events/lib/notebooks.js +8 -148
  41. package/scripts/events/lib/utils.js +10 -1
  42. package/scripts/filters/index.js +1 -0
  43. package/scripts/filters/lib/img_lazyload.js +4 -0
  44. package/scripts/filters/lib/img_onerror.js +4 -0
  45. package/scripts/filters/lib/md_table.js +46 -0
  46. package/scripts/generators/search.js +12 -18
  47. package/scripts/generators/stellar-icons.js +47 -0
  48. package/scripts/helpers/ai_label.js +29 -0
  49. package/scripts/helpers/icon.js +2 -2
  50. package/scripts/helpers/json_ld.js +10 -13
  51. package/scripts/helpers/related_posts.js +0 -7
  52. package/scripts/helpers/seo.js +9 -0
  53. package/scripts/lib/ai_label.js +55 -0
  54. package/scripts/lib/doc_tree.js +267 -0
  55. package/scripts/lib/notebooks.js +181 -0
  56. package/scripts/lib/search_index.js +82 -0
  57. package/scripts/lib/seo.js +79 -0
  58. package/scripts/tags/lib/about.js +2 -2
  59. package/scripts/tags/lib/albums.js +1 -1
  60. package/scripts/tags/lib/banner.js +1 -1
  61. package/scripts/tags/lib/chat.js +49 -49
  62. package/scripts/tags/lib/copy.js +1 -1
  63. package/scripts/tags/lib/friends.js +1 -1
  64. package/scripts/tags/lib/gallery.js +1 -1
  65. package/scripts/tags/lib/hashtag.js +1 -1
  66. package/scripts/tags/lib/image.js +3 -3
  67. package/scripts/tags/lib/posters.js +1 -1
  68. package/scripts/tags/lib/sites.js +2 -2
  69. package/source/css/_common/base.styl +5 -9
  70. package/source/css/_common/device.styl +3 -3
  71. package/source/css/_components/md-table.styl +23 -0
  72. package/source/css/_components/pages/archives.styl +2 -22
  73. package/source/css/_components/pages/article-story.styl +4 -4
  74. package/source/css/_components/partial/article-tags.styl +10 -0
  75. package/source/css/_components/partial/bread-nav.styl +14 -0
  76. package/source/css/_components/partial/navbar.styl +10 -2
  77. package/source/css/_components/sidebar/search.styl +24 -4
  78. package/source/css/_components/tag-plugins/table.styl +16 -11
  79. package/source/css/_components/widgets/components.styl +6 -0
  80. package/source/css/_components/widgets/list.styl +6 -0
  81. package/source/css/_components/widgets/toc.styl +1 -1
  82. package/source/css/_custom.styl +1 -12
  83. package/source/css/_defines/func.styl +28 -3
  84. package/source/css/_plugins/index.styl +0 -18
  85. package/source/css/{_plugins/comments → comments}/artalk.styl +4 -0
  86. package/source/css/{_plugins/comments → comments}/twikoo.styl +4 -0
  87. package/source/css/{_plugins → plugins}/mermaid.styl +3 -0
  88. package/source/css/{_plugins → plugins}/swiper.styl +5 -1
  89. package/source/js/icons.js +60 -0
  90. package/source/js/main.js +114 -0
  91. package/source/js/search/highlight.js +82 -0
  92. package/source/js/search/local-search.js +294 -103
  93. package/source/js/services/timeline.js +1 -1
  94. package/source/js/services/weibo.js +3 -3
  95. package/source/js/services.js +179 -0
  96. package/source/js/tagtree.js +29 -0
  97. package/source/js/theme.js +61 -0
  98. package/{layout/_partial/scripts/utils.ejs → source/js/utils.js} +13 -10
  99. package/layout/_partial/scripts/services.ejs +0 -178
  100. package/layout/_partial/scripts/sidebar.ejs +0 -25
  101. package/layout/_partial/scripts/tagtree.ejs +0 -29
  102. package/layout/_partial/scripts/theme.ejs +0 -67
  103. /package/source/css/{_plugins/comments → comments}/beaudar.styl +0 -0
  104. /package/source/css/{_plugins/comments → comments}/utterances.styl +0 -0
  105. /package/source/css/{_plugins/comments → comments}/waline.styl +0 -0
  106. /package/source/css/{_plugins → plugins}/fancybox.styl +0 -0
@@ -0,0 +1,29 @@
1
+ (() => {
2
+ const tagtreeConf = window.__STELLAR_TAGTREE__ || {};
3
+
4
+ const tagSwitchers = document.querySelectorAll('.tag-subtree.parent-tag > a > .tag-switcher-wrapper')
5
+ for (const tagSwitcher of tagSwitchers) {
6
+ tagSwitcher.addEventListener('click', (e) => {
7
+ const parent = e.target.closest('.tag-subtree.parent-tag')
8
+ parent.classList.toggle('expanded')
9
+ e.preventDefault()
10
+ })
11
+ }
12
+
13
+ // Get active tag from query string, then activate it.
14
+ const urlParams = new URLSearchParams(window.location.search)
15
+ const activeTag = urlParams.get('tag')
16
+ if (activeTag) {
17
+ let tag = document.querySelector(`.tag-subtree[data-tag="${activeTag}"]`)
18
+ if (tag) {
19
+ tag.querySelector('a').classList.add('active')
20
+ if (!tagtreeConf.expand_active) {
21
+ tag = tag.parentElement.closest('.tag-subtree.parent-tag')
22
+ }
23
+ while (tag) {
24
+ tag.classList.add('expanded')
25
+ tag = tag.parentElement.closest('.tag-subtree.parent-tag')
26
+ }
27
+ }
28
+ }
29
+ })()
@@ -0,0 +1,61 @@
1
+ const applyTheme = (theme) => {
2
+ if (theme === 'auto') {
3
+ document.documentElement.removeAttribute('data-theme')
4
+ } else {
5
+ document.documentElement.setAttribute('data-theme', theme)
6
+ }
7
+
8
+ // applyThemeToGiscus(theme)
9
+ }
10
+
11
+ // FIXME: 这会导致无法使用 preferred_color_scheme 以外的主题
12
+ const applyThemeToGiscus = (theme) => {
13
+ // theme = theme === 'auto' ? 'preferred_color_scheme' : theme
14
+ const cmt = document.getElementById('giscus')
15
+ if (cmt) {
16
+ // This works before giscus load.
17
+ cmt.setAttribute('data-theme', theme)
18
+ }
19
+
20
+ const iframe = document.querySelector('#comments > section.giscus > iframe')
21
+ if (iframe) {
22
+ // This works after giscus loaded.
23
+ const src = iframe.src
24
+ const newSrc = src.replace(/theme=[\w]+/, `theme=${theme}`)
25
+ iframe.src = newSrc
26
+ }
27
+ }
28
+
29
+ const switchTheme = () => {
30
+ // light -> dark -> auto -> light -> ...
31
+ const currentTheme = document.documentElement.getAttribute('data-theme')
32
+ let newTheme;
33
+ switch (currentTheme) {
34
+ case 'light':
35
+ newTheme = 'dark'
36
+ break
37
+ case 'dark':
38
+ newTheme = 'auto'
39
+ break
40
+ default:
41
+ newTheme = 'light'
42
+ }
43
+ applyTheme(newTheme)
44
+ window.localStorage.setItem('Stellar.theme', newTheme)
45
+ utils.dark.mode = newTheme === 'auto' ? (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light") : newTheme;
46
+ utils.dark.method.toggle.start();
47
+
48
+ const messages = window.__STELLAR_I18N__ || {};
49
+ hud?.toast?.(messages[newTheme])
50
+ }
51
+
52
+ (() => {
53
+ // Apply user's preferred theme, if any.
54
+ const theme = window.localStorage.getItem('Stellar.theme')
55
+ if (theme !== null) {
56
+ applyTheme(theme)
57
+ } else {
58
+ utils.dark.mode = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
59
+ }
60
+ utils.dark.method.toggle.start();
61
+ })()
@@ -1,4 +1,3 @@
1
- <script type="text/javascript">
2
1
 
3
2
  function RunItem() {
4
3
  this.list = []; // 存放回调函数
@@ -63,7 +62,7 @@
63
62
  var sheets = doc.styleSheets;
64
63
  if (attributes) {
65
64
  for (var attributeName in attributes) {
66
- if (attributes.hasOwnProperty(attributeName)) {
65
+ if (Object.prototype.hasOwnProperty.call(attributes, attributeName)) {
67
66
  ss.setAttribute(attributeName, attributes[attributeName]);
68
67
  }
69
68
  }
@@ -296,7 +295,7 @@
296
295
  onLoading: (el) => {
297
296
  if (el) {
298
297
  if (el.querySelector('.loading-wrap') === null) {
299
- el.insertAdjacentHTML('beforeend', '<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>');
298
+ el.insertAdjacentHTML('beforeend', `<div class="loading-wrap">${ctx.icons['default:loading-spinner']}</div>`);
300
299
  }
301
300
  }
302
301
  },
@@ -312,7 +311,7 @@
312
311
  if (wrap) {
313
312
  var svg = wrap.querySelector('svg');
314
313
  if (svg) svg.remove();
315
- wrap.insertAdjacentHTML('beforeend', '<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-linejoin="round" stroke-width="2"><path stroke-dasharray="60" stroke-dashoffset="60" d="M12 3L21 20H3L12 3Z"><animate fill="freeze" attributeName="stroke-dashoffset" dur="0.5s" values="60;0"/></path><path stroke-dasharray="6" stroke-dashoffset="6" d="M12 10V14"><animate fill="freeze" attributeName="stroke-dashoffset" begin="0.6s" dur="0.2s" values="6;0"/></path></g><circle cx="12" cy="17" r="1" fill="currentColor" fill-opacity="0"><animate fill="freeze" attributeName="fill-opacity" begin="0.8s" dur="0.4s" values="0;1"/></circle></svg>');
314
+ wrap.insertAdjacentHTML('beforeend', ctx.icons['default:warning']);
316
315
  wrap.classList.add('error');
317
316
  }
318
317
  }
@@ -533,11 +532,13 @@
533
532
  if (timedOut) return;
534
533
  // 标记元素实例为已加载
535
534
  if (el) utils._loadedElements.add(el);
536
- // 写入缓存(克隆响应,避免影响回调读取;延迟执行,避免阻塞渲染)
535
+ // 写入缓存:回调会立即消费 data 的 body,必须先同步 clone 再延迟读取
537
536
  if (cacheable && data.ok) {
537
+ const cacheClone = data.clone();
538
+ const contentType = data.headers.get('Content-Type') || 'application/json';
538
539
  utils._defer(() => {
539
- data.clone().text().then(text => {
540
- utils.cache.set(url, text, data.headers.get('Content-Type') || 'application/json', ttl);
540
+ cacheClone.text().then(text => {
541
+ utils.cache.set(url, text, contentType, ttl);
541
542
  }).catch(() => {});
542
543
  });
543
544
  }
@@ -609,9 +610,12 @@
609
610
  clearTimeout(timer);
610
611
  if (timedOut) return;
611
612
  if (cacheable) {
613
+ // 调用方会消费 resp 的 body,必须先同步 clone 再延迟读取
614
+ const cacheClone = resp.clone();
615
+ const contentType = resp.headers.get('Content-Type') || 'application/json';
612
616
  utils._defer(() => {
613
- resp.clone().text().then(text => {
614
- utils.cache.set(url, text, resp.headers.get('Content-Type') || 'application/json', ttl);
617
+ cacheClone.text().then(text => {
618
+ utils.cache.set(url, text, contentType, ttl);
615
619
  }).catch(() => {});
616
620
  });
617
621
  }
@@ -722,4 +726,3 @@
722
726
  utils.dark = Object.assign(utils.dark, {
723
727
  push: utils.dark.method.toggle.push,
724
728
  });
725
- </script>
@@ -1,178 +0,0 @@
1
- <script defer>
2
- const initServices = () => {
3
- // 用于存储需要清理的资源
4
- let timers = [];
5
-
6
- ctx.services = Object.assign({}, JSON.parse(`<%- JSON.stringify(theme.data_services) %>`));
7
- for (let id of Object.keys(ctx.services)) {
8
- const js = ctx.services[id].js;
9
- if (id == 'siteinfo') {
10
- const cardlinks = document.querySelectorAll('a.link-card[cardlink]');
11
- if (cardlinks?.length > 0) {
12
- utils.js(js, { defer: true }).then(function () {
13
- setCardLink(cardlinks);
14
- });
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
- }
21
- } else if (id == 'voice') {
22
- const voiceAudios = document.querySelectorAll('.voice>audio');
23
- if (voiceAudios?.length > 0) {
24
- utils.js(js, { defer: true }).then(function () {
25
- createVoiceDom(voiceAudios);
26
- });
27
- }
28
- } else if (id == 'video') {
29
- const videos = document.querySelectorAll('.video>video');
30
- if (videos?.length > 0) {
31
- utils.js(js, { defer: true }).then(function () {
32
- videoEvents(videos);
33
- });
34
- }
35
- } else if (id == 'download-file') {
36
- const files = document.querySelectorAll('.chat-file');
37
- if (files?.length > 0) {
38
- utils.js(js, { defer: true }).then(function () {
39
- downloadFileEvent(files);
40
- });
41
- }
42
- } else {
43
- const els = document.getElementsByClassName(`ds-${id}`);
44
- if (els?.length > 0) {
45
- if (id == 'timeline' || id == 'memos' || id == 'marked' || id == 'mdrender') {
46
- utils.js(deps.marked).then(function () {
47
- utils.js(js, { defer: true });
48
- });
49
- } else {
50
- utils.js(js, { defer: true });
51
- }
52
- }
53
- }
54
- }
55
-
56
- // search
57
- if (ctx.search && typeof searchFunc === 'function') {
58
- const inputArea = document.querySelector("input#search-input");
59
- if (inputArea && !inputArea._searchInitialized) {
60
- const path = ctx.search.path.startsWith('/') ? ctx.root + ctx.search.path.substring(1) : ctx.root + ctx.search.path;
61
- const filter = inputArea.getAttribute('data-filter') || '';
62
- searchFunc(path, filter, 'search-wrapper', 'search-input', 'search-result');
63
- // 标记为已初始化,防止重复绑定
64
- inputArea._searchInitialized = true;
65
- }
66
- }
67
-
68
- // chat iphone time
69
- let phoneTimes = document.querySelectorAll('.chat .status-bar .time');
70
- let firstAdjustInterval = null;
71
- let mainInterval = null;
72
-
73
- if (phoneTimes.length > 0) {
74
- NowTime();
75
- const date = new Date();
76
- const sec = date.getSeconds();
77
- firstAdjustInterval = setInterval(firstAdjustTime, 1000 * (60 - sec));
78
- timers.push(firstAdjustInterval);
79
-
80
- function firstAdjustTime() {
81
- NowTime();
82
- if (firstAdjustInterval) {
83
- clearInterval(firstAdjustInterval);
84
- firstAdjustInterval = null;
85
- }
86
- mainInterval = setInterval(NowTime, 1000 * 60);
87
- timers.push(mainInterval);
88
- }
89
-
90
- function NowTime() {
91
- for (let i = 0; i < phoneTimes.length; ++i) {
92
- const timeSpan = phoneTimes[i];
93
- const date = new Date();
94
- const hour = date.getHours();
95
- const min = date.getMinutes();
96
- timeSpan.innerHTML = check(hour) + ":" + check(min);
97
- }
98
- };
99
-
100
- function check(val) {
101
- if (val < 10) {
102
- return ("0" + val);
103
- }
104
- return (val);
105
- }
106
- }
107
-
108
- // chat quote - 存储事件监听器以便清理
109
- const quoteClickHandlers = new Map();
110
- const chat_quote_obverser = new IntersectionObserver((entries, observer) => {
111
- 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) => {
112
- observer.unobserve(entry.target);
113
- setTimeout(() => {
114
- entry.target.classList.add('quote-blink');
115
- setTimeout(() => {
116
- entry.target.classList.remove('quote-blink');
117
- }, 1000);
118
- }, Math.max(100, 16) * (index + 1));
119
- });
120
- });
121
-
122
- var chatQuotes = document.querySelectorAll(".chat .talk .quote");
123
- chatQuotes.forEach((quote) => {
124
- const handler = function () {
125
- var chatCellDom = document.getElementById("quote-" + quote.getAttribute("quotedCellTag"));
126
- if (chatCellDom) {
127
- var chatDiv = chatCellDom.parentElement;
128
- var mid = chatDiv.clientHeight / 2;
129
- var offsetTop = chatCellDom.offsetTop;
130
- if (offsetTop > mid - chatCellDom.clientHeight / 2) {
131
- chatDiv.scrollTo({
132
- top: chatCellDom.offsetTop - mid + chatCellDom.clientHeight / 2,
133
- behavior: "smooth"
134
- });
135
- } else {
136
- chatDiv.scrollTo({
137
- top: 0,
138
- behavior: "smooth"
139
- });
140
- }
141
- chat_quote_obverser.observe(chatCellDom);
142
- }
143
- };
144
- quote.addEventListener('click', handler);
145
- quoteClickHandlers.set(quote, handler); // 保存处理器引用
146
- });
147
-
148
- // 返回清理函数,用于清理定时器和观察器
149
- return () => {
150
- // 清理所有定时器(包括可能未完成的 firstAdjustInterval)
151
- timers.forEach(timer => {
152
- if (timer) clearInterval(timer);
153
- });
154
- timers = [];
155
- if (firstAdjustInterval) {
156
- clearInterval(firstAdjustInterval);
157
- firstAdjustInterval = null;
158
- }
159
- if (mainInterval) {
160
- clearInterval(mainInterval);
161
- mainInterval = null;
162
- }
163
-
164
- // 断开观察器
165
- if (chat_quote_obverser) {
166
- chat_quote_obverser.disconnect();
167
- }
168
-
169
- // 移除所有 click 事件监听器
170
- quoteClickHandlers.forEach((handler, quote) => {
171
- quote.removeEventListener('click', handler);
172
- });
173
- quoteClickHandlers.clear();
174
- };
175
- };
176
-
177
- utils.initPlugin(initServices, 'services');
178
- </script>
@@ -1,25 +0,0 @@
1
- <script>
2
- const sidebar = {
3
- leftbar: () => {
4
- if (l_body) {
5
- l_body.toggleAttribute('leftbar');
6
- l_body.removeAttribute('rightbar');
7
- }
8
- },
9
- rightbar: () => {
10
- if (l_body) {
11
- l_body.toggleAttribute('rightbar');
12
- l_body.removeAttribute('leftbar');
13
- }
14
- },
15
- dismiss: () => {
16
- if (l_body) {
17
- l_body.removeAttribute('leftbar');
18
- l_body.removeAttribute('rightbar');
19
- }
20
- },
21
- toggleTOC: () => {
22
- document.querySelector('#data-toc').classList.toggle('collapse');
23
- }
24
- }
25
- </script>
@@ -1,29 +0,0 @@
1
- <script type="text/javascript">
2
- (() => {
3
- const tagSwitchers = document.querySelectorAll('.tag-subtree.parent-tag > a > .tag-switcher-wrapper')
4
- for (const tagSwitcher of tagSwitchers) {
5
- tagSwitcher.addEventListener('click', (e) => {
6
- const parent = e.target.closest('.tag-subtree.parent-tag')
7
- parent.classList.toggle('expanded')
8
- e.preventDefault()
9
- })
10
- }
11
-
12
- // Get active tag from query string, then activate it.
13
- const urlParams = new URLSearchParams(window.location.search)
14
- const activeTag = urlParams.get('tag')
15
- if (activeTag) {
16
- let tag = document.querySelector(`.tag-subtree[data-tag="${activeTag}"]`)
17
- if (tag) {
18
- tag.querySelector('a').classList.add('active')
19
- <% if (!theme.widgets.tagtree.expand_active) { %>
20
- tag = tag.parentElement.closest('.tag-subtree.parent-tag')
21
- <% } %>
22
- while (tag) {
23
- tag.classList.add('expanded')
24
- tag = tag.parentElement.closest('.tag-subtree.parent-tag')
25
- }
26
- }
27
- }
28
- })()
29
- </script>
@@ -1,67 +0,0 @@
1
- <script type="text/javascript">
2
- const applyTheme = (theme) => {
3
- if (theme === 'auto') {
4
- document.documentElement.removeAttribute('data-theme')
5
- } else {
6
- document.documentElement.setAttribute('data-theme', theme)
7
- }
8
-
9
- // applyThemeToGiscus(theme)
10
- }
11
-
12
- // FIXME: 这会导致无法使用 preferred_color_scheme 以外的主题
13
- const applyThemeToGiscus = (theme) => {
14
- // theme = theme === 'auto' ? 'preferred_color_scheme' : theme
15
- const cmt = document.getElementById('giscus')
16
- if (cmt) {
17
- // This works before giscus load.
18
- cmt.setAttribute('data-theme', theme)
19
- }
20
-
21
- const iframe = document.querySelector('#comments > section.giscus > iframe')
22
- if (iframe) {
23
- // This works after giscus loaded.
24
- const src = iframe.src
25
- const newSrc = src.replace(/theme=[\w]+/, `theme=${theme}`)
26
- iframe.src = newSrc
27
- }
28
- }
29
-
30
- const switchTheme = () => {
31
- // light -> dark -> auto -> light -> ...
32
- const currentTheme = document.documentElement.getAttribute('data-theme')
33
- let newTheme;
34
- switch (currentTheme) {
35
- case 'light':
36
- newTheme = 'dark'
37
- break
38
- case 'dark':
39
- newTheme = 'auto'
40
- break
41
- default:
42
- newTheme = 'light'
43
- }
44
- applyTheme(newTheme)
45
- window.localStorage.setItem('Stellar.theme', newTheme)
46
- utils.dark.mode = newTheme === 'auto' ? (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light") : newTheme;
47
- utils.dark.method.toggle.start();
48
-
49
- const messages = {
50
- light: `<%- __('message.theme_switched.light') %>`,
51
- dark: `<%- __('message.theme_switched.dark') %>`,
52
- auto: `<%- __('message.theme_switched.auto') %>`,
53
- }
54
- hud?.toast?.(messages[newTheme])
55
- }
56
-
57
- (() => {
58
- // Apply user's preferred theme, if any.
59
- const theme = window.localStorage.getItem('Stellar.theme')
60
- if (theme !== null) {
61
- applyTheme(theme)
62
- } else {
63
- utils.dark.mode = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
64
- }
65
- utils.dark.method.toggle.start();
66
- })()
67
- </script>
File without changes