hexo-theme-volantis 6.0.2 → 6.0.4

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [6.0.4](https://github.com/volantis-x/hexo-theme-volantis/compare/v6.0.3...v6.0.4) (2026-06-09)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * bootcdn被投毒 ([e8f7c33](https://github.com/volantis-x/hexo-theme-volantis/commit/e8f7c33dddb2d23066e12ce81ad168bc0e3da0a1))
9
+
10
+ ## [6.0.3](https://github.com/volantis-x/hexo-theme-volantis/compare/v6.0.2...v6.0.3) (2026-05-17)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * debounce ([#969](https://github.com/volantis-x/hexo-theme-volantis/issues/969)) ([faf3590](https://github.com/volantis-x/hexo-theme-volantis/commit/faf35904b8fcb25f888314c9031af5f7fda26526))
16
+
3
17
  ## [6.0.2](https://github.com/volantis-x/hexo-theme-volantis/compare/v6.0.1...v6.0.2) (2025-11-21)
4
18
 
5
19
 
package/_config.yml CHANGED
@@ -62,7 +62,7 @@ cdn_system:
62
62
  # Tip: 在本配置文件中 匹配以 "volantis-static" 开头的链接路径替换为该格式的前缀开头 prefix
63
63
  static:
64
64
  # CDN 链接前缀
65
- prefix: https://unpkg.com/volantis-static@0.0.1660614606622
65
+ prefix: https://unpkg.com/volantis-static@0.0.1761982841160
66
66
  # volantis-static CDN 格式
67
67
  format: ${prefix}/libs/${name}/${file}
68
68
  # cdnjs
@@ -70,7 +70,7 @@ cdn_system:
70
70
  # Tip: 在本配置文件中 匹配以 "volantis-cdnjs" 开头的链接路径替换为该格式的前缀开头 prefix
71
71
  cdnjs:
72
72
  # CDN 链接前缀
73
- prefix: https://cdn.bootcdn.net/ajax/libs
73
+ prefix: https://cdnjs.cloudflare.com/ajax/libs
74
74
  # cdnjs CDN 格式
75
75
  format: ${prefix}/${name}/${version}/${min_file}
76
76
  # ========================================================================
@@ -121,7 +121,6 @@ replace:
121
121
  # dns-prefetch preconnect x-dns-prefetch-control
122
122
  dns_prefetch:
123
123
  - https://unpkg.com
124
- - https://cdn.bootcdn.net
125
124
 
126
125
 
127
126
  # 平滑滚动效果
@@ -869,7 +868,7 @@ plugins:
869
868
  pjax:
870
869
  enable: true
871
870
  cover: true # 封面是否pjax处理 false:每次切换页面封面都重载,适合封面较少的情况 true:封面经过Pjax处理,适合封面较多的情况
872
- timeout: 5000 # The timeout in milliseconds for the XHR requests. Set to 0 to disable the timeout.
871
+ timeout: 900 # The timeout in milliseconds for the XHR requests. Set to 0 to disable the timeout.
873
872
  cacheBust: false # When set to true, Pjax appends a timestamp to skip the browser cache.
874
873
  exclude: # 被屏蔽的 url 地址将不启用 pjax 跳转,可以在控制台下使用 window.location.pathname 获取
875
874
  # - '/artitalk/' # artitalk 不支持 pjax
@@ -0,0 +1,59 @@
1
+ navbar:
2
+ archive: 归档
3
+ category: 分类
4
+ tag: 标签
5
+
6
+ post:
7
+ pin: 置顶
8
+ readmore: 阅读全文
9
+ readoriginal: 去源站阅读
10
+ wordcount: '字数:%s 字'
11
+ duration: '时长:%s 分钟'
12
+ comments: 评论
13
+ comments_load: 点击加载 %s 评论
14
+ comments_check: 正在检查 %s 能否访问...
15
+ comments_placeholder: 无法加载 %s 评论系统,请确保您的网络能够正常访问。
16
+ copy_button: 复制
17
+ copy_success: 复制成功
18
+ copy_failure: 复制失败
19
+ updated: 更新于
20
+ prev_page: 上一页
21
+ next_page: 下一页
22
+ copyright:
23
+ author: 本文作者
24
+ link: 本文链接
25
+ license_title: 版权声明
26
+ license_content: "本博客所有文章除特别声明外,均采用 %s 许可协议。转载请注明出处!"
27
+ date: 发布于
28
+ updated: 更新于
29
+ references: 原文出处
30
+ license: 许可协议
31
+
32
+ footer:
33
+ license: '博客内容遵循 [署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans)'
34
+ use: 本站使用
35
+ theme: 作为主题
36
+ total_views: 总访问量为
37
+ total_visitors: 总访客量
38
+ times: 次
39
+ site_source: 本站使用 %s 作为主题,您可以在 %s 找到[本站源码](%s)。
40
+ site_views: 总访问量约为 %s 次,访问人数约为 %s 人。
41
+
42
+ symbol:
43
+ comma: ","
44
+ period: "。"
45
+ colon: ":"
46
+ brackets_l: "("
47
+ brackets_r: ")"
48
+
49
+ kill:
50
+ title: "抱歉,您的浏览器无法访问本站"
51
+ more: "了解详情 >"
52
+ noscript: "本页面需要浏览器支持(启用)JavaScript"
53
+ ie: "微软已经于2016年终止了对 Internet Explorer (IE) 10 及更早版本的支持,<br/>继续使用存在极大的安全隐患,请使用当代主流的浏览器进行访问。"
54
+
55
+ search:
56
+ title: 搜索
57
+ load_data: 数据库加载中
58
+ hits_empty: '找不到您查询的内容:${query}'
59
+ hits_stats: '找到 ${hits} 条结果,用时 ${time} 毫秒'
@@ -6,7 +6,7 @@ if (aid && site.data && site.data.author && (aid in site.data.author)) {
6
6
  }
7
7
  %>
8
8
  <div class='new-meta-item author' itemprop="author" itemscope itemtype="http://schema.org/Person">
9
- <a itemprop="url" class='author' href="<%- author.url %>" rel="nofollow">
9
+ <a itemprop="url" class='author' href="<%- author.url %>" rel="nofollow" alt="author">
10
10
  <img itemprop="image" src="<%- author.avatar %>">
11
11
  <p itemprop="name"><%- author.name %></p>
12
12
  </a>
@@ -1,5 +1,5 @@
1
1
  <% if(post.thumbnail && post.thumbnail.length){ %>
2
2
  <a title='<%- post.title %>' href='<%- url_for(post.link || post.path) %>'>
3
- <img class='thumbnail' src='<%- post.thumbnail %>' itemprop="image">
3
+ <img class='thumbnail' src='<%- post.thumbnail %>' itemprop="image" alt="thumbnail">
4
4
  </a>
5
5
  <% } %>
@@ -104,6 +104,35 @@
104
104
  })();
