hexo-theme-stellar 1.29.0 → 1.30.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 (48) hide show
  1. package/_config.yml +29 -9
  2. package/_data/chat_users.yml +0 -0
  3. package/_data/widgets.yml +7 -0
  4. package/languages/zh-TW.yml +2 -2
  5. package/layout/_partial/head.ejs +2 -1
  6. package/layout/_partial/main/navbar/nav_tabs_blog.ejs +10 -10
  7. package/layout/_partial/scripts/defines.ejs +3 -0
  8. package/layout/_partial/scripts/services.ejs +91 -0
  9. package/layout/_partial/scripts/theme.ejs +5 -0
  10. package/layout/_partial/scripts/utils.ejs +67 -7
  11. package/layout/_partial/scripts.ejs +1 -1
  12. package/layout/_plugins/mermaid.ejs +1 -1
  13. package/layout/_plugins/scrollreveal.ejs +1 -0
  14. package/layout/_plugins/search/algolia_search.ejs +11 -12
  15. package/layout/layout.ejs +1 -1
  16. package/package.json +1 -1
  17. package/scripts/events/lib/config.js +3 -0
  18. package/scripts/events/lib/doc_tree.js +1 -6
  19. package/scripts/filters/index.js +26 -0
  20. package/scripts/generators/notebooks.js +1 -1
  21. package/scripts/generators/search.js +21 -2
  22. package/scripts/helpers/dynamic_color.js +138 -0
  23. package/scripts/tags/index.js +1 -0
  24. package/scripts/tags/lib/box.js +1 -1
  25. package/scripts/tags/lib/chat.js +515 -0
  26. package/scripts/tags/lib/folders.js +1 -1
  27. package/scripts/tags/lib/folding.js +2 -2
  28. package/scripts/tags/lib/sites.js +1 -1
  29. package/scripts/tags/lib/video.js +7 -2
  30. package/source/css/_components/tag-plugins/chat.styl +621 -0
  31. package/source/css/_components/tag-plugins/friends.styl +10 -0
  32. package/source/css/_components/tag-plugins/sites.styl +12 -0
  33. package/source/css/_components/tag-plugins/tabs.styl +5 -2
  34. package/source/css/_custom.styl +1 -0
  35. package/source/css/_defines/func.styl +7 -6
  36. package/source/css/_defines/theme.styl +38 -0
  37. package/source/css/_plugins/katex.styl +0 -1
  38. package/source/js/plugins/download-file.js +198 -0
  39. package/source/js/plugins/video.js +65 -0
  40. package/source/js/plugins/voice.js +438 -0
  41. package/source/js/search/algolia-search.js +63 -61
  42. package/source/js/services/artalk_latest_comment.js +33 -0
  43. package/source/js/services/friends.js +4 -0
  44. package/source/js/services/giscus_latest_comment.js +36 -0
  45. package/source/js/services/memos.js +91 -69
  46. package/source/js/services/sites.js +4 -0
  47. package/source/js/services/twikoo_latest_comment.js +47 -0
  48. package/source/js/services/waline_latest_comment.js +32 -0
package/_config.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  ######## Stellar info ########
2
2
  stellar:
3
- version: '1.29.0'
3
+ version: '1.30.0'
4
4
  homepage: 'https://xaoxuu.com/wiki/stellar/'
5
5
  repo: 'https://github.com/xaoxuu/hexo-theme-stellar'
6
6
  main_css: /css/main.css
@@ -10,8 +10,8 @@ stellar:
10
10
  ######## head tags ########
11
11
  preconnect:
12
12
  # - https://gcore.jsdelivr.net
13
+ # - https://cdn.jsdelivr.net
13
14
  # - https://unpkg.com
14
- # - https://cdn.bootcdn.net
15
15
 
16
16
  open_graph:
17
17
  enable: true
@@ -197,10 +197,12 @@ search:
197
197
  field: all # post, page, all
198
198
  path: /search.json # 搜索文件存放位置
199
199
  content: true # 是否搜索内容
200
+ skip_search: # 指定 path 中的内容不被搜索。
200
201
  algolia_search: # Docsearch https://docsearch.algolia.com/apply/ 申请
201
202
  appId:
202
203
  apiKey:
203
204
  indexName:
205
+ js: https://gcore.jsdelivr.net/algoliasearch/3/algoliasearch.min.js
204
206
 
205
207
 
206
208
  ######## Comments ########
@@ -416,12 +418,14 @@ tag_plugins:
416
418
  layout: grid # grid / flow
417
419
  size: mix # s / m / l / xl / mix
418
420
  ratio: square # origin / square
421
+ chat:
422
+ api: https://siteinfo.listentothewind.cn/api/v1
419
423
 
420
424
 
421
425
  # 基础依赖
422
426
  dependencies:
423
- jquery: https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js
424
- marked: https://cdn.bootcdn.net/ajax/libs/marked/4.0.18/marked.min.js
427
+ jquery: https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js
428
+ marked: https://cdn.jsdelivr.net/npm/marked@13.0.1/lib/marked.umd.min.js
425
429
 
426
430
 
427
431
  # 内置服务,按需加载(页面内用到了就会加载,没有用到就不会加载)
@@ -451,6 +455,22 @@ data_services:
451
455
  js: /js/services/weibo.js
452
456
  memos:
453
457
  js: /js/services/memos.js
458
+ # chat
459
+ voice:
460
+ js: /js/plugins/voice.js
461
+ video:
462
+ js: /js/plugins/video.js
463
+ download-file:
464
+ js: /js/plugins/download-file.js
465
+ # 最新评论获取类
466
+ twikoo:
467
+ js: /js/services/twikoo_latest_comment.js
468
+ waline:
469
+ js: /js/services/waline_latest_comment.js
470
+ artalk:
471
+ js: /js/services/artalk_latest_comment.js
472
+ giscus:
473
+ js: /js/services/giscus_latest_comment.js
454
474
 
455
475
 
456
476
  # 扩展插件接入方法:(插件名下面用 #plugin# 代替)
@@ -466,13 +486,13 @@ plugins:
466
486
  preload:
467
487
  enable: true
468
488
  service: flying_pages # flying_pages
469
- flying_pages: https://cdn.bootcdn.net/ajax/libs/flying-pages/2.1.2/flying-pages.min.js
489
+ flying_pages: https://cdn.jsdelivr.net/npm/flying-pages@2/flying-pages.min.js
470
490
 
471
491
  # lazyload
472
492
  # https://www.npmjs.com/package/vanilla-lazyload
473
493
  lazyload:
474
494
  enable: true # [hexo clean && hexo s] is required after changing this value.
475
- js: https://cdn.bootcdn.net/ajax/libs/vanilla-lazyload/17.8.4/lazyload.min.js
495
+ js: https://cdn.jsdelivr.net/npm/vanilla-lazyload@19.1.3/dist/lazyload.min.js
476
496
  transition: fade # blur, fade
477
497
 
478
498
  # https://fancyapps.com/docs/ui/fancybox/
@@ -480,8 +500,8 @@ plugins:
480
500
  fancybox:
481
501
  enable: true
482
502
  loader: /js/plugins/fancybox-loader.js
483
- js: https://cdn.bootcdn.net/ajax/libs/fancyapps-ui/5.0.22/fancybox/fancybox.umd.min.js
484
- css: https://cdn.bootcdn.net/ajax/libs/fancyapps-ui/5.0.22/fancybox/fancybox.min.css
503
+ js: https://cdn.jsdelivr.net/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.umd.js
504
+ css: https://cdn.jsdelivr.net/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.css
485
505
  # 让 md 语法图片支持放大可以这样写: .md-text img:not([class]), .md-text .image img
486
506
  # 可以处理评论区的图片(不支持 iframe 类评论系统)例如:
487
507
  # 使用 twikoo 评论可以写: .tk-content img:not([class*="emo"])
@@ -497,7 +517,7 @@ plugins:
497
517
  # https://scrollrevealjs.org/api/reveal.html
498
518
  scrollreveal:
499
519
  enable: #true
500
- js: https://cdn.bootcdn.net/ajax/libs/scrollReveal.js/4.0.9/scrollreveal.min.js
520
+ js: https://cdn.jsdelivr.net/npm/scrollreveal@4.0.9/dist/scrollreveal.min.js
501
521
  distance: 16px
502
522
  duration: 800 # ms
503
523
  interval: 100 # ms
File without changes
package/_data/widgets.yml CHANGED
@@ -71,6 +71,13 @@ timeline:
71
71
  type: # 默认不用写,如果是友链朋友圈数据请写 fcircle
72
72
  limit: # 默认通过 api 上增加 per_page 来设置,如果是友链朋友圈,可通过这个设置数量
73
73
 
74
+ latest_comment:
75
+ layout: timeline
76
+ title: 最新评论
77
+ api: # 参照文档获取服务对应的api
78
+ type: # 选择评论服务
79
+ limit: 16 # 限制获取数量
80
+
74
81
  tagtree:
75
82
  layout: tagtree
76
83
  expand_all: false # 是否展开所有节点
@@ -50,8 +50,8 @@ page:
50
50
  action: 返回首頁
51
51
 
52
52
  search:
53
- search: 站內搜索
54
- search_in: 在 %s 中搜索
53
+ search: 站內搜尋
54
+ search_in: 在 %s 中搜尋
55
55
  no_results: 沒有找到內容!
56
56
 
57
57
  message:
@@ -119,7 +119,8 @@ function custom_inject() {
119
119
  <%- feed_tag(config.feed.path, {title: config.title}) %>
120
120
  <% } %>
121
121
 
122
- <link rel="stylesheet" href="<%- `${theme.stellar.main_css}?v=${stellar_info('version')}` %>">
122
+ <link rel="stylesheet" href="<%- url_for(`${theme.stellar.main_css}?v=${stellar_info('version')}`) %>">
123
+
123
124
 
124
125
  <% if (config.favicon) { %>
125
126
  <%- favicon_tag(config.favicon) %>
@@ -11,37 +11,37 @@ function layoutDiv() {
11
11
 
12
12
  if (site.categories && site.categories.length > 0) {
13
13
  if (page.category) {
14
- el += '<a class="active" href="' + url_for(config.category_dir) + '">' + __("btn.category") + __("symbol.colon") + page.category + '</a>';
14
+ el += '<a class="active" href="' + url_for(config.category_dir) + '/">' + __("btn.category") + __("symbol.colon") + page.category + '</a>';
15
15
  } else if (page.layout == "categories") {
16
- el += '<a class="active" href="' + url_for(config.category_dir) + '">' + __("btn.categories") + '</a>';
16
+ el += '<a class="active" href="' + url_for(config.category_dir) + '/">' + __("btn.categories") + '</a>';
17
17
  } else {
18
- el += '<a href="' + url_for(config.category_dir) + '">' + __("btn.categories") + '</a>';
18
+ el += '<a href="' + url_for(config.category_dir) + '/">' + __("btn.categories") + '</a>';
19
19
  }
20
20
  }
21
21
 
22
22
  if (site.tags && site.tags.length > 0) {
23
23
  if (page.tag) {
24
- el += '<a class="active" href="' + url_for(config.tag_dir) + '">' + __("btn.tag") + __("symbol.colon") + page.tag + '</a>';
24
+ el += '<a class="active" href="' + url_for(config.tag_dir) + '/">' + __("btn.tag") + __("symbol.colon") + page.tag + '</a>';
25
25
  } else if (page.layout == "tags") {
26
- el += '<a class="active" href="' + url_for(config.tag_dir) + '">' + __("btn.tags") + '</a>';
26
+ el += '<a class="active" href="' + url_for(config.tag_dir) + '/">' + __("btn.tags") + '</a>';
27
27
  } else {
28
- el += '<a href="' + url_for(config.tag_dir) + '">' + __("btn.tags") + '</a>';
28
+ el += '<a href="' + url_for(config.tag_dir) + '/">' + __("btn.tags") + '</a>';
29
29
  }
30
30
  }
31
31
 
32
32
  if (theme.topic?.publish_list?.length > 0) {
33
33
  if (page.layout == 'index_topic') {
34
- el += '<a class="active" href="' + url_for(theme.site_tree.index_topic.base_dir) + '">' + __("btn.topic") + '</a>';
34
+ el += '<a class="active" href="' + url_for(theme.site_tree.index_topic.base_dir) + '/">' + __("btn.topic") + '</a>';
35
35
  } else {
36
- el += '<a href="' + url_for(theme.site_tree.index_topic.base_dir) + '">' + __("btn.topic") + '</a>';
36
+ el += '<a href="' + url_for(theme.site_tree.index_topic.base_dir) + '/">' + __("btn.topic") + '</a>';
37
37
  }
38
38
  }
39
39
 
40
40
  if (site.posts && site.posts.length > 0) {
41
41
  if (is_archive()) {
42
- el += '<a class="active" href="' + url_for(config.archive_dir) + '">' + __("btn.archives") + '</a>';
42
+ el += '<a class="active" href="' + url_for(config.archive_dir) + '/">' + __("btn.archives") + '</a>';
43
43
  } else {
44
- el += '<a href="' + url_for(config.archive_dir) + '">' + __("btn.archives") + '</a>';
44
+ el += '<a href="' + url_for(config.archive_dir) + '/">' + __("btn.archives") + '</a>';
45
45
  }
46
46
  }
47
47
 
@@ -7,6 +7,9 @@
7
7
  day: `<%- __('meta.date_suffix.day') %>`,
8
8
  },
9
9
  root : `<%- config.root %>`,
10
+ tag_plugins: {
11
+ chat: Object.assign(<%- JSON.stringify(theme.tag_plugins.chat) %>),
12
+ }
10
13
  };