105
105
  volantis.debug = "<%- theme.debug %>"; // 调试模式
106
106
  volantis.dom = {}; // 页面Dom see: /source/js/app.js etc.
107
+ /******************** volantis.getFunctionHash ********************************/
108
+ // 简易文本哈希
109
+ volantis.simpleTextHash= (str) => {
110
+ // 初始哈希值:选择质数5381(DJB2算法推荐的起始值)
111
+ let hash = 5381;
112
+ // 遍历每个字符,更新哈希值
113
+ for (let i = 0; i < str.length; i++) {
114
+ // 等价于 hash = hash * 33 + charCode(位移运算更高效)
115
+ hash = (hash << 5) + hash + str.charCodeAt(i);
116
+ // 避免数值过大溢出(可选:取模2^32,模拟32位无符号整数)
117
+ hash = hash & hash; // 或 hash >>> 0(转换为无符号)
118
+ }
119
+ // 转换为十六进制字符串,确保至少8位(补前导零)
120
+ return hash.toString(16).padStart(8, '0');
121
+ }
122
+
123
+ // 获取函数内容的哈希
124
+ volantis.getFunctionHash= (func) => {
125
+ // 提取函数源码字符串
126
+ const funcStr = func.toString();
127
+ // 计算哈希值
128
+ return volantis.simpleTextHash(funcStr);
129
+ }
130
+
131
+ // 测试示例
132
+ //function sampleFunc(a, b) {
133
+ // return a * b + 10;
134
+ //}
135
+ //console.log(volantis.getFunctionHash(sampleFunc)); // 输出函数源码的MD5哈希
107
136
  /******************** volantis.EventListener ********************************/