11
14
 
12
15
  // required plugins (only load if needs)
@@ -10,6 +10,27 @@
10
10
  setCardLink(ctx.cardlinks);
11
11
  });
12
12
  }
13
+ } else if (id == 'voice') {
14
+ ctx.voiceAudios = document.querySelectorAll('.voice>audio');
15
+ if (ctx.voiceAudios?.length > 0) {
16
+ utils.js(js, { defer: true }).then(function () {
17
+ createVoiceDom(ctx.voiceAudios);
18
+ });
19
+ }
20
+ } else if (id == 'video') {
21
+ ctx.videos = document.querySelectorAll('.video>video');
22
+ if (ctx.videos?.length > 0) {
23
+ utils.js(js, { defer: true }).then(function () {
24
+ videoEvents(ctx.videos);
25
+ });
26
+ }
27
+ } else if (id == 'download-file') {
28
+ ctx.files = document.querySelectorAll('.file');
29
+ if (ctx.files?.length > 0) {
30
+ utils.js(js, { defer: true }).then(function () {
31
+ downloadFileEvent(ctx.files);
32
+ });
33
+ }
13
34
  } else {
14
35
  const els = document.getElementsByClassName(`ds-${id}`);
15
36
  if (els?.length > 0) {
@@ -25,5 +46,75 @@
25
46
  }
26
47
  }
27
48
  }