108
137
  // 事件监听器 see: /source/js/app.js
109
138
  volantis.EventListener = {}
@@ -203,14 +232,20 @@
203
232
  }
204
233
  };
205
234
  this.push = (fn, name, setRequestAnimationFrame = true) => {
235
+ if (typeof fn !== 'function') return;
206
236
  let myfn = fn
207
237
  if (setRequestAnimationFrame) {
208
238
  myfn = () => {
209
239
  volantis.requestAnimationFrame(fn)
210
240
  }
211
241
  }
212
- var f = new Item(myfn, name);
213
- this.list.push(f);
242
+ name = name || volantis.getFunctionHash(fn)
243
+ const idx = this.list.findIndex(item => item.name === name);
244
+ if (idx !== -1) {
245
+ this.list[idx] = new Item(myfn, name);
246
+ return;
247
+ }
248
+ this.list.push(new Item(myfn, name));
214
249
  };
215
250
  this.remove = (name) => {
216
251
  for (let index = 0; index < this.list.length; index++) {
@@ -223,7 +258,7 @@
223
258
  // 构造一个可以run的对象
224
259
  function Item(fn, name) {
225
260
  // 函数名称
226
- this.name = name || fn.name;
261
+ this.name = name || volantis.getFunctionHash(fn);
227
262
  // run方法
228
263
  this.run = () => {
229
264
  try {
@@ -346,15 +381,42 @@
346
381
  }
347
382
  }
348
383
  }
384
+ /********************** 节流和防抖 ********************************/
385
+ // 节流函数:确保指定时间内仅执行一次 时间戳版本的节流实现
386
+ volantis.throttle=(func, delay = 200) => {
387
+ let lastTime = 0;
388
+ return function (...args) {
389
+ const now = Date.now();
390
+ if (now - lastTime >= delay) {
391
+ func.apply(this, args);
392
+ lastTime = now;
393
+ }
394
+ };
395
+ };
396
+ // 防抖函数 setTimeout实现
397
+ volantis.debounce=(func, wait = 200) => {
398
+ let timer = null;
399
+ return function () {
400
+ if (timer) {
401
+ clearTimeout(timer)
402
+ }
403
+ timer = setTimeout(() => {
404
+ func.apply(this, arguments)
405
+ timer = null
406
+ }, wait)
407
+ };
408
+ };
349
409
  /********************** requestAnimationFrame ********************************/
350
410
  // 1、requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。
351
411
  // 2、在隐藏或不可见的元素中,requestAnimationFrame 将不会进行重绘或回流,这当然就意味着更少的的 cpu,gpu 和内存使用量。
352
412
  volantis.requestAnimationFrame = (fn) => {
353
413
  if (!window.requestAnimationFrame) {
354
- window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame;
414
+ window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || function (callback) {
415
+ return window.setTimeout(callback, 1000 / 60);
416
+ };
355
417
  }
356
- window.requestAnimationFrame(fn)
357
- }
418
+ return window.requestAnimationFrame(fn);
419
+ };
358
420
  /************************ layoutHelper *****************************************/
359
421
  volantis.layoutHelper = (helper, html, opt) => {
360
422
  opt = Object.assign({ clean: false, pjax: true }, opt)
@@ -507,5 +569,72 @@
507
569
  img.onerror = null;
508
570
  }
509
571
  /******************************************************************************/
572
+ // Hack Interval
573
+ // 保存原生定时器方法
574
+ const nativeSetTimeout = window.setTimeout;
575
+ const nativeSetInterval = window.setInterval;
576
+ const nativeClearTimeout = window.clearTimeout;
577
+ const nativeClearInterval = window.clearInterval;
578
+
579
+ // 存储活动定时器ID的数组
580
+ volantis.activeTimeout = [];
581
+ volantis.activeInterval = [];
582
+
583
+ // 重写setTimeout:执行后自动移除ID
584
+ window.setTimeout = function(callback, delay, ...args) {
585
+ // 包装回调函数,执行后从列表移除ID
586
+ const wrappedCallback = function(...innerArgs) {
587
+ try {
588
+ // 执行原始回调(保持this指向和参数传递)
589
+ return callback.apply(this, innerArgs);
590
+ } finally {
591
+ // 无论回调是否报错,均移除ID
592
+ const index = volantis.activeTimeout.indexOf(timerId);
593
+ if (index !== -1) volantis.activeTimeout.splice(index, 1);
594
+ }
595
+ };
596
+
597
+ // 调用原生setTimeout获取ID
598
+ const timerId = nativeSetTimeout(wrappedCallback, delay, ...args);
599
+ // 添加到活动列表
600
+ volantis.activeTimeout.push(timerId);
601
+ return timerId;
602
+ };
603
+
604
+ // 重写setInterval:需显式清除才移除ID
605
+ window.setInterval = function(callback, delay, ...args) {
606
+ const timerId = nativeSetInterval(callback, delay, ...args);
607
+ volantis.activeInterval.push(timerId);
608
+ return timerId;
609
+ };
610
+
611
+ // 重写清除方法:同步移除ID列表记录
612
+ window.clearTimeout = function(timerId) {
613
+ nativeClearTimeout(timerId);
614
+ const index = volantis.activeTimeout.indexOf(timerId);
615
+ if (index !== -1) volantis.activeTimeout.splice(index, 1);
616
+ };
617
+
618
+ window.clearInterval = function(timerId) {
619
+ nativeClearInterval(timerId);
620
+ const index = volantis.activeInterval.indexOf(timerId);
621
+ if (index !== -1) volantis.activeInterval.splice(index, 1);
622
+ };
623
+ volantis.getActiveInterval = function () {
624
+ return [...volantis.activeInterval];
625
+ }
626
+ volantis.getActiveTimeout = function () {
627
+ return [...volantis.activeTimeout];
628
+ }
629
+ volantis.clearAllTimers = function (){
630
+ volantis.getActiveInterval().forEach(function(i) {
631
+ clearInterval(i);
632
+ });
633
+ volantis.getActiveTimeout().forEach(function(i) {
634
+ clearTimeout(i);
635
+ });
636
+ }
637
+ volantis.pjax.send(volantis.clearAllTimers,"clearAllTimers");
638
+ /******************************************************************************/
510
639
  </script>
511
640
  <%- partial('./config') %>
@@ -30,7 +30,7 @@ let aplayer = theme.plugins.aplayer;
30
30
  <% } %>
31
31
  <% } else { %>
32
32
  <% if (post && post.music) { %>
33
- <div class="aplayer-local-min"></div>
33
+ <div class="aplayer-local-min"><div class="aplayer-local-min-conf" style="display: none;"><%- JSON.stringify(post.music) %></div></div>
34
34
  <% } else { %>
35
35
  <div class="aplayer-local"></div>
36
36
  <% } %>
@@ -72,7 +72,7 @@
72
72
  document.addEventListener("DOMContentLoaded", ()=>{
73
73
  SetAPlayerPlugin();
74
74
  });
75
- volantis.pjax.push(SetAPlayerPlugin);
75
+ volantis.pjax.push(SetAPlayerPlugin,"SetAPlayerPlugin");
76
76
  <% } else { %>
77
77
  volantis.js("<%- theme.cdn.aplayer_js %>").then(()=>{
78
78
  document.querySelectorAll('.aplayer-local').forEach(e=>{
@@ -92,10 +92,28 @@
92
92
  });
93
93
  })
94
94
 
95
- // post.music 迷你模式 pjax todo...
96
- // document.querySelectorAll('.aplayer-local-min').forEach(e=>{
97
- // // todo...
98
- // })
95
+ // post.music 迷你模式 pjax
96
+ function aplayerMin(){
97
+ if(!document.querySelector('.aplayer-local-min')) return;
98
+ document.querySelectorAll('.aplayer-local-min').forEach(e=>{
99
+ new APlayer(Object.assign({
100
+ container: e,
101
+ mini: true,
102
+ order: "<%-theme.plugins.aplayer.order %>",
103
+ volume: "<%-theme.plugins.aplayer.volume %>",
104
+ autoplay: <%-theme.plugins.aplayer.autoplay %>,
105
+ loop: "<%-theme.plugins.aplayer.loop %>",
106
+ theme: "<%-theme.plugins.aplayer.theme %>",
107
+ listMaxHeight: "<%-theme.plugins.aplayer.list_max_height %>",
108
+ listFolded: "<%-theme.plugins.aplayer.list_folded %>",
109
+ preload: 'auto',
110
+ lrcType: 3,
111
+ audio: <%- JSON.stringify(theme.plugins.aplayer.audio) %>
112
+ },JSON.parse(e.querySelector('.aplayer-local-min-conf').innerText)));
113
+ })
114
+ };
115
+ aplayerMin();
116
+ volantis.pjax.push(aplayerMin,"aplayerMin");
99
117
  // 右键 todo...
100
118
  })