49
+
50
+ // chat iphone time
51
+ let phoneTimes = document.querySelectorAll('.chat .status-bar .time');
52
+
53
+ if (phoneTimes.length > 0) {
54
+ NowTime();
55
+ var date = new Date();
56
+ var sec = date.getSeconds();
57
+ var firstAdjustInterval = setInterval(firstAdjustTime, 1000 * (60 - sec));
58
+ }
59
+
60
+ function firstAdjustTime() {
61
+ NowTime();
62
+ clearInterval(firstAdjustInterval);
63
+ setInterval(NowTime, 1000 * 60);
64
+ }
65
+
66
+ function NowTime() {
67
+ for (let i = 0; i < phoneTimes.length; ++i) {
68
+ var timeSpan = phoneTimes[i];
69
+ var date = new Date();
70
+ var hour = date.getHours();
71
+ var min = date.getMinutes();
72
+ timeSpan.innerHTML = check(hour) + ":" + check(min);
73
+ }
74
+ };
75
+
76
+ function check(val) {
77
+ if (val < 10) {
78
+ return ("0" + val);
79
+ }
80
+ return (val);
81
+ }
82
+
83
+ // chat quote
84
+ const chat_quote_obverser = new IntersectionObserver((entries, observer) => {
85
+ 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) => {
86
+ observer.unobserve(entry.target);
87
+ setTimeout(() => {
88
+ entry.target.classList.add('quote-blink');
89
+ setTimeout(() => {
90
+ entry.target.classList.remove('quote-blink');
91
+ }, 1000);
92
+ }, Math.max(100, 16) * (index + 1));
93
+ });
94
+ });
95
+
96
+ var chatQuotes = document.querySelectorAll(".chat .talk .quote");
97
+ chatQuotes.forEach((quote) => {
98
+ quote.addEventListener('click', function () {
99
+ var chatCellDom = document.getElementById("quote-" + quote.getAttribute("quotedCellTag"));
100
+ if (chatCellDom) {
101
+ var chatDiv = chatCellDom.parentElement;
102
+ var mid = chatDiv.clientHeight / 2;
103
+ var offsetTop = chatCellDom.offsetTop;
104
+ if (offsetTop > mid - chatCellDom.clientHeight / 2) {
105
+ chatDiv.scrollTo({
106
+ top: chatCellDom.offsetTop - mid + chatCellDom.clientHeight / 2,
107
+ behavior: "smooth"
108
+ });
109
+ } else {
110
+ chatDiv.scrollTo({
111
+ top: 0,
112
+ behavior: "smooth"
113
+ });
114
+ }
115
+ chat_quote_obverser.observe(chatCellDom);
116
+ }
117
+ });
118
+ });
28
119
  });
29
120
  </script>
@@ -43,6 +43,8 @@
43
43
  }
44
44
  applyTheme(newTheme)
45
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();
46
48
 
47
49
  const messages = {
48
50
  light: `<%- __('message.theme_switched.light') %>`,
@@ -57,6 +59,9 @@
57
59
  const theme = window.localStorage.getItem('Stellar.theme')
58
60
  if (theme !== null) {
59
61
  applyTheme(theme)
62
+ } else {
63
+ utils.dark.mode = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
60
64
  }
65
+ utils.dark.method.toggle.start();
61
66
  })()
62
67
  </script>
@@ -1,4 +1,44 @@
1
1
  <script type="text/javascript">
2
+ function RunItem() {
3
+ this.list = []; // 存放回调函数
4
+ this.start = () => {
5
+ for (var i = 0; i < this.list.length; i++) {
6
+ this.list[i].run();
7
+ }
8
+ };
9
+ this.push = (fn, name, setRequestAnimationFrame = true) => {
10
+ let myfn = fn
11
+ if (setRequestAnimationFrame) {
12
+ myfn = () => {
13
+ utils.requestAnimationFrame(fn)
14
+ }
15
+ }
16
+ var f = new Item(myfn, name);
17
+ this.list.push(f);
18
+ };
19
+ this.remove = (name) => {
20
+ for (let index = 0; index < this.list.length; index++) {
21
+ const e = this.list[index];
22
+ if (e.name == name) {
23
+ this.list.splice(index, 1);
24
+ }
25
+ }
26
+ }
27
+ // 构造一个可以run的对象
28
+ function Item(fn, name) {
29
+ // 函数名称
30
+ this.name = name || fn.name;
31
+ // run方法
32
+ this.run = () => {
33
+ try {
34
+ fn()
35
+ } catch (error) {
36
+ console.log(error);
37
+ }
38
+ };
39
+ }
40
+ }
41
+
2
42
  const utils = {
3
43
  // 懒加载 css https://github.com/filamentgroup/loadCSS
4
44
  css: (href, before, media, attributes) => {
@@ -61,7 +101,7 @@
61
101
 
62
102
  js: (src, opt) => new Promise((resolve, reject) => {
63
103
  var script = document.createElement('script');
64
- if (src.startsWith('/')){
104
+ if (src.startsWith('/')) {
65
105
  src = ctx.root + src.substring(1);
66
106
  }
67
107
  script.src = src;
@@ -74,7 +114,7 @@
74
114
  script.async = true
75
115
  }
76
116
  script.onerror = reject
77
- script.onload = script.onreadystatechange = function() {
117
+ script.onload = script.onreadystatechange = function () {
78
118
  const loadState = this.readyState
79
119
  if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
80
120
  script.onload = script.onreadystatechange = null
@@ -90,7 +130,7 @@
90
130
  fn()
91
131
  }
92
132
  },
93
-
133
+
94
134
  onLoading: (el) => {
95
135
  if (el) {
96
136
  $(el).append('<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>');
@@ -124,7 +164,7 @@
124
164
  }
125
165
  }
126
166
  }, 5000);
127
- fetch(url).then(function(response) {
167
+ fetch(url).then(function (response) {
128
168
  if (status !== 2) {
129
169
  clearTimeout(timer);
130
170
  resolve(response);
@@ -135,11 +175,11 @@
135
175
  return response.json();
136
176
  }
137
177
  throw new Error('Network response was not ok.');
138
- }).then(function(data) {
178
+ }).then(function (data) {
139
179
  retryTimes = 0;
140
180
  utils.onLoadSuccess(el);
141
181
  callback(data);
142
- }).catch(function(error) {
182
+ }).catch(function (error) {
143
183
  if (retryTimes > 0) {
144
184
  retryTimes -= 1;
145
185
  setTimeout(() => {
@@ -154,5 +194,25 @@
154
194
  }
155
195
  req();
156
196
  },
197
+ /********************** requestAnimationFrame ********************************/
198
+ // 1、requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。
199
+ // 2、在隐藏或不可见的元素中,requestAnimationFrame 将不会进行重绘或回流,这当然就意味着更少的的 cpu,gpu 和内存使用量。
200
+ requestAnimationFrame: (fn) => {
201
+ if (!window.requestAnimationFrame) {
202
+ window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame;
203
+ }
204
+ window.requestAnimationFrame(fn)
205
+ },
206
+ dark: {},
207
+ };
208
+
209
+ // utils.dark.mode 当前模式 dark or light
210
+ // utils.dark.toggle() 暗黑模式触发器
211
+ // utils.dark.push(callBack[,"callBackName"]) 传入触发器回调函数
212
+ utils.dark.method = {
213
+ toggle: new RunItem(),
157
214
  };
158
- </script>
215
+ utils.dark = Object.assign(utils.dark, {
216
+ push: utils.dark.method.toggle.push,
217
+ });
218
+ </script>
@@ -21,7 +21,7 @@ function custom_inject() {
21
21
  <%- partial('scripts/tagtree') %>
22
22
 
23
23
  <!-- required -->
24
- <script src="<%- `${theme.stellar.main_js}?v=${stellar_info('version')}` %>" defer></script>
24
+ <script src="<%- url_for(`${theme.stellar.main_js}?v=${stellar_info('version')}`) %>" defer></script>
25
25
 
26
26
  <%- partial('scripts/theme') %>
27
27
 
@@ -4,7 +4,7 @@
4
4
  var mermaid_config = {
5
5
  startOnLoad: true,
6
6
  theme:
7
- "<%- theme.style.darkmode %>" == "auto" &&
7
+ "<%- theme.style.prefers_theme %>" == "auto" &&
8
8
  window.matchMedia("(prefers-color-scheme: dark)").matches
9
9
  ? "dark"
10
10
  : "<%- conf.theme %>",
@@ -11,5 +11,6 @@
11
11
  };
12
12
  ScrollReveal().reveal('.l_left .slide-up', slideUp);
13
13
  ScrollReveal().reveal('.l_main .slide-up', slideUp);
14
+ ScrollReveal().reveal('.l_right .slide-up', slideUp);
14
15
  });
15
16
  </script>
@@ -1,13 +1,12 @@
1
1
  <script>
2
- window.addEventListener('DOMContentLoaded', (event) => {
3
- window.searchConfig = {
4
- appId: '<%- conf.appId %>',
5
- apiKey: '<%- conf.apiKey %>',
6
- indexName: '<%- conf.indexName %>',
7
- hitsPerPage: 100,
8
- };
9
- utils.js('https://gcore.jsdelivr.net/algoliasearch/3/algoliasearch.min.js', { defer: true });
10
- utils.js('/js/search/algolia-search.js', { defer: true });
11
- });
12
- </script>
13
-
2
+ window.addEventListener('DOMContentLoaded', (event) => {
3
+ window.searchConfig = {
4
+ appId: '<%- conf.appId %>',
5
+ apiKey: '<%- conf.apiKey %>',
6
+ indexName: '<%- conf.indexName %>',
7
+ js: '<%- conf.js %>',
8
+ hitsPerPage: 100,
9
+ };
10
+ utils.js('/js/search/algolia-search.js', { defer: true });
11
+ });
12
+ </script>
package/layout/layout.ejs CHANGED
@@ -46,7 +46,7 @@ if (theme.style.prefers_theme === 'auto') {
46
46
  html += `<body>`
47
47
  html += site_background
48
48
  html += partial('_partial/cover/index')
49
- html += `<div class="l_body s:aa ${page_type} ${article_type}" id="start" layout="${page.layout}" ${indent ? 'text-indent' : ''}>`
49
+ html += `<div class="l_body ${page_type} ${article_type}" id="start" layout="${page.layout}" ${indent ? 'text-indent' : ''}>`
50
50
  html += `<aside class="l_left">`
51
51
  html += `<div class="leftbar-container${theme.style.leftbar?.blur ? ' leftbar-blur' : ''}">`
52
52
  html += partial('_partial/sidebar/index_leftbar')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hexo-theme-stellar",
3
- "version": "1.29.0",
3
+ "version": "1.30.0",
4
4
  "description": "Elegant and powerful theme for Hexo.",
5
5
  "main": "package.json",
6
6
  "scripts": {
@@ -64,4 +64,7 @@ module.exports = ctx => {
64
64
  ctx.theme.config.menubar = {}
65
65
  }
66
66
 
67
+ // chat users
68
+ ctx.theme.config.chat_users = data.chat_users;
69
+
67
70
  }
@@ -63,7 +63,7 @@ module.exports = ctx => {
63
63
  const pages = ctx.locals.get('pages')
64
64
  // wiki 所有页面
65
65
  const wiki_pages = pages.filter(p => (p.wiki != null)).map(p => new WikiPage(p))
66
- const wiki_list = Object.keys(wiki.tree)
66
+ const wiki_list = Object.keys(wiki.tree).filter(id => wiki_pages.some(p => p.wiki === id))
67
67
  // 上架的项目列表
68
68
  wiki.shelf = ctx.locals.get('data').wiki || []
69
69
 
@@ -99,11 +99,6 @@ module.exports = ctx => {
99
99
  let id = wiki_list[i];
100
100
  let item = wiki.tree[id]
101
101
  let sub_pages = wiki_pages.filter(p => p.wiki === id)
102
- if (!sub_pages || sub_pages.length == 0) {
103
- wiki_list.splice(i, 1)
104
- delete wiki.tree[id]
105
- continue
106
- }
107
102
 
108
103
  // 首页
109
104
  // 未特别指定首页时,获取TOC第一页作为首页
@@ -2,3 +2,29 @@
2
2
 
3
3
  hexo.extend.filter.register('after_render:html', require('./lib/img_lazyload').processSite);
4
4
  hexo.extend.filter.register('after_render:html', require('./lib/img_onerror').processSite);
5
+
6
+ function change_image(data) {
7
+ if (this.theme.config.tag_plugins.image.parse_markdown) {
8
+ // data.content = data.content.replace(
9
+ // /!\[(.*?)\]\((.*?)\s*(?:"(.*?)")?\)/g,
10
+ // '{% image $2 $3 %}'
11
+ // );
12
+ let splited_content = data.content.split(/(```[\s\S]*?```|{%\s*gallery\s*%}[\s\S]*?{%\s*endgallery\s*%})/g);
13
+ splited_content = splited_content.map((s) => {
14
+ let matches_no = s.match(/(```[\s\S]*?```|{%\s*gallery\s*%}[\s\S]*?{%\s*endgallery\s*%})/i);
15
+ let matches_img = s.match(/!\[(.*?)\]\((.*?)\s*(?:"(.*?)")?\)/i);
16
+ if (!matches_no && matches_img) {
17
+ s = s.replace(
18
+ /!\[(.*?)\]\((.*?)\s*(?:"(.*?)")?\)/g,
19
+ `{% image $2 $3 %}`
20
+ );
21
+ }
22
+ return s;
23
+ });
24
+ data.content = splited_content.join('');
25
+ }
26
+ return data;
27
+ }
28
+
29
+ hexo.extend.filter.register('before_post_render', change_image, 9);
30
+
@@ -30,7 +30,7 @@ function paginationWithEmpty(base, posts, options={}) {
30
30
 
31
31
  hexo.extend.generator.register('notebooks', function (locals) {
32
32
  const { site_tree, notebooks } = hexo.theme.config
33
- if (notebooks.tree.length === 0) {
33
+ if (!notebooks?.tree || Object.keys(notebooks.tree).length === 0) {
34
34
  return []
35
35
  }
36
36
 
@@ -31,11 +31,13 @@ hexo.extend.generator.register('search_json_generator', function (locals) {
31
31
  temp_post.path = root + post.path
32
32
  }
33
33
  if (cfg.content != false && post.content) {
34
- var content = stripHTML(post.content).trim()
34
+ var content = stripHTML(post.content.replace(/<span class="line">\d+<\/span>/g, '')).trim()
35
35
  // 部分HTML标签
36
36
  content = content.replace(/<iframe[\s|\S]+iframe>/g, '')
37
37
  content = content.replace(/<hr>/g, '')
38
38
  content = content.replace(/<br>/g, '')
39
+ // 去除HTML实体
40
+ content = content.replace(/&[^\s;]+;/g, "")
39
41
  // 换行符换成空格
40
42
  content = content.replace(/\\n/g, ' ')
41
43
  content = content.replace(/\n/g, ' ')
@@ -60,8 +62,22 @@ hexo.extend.generator.register('search_json_generator', function (locals) {
60
62
  return temp_post
61
63
  }
62
64
 
65
+ function matchAndExit(path, patterns) {
66
+ for (let pattern of patterns) {
67
+ const regexPattern = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
68
+ if (path.match(regexPattern)) {
69
+ // console.log("Matched pattern:", pattern);
70
+ return true;
71
+ }
72
+ }
73
+ return false;
74
+ }
75
+
63
76
  if (posts) {
64
77
  posts.each(function(post) {
78
+ var layout_list = ["post"]
79
+ if (!layout_list.includes(post.layout)) return
80
+ if (cfg.skip_search && matchAndExit(post.path, cfg.skip_search)) return
65
81
  if (post.indexing == false) return
66
82
  let temp_post = generateJson(post)
67
83
  res.push(temp_post)
@@ -69,6 +85,9 @@ hexo.extend.generator.register('search_json_generator', function (locals) {
69
85
  }
70
86
  if (pages) {
71
87
  pages.each(function(page) {
88
+ var layout_list = ["page", "wiki"]
89
+ if (!layout_list.includes(page.layout)) return
90
+ if (cfg.skip_search && matchAndExit(page.path, cfg.skip_search)) return
72
91
  if (page.indexing == false) return
73
92
  let temp_post = generateJson(page)
74
93
  res.push(temp_post)
@@ -78,4 +97,4 @@ hexo.extend.generator.register('search_json_generator', function (locals) {
78
97
  path: cfg.path,
79
98
  data: JSON.stringify(res)
80
99
  }
81
- })
100
+ })