101
119
  <% } %>
@@ -83,5 +83,5 @@
83
83
  ctx.setDarkMode(volantis.dark.mode === "dark")
84
84
  })
85
85
  }
86
- volantis.dark.push(dark_artalk);
86
+ volantis.dark.push(dark_artalk,"dark_artalk");
87
87
  </script>
@@ -55,7 +55,7 @@
55
55
  }
56
56
 
57
57
  pjax_beaudar();
58
- volantis.pjax.push(check_beaudar_style);
59
- volantis.pjax.push(pjax_beaudar);
60
- volantis.dark.push(dark_beaudar);
58
+ volantis.pjax.push(check_beaudar_style,"check_beaudar_style");
59
+ volantis.pjax.push(pjax_beaudar,"pjax_beaudar");
60
+ volantis.dark.push(dark_beaudar,"dark_beaudar");
61
61
  </script>
@@ -2,14 +2,14 @@
2
2
  volantis.css("<%- theme.cdn.pace_css %>");
3
3
  volantis.js("<%- theme.cdn.pace_js %>").then(() => {
4
4
  Pace.options.restartOnPushState = false;
5
- volantis.pjax.send(Pace.restart);
5
+ volantis.pjax.send(Pace.restart,"Pace.restart");
6
6
  // 阅读进度
7
- volantis.scroll.push(() => {
7
+ volantis.scroll.push(volantis.debounce(() => {
8
8
  Pace.stop();
9
9
  Pace.trigger('start');
10
10
  Pace.bar.update(volantis.scroll.progress() * 100);
11
11
  document.body.className = document.body.className.replaceAll("pace-running ","");
12
- },"阅读进度")
12
+ }),"阅读进度")
13
13
  // todo:配置项开关, 哪些页面添加阅读进度
14
14
  })
15
- </script>
15
+ </script>
@@ -15,7 +15,7 @@
15
15
  const performanceMonitor = {
16
16
  slowThreshold: 800, // 慢请求阈值(毫秒)
17
17
  slowCount: 0, // 连续慢请求计数
18
- maxSlowAllowed: 2, // 允许的最大连续慢请求数
18
+ maxSlowAllowed: 1, // 允许的最大连续慢请求数
19
19
  requestStartTime: 0, // 请求开始时间戳
20
20
 
21
21
  // 记录请求开始时间
@@ -45,7 +45,7 @@
45
45
  volantis.activateNavIndex = 0
46
46
  activateNavByIndex(navItems[volantis.activateNavIndex])
47
47
  if (targets[0]) {
48
- volantis.scroll.push(() => {
48
+ volantis.scroll.push(volantis.debounce(() => {
49
49
  if (targets[0].getBoundingClientRect().top >= 0) {
50
50
  volantis.activateNavIndex = 0
51
51
  } else if (targets[targets.length - 1].getBoundingClientRect().top < 0) {
@@ -61,7 +61,7 @@
61
61
  }
62
62
  }
63
63
  activateNavByIndex(navItems[volantis.activateNavIndex])
64
- })
64
+ }),"sidebar-toc")
65
65
  }
66
66
 
67
67
  // 方案二:
@@ -109,6 +109,6 @@
109
109
  document.addEventListener("DOMContentLoaded", () => {
110
110
  volantis.requestAnimationFrame(listenSidebarTOC)
111
111
  });
112
- volantis.pjax.push(listenSidebarTOC);
112
+ volantis.pjax.push(listenSidebarTOC,"listenSidebarTOC");
113
113
  </script>
114
114
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hexo-theme-volantis",
3
- "version": "6.0.2",
3
+ "version": "6.0.4",
4
4
  "description": "Elegant and powerful theme for Hexo.",
5
5
  "main": "package.json",
6
6
  "scripts": {
@@ -11,5 +11,5 @@ hexo.extend.helper.register('autoCanonical', function (config, page) {
11
11
  var base_url = config.url;
12
12
  if (config.url.charAt(config.url.length - 1) !== '/') base_url += '/';
13
13
 
14
- return '<link rel="canonical" href="' + base_url + page.canonical_path.replace('index.html', '').toLowerCase() + '"/>';
15
- });
14
+ return '<link rel="canonical" href="' + base_url + page.canonical_path.replace('index.html', '').replace('.html', '') + '"/>';
15
+ });
@@ -29,6 +29,10 @@ hexo.extend.helper.register('generate_seo', function (theme, page) {
29
29
  robots_content = theme.seo.robots.tag
30
30
  }
31
31
  }
32
+ const IS_BACKUP = process.env.IS_BACKUP === 'true';
33
+ if (IS_BACKUP) {
34
+ robots_content = 'noindex, nofollow, noarchive';
35
+ }
32
36
  if (robots_content) {
33
37
  robots_content = addImagePreviewIfNoNoindex(robots_content)
34
38
  return `<meta name="robots" content="${robots_content}">`
@@ -16,6 +16,7 @@ function init(hexo, config, theme, page) {
16
16
  } else if (hexo.is_home() && page.prev == 0) {
17
17
  keywords = config.keywords || "";
18
18
  description = config.description || "";
19
+ title = config.seo_title || config.title;
19
20
  } else {
20
21
  if (
21
22
  page.layout == "docs" &&
package/source/js/app.js CHANGED
@@ -69,7 +69,7 @@ const VolantisApp = (() => {
69
69
  fn.setHeaderSearch();
70
70
  }
71
71
  }
72
- volantis.scroll.push(fn.scrollEventCallBack, "scrollEventCallBack")
72
+ volantis.scroll.push(volantis.debounce(fn.scrollEventCallBack), "scrollEventCallBack")
73
73
  }
74
74
 
75
75
  fn.event = () => {