hexo-theme-stellar 1.32.0 → 1.32.2

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 (115) hide show
  1. package/_config.yml +47 -41
  2. package/layout/_partial/cover/post_cover.ejs +1 -5
  3. package/layout/_partial/cover/wiki_cover.ejs +1 -1
  4. package/layout/_partial/main/footer.ejs +15 -5
  5. package/layout/_partial/main/navbar/article_banner.ejs +1 -1
  6. package/layout/_partial/main/navbar/ghinfo.ejs +1 -1
  7. package/layout/_partial/scripts/defines.ejs +3 -1
  8. package/layout/_partial/scripts/lazyload.ejs +67 -0
  9. package/layout/_partial/scripts/utils.ejs +1 -0
  10. package/layout/_partial/scripts.ejs +1 -0
  11. package/layout/_partial/sidebar/index_leftbar.ejs +1 -1
  12. package/layout/_partial/sidebar/logo.ejs +1 -1
  13. package/layout/_partial/widgets/author.ejs +1 -1
  14. package/layout/_partial/widgets/ghissues.ejs +1 -1
  15. package/layout/_partial/widgets/ghrepo.ejs +2 -2
  16. package/layout/_partial/widgets/ghuser.ejs +2 -2
  17. package/layout/_partial/widgets/markdown.ejs +1 -1
  18. package/layout/_partial/widgets/recent.ejs +1 -1
  19. package/layout/_partial/widgets/related.ejs +1 -1
  20. package/layout/_partial/widgets/timeline.ejs +1 -1
  21. package/layout/_partial/widgets/tree.ejs +1 -1
  22. package/layout/_plugins/scrollreveal.ejs +3 -3
  23. package/package.json +1 -1
  24. package/scripts/events/lib/get_image_ratios.js +53 -31
  25. package/scripts/tags/index.js +1 -1
  26. package/scripts/tags/lib/albums.js +4 -3
  27. package/scripts/tags/lib/banner.js +1 -1
  28. package/scripts/tags/lib/{quote.js → blockquote.js} +5 -5
  29. package/scripts/tags/lib/chat.js +1 -5
  30. package/scripts/tags/lib/friends.js +1 -1
  31. package/scripts/tags/lib/gallery.js +4 -3
  32. package/scripts/tags/lib/image.js +5 -5
  33. package/scripts/tags/lib/link.js +1 -5
  34. package/scripts/tags/lib/md.js +1 -1
  35. package/scripts/tags/lib/navbar.js +2 -2
  36. package/scripts/tags/lib/posters.js +4 -3
  37. package/scripts/tags/lib/sites.js +7 -1
  38. package/scripts/tags/lib/timeline.js +1 -1
  39. package/source/css/_common/base.styl +17 -9
  40. package/source/css/_common/blockquote.styl +1 -1
  41. package/source/css/_common/blur.styl +7 -7
  42. package/source/css/_common/button.styl +2 -2
  43. package/source/css/_common/canonical.styl +1 -1
  44. package/source/css/_common/cap.styl +1 -1
  45. package/source/css/_common/device.styl +4 -4
  46. package/source/css/_common/highlight.styl +7 -2
  47. package/source/css/_common/html.styl +1 -1
  48. package/source/css/_common/svg.styl +1 -1
  49. package/source/css/_common/title.styl +1 -1
  50. package/source/css/_components/layout.styl +2 -2
  51. package/source/css/_components/list.styl +1 -2
  52. package/source/css/_components/main.styl +8 -4
  53. package/source/css/_components/md.styl +2 -2
  54. package/source/css/_components/pages/archives.styl +5 -5
  55. package/source/css/_components/pages/article-story.styl +42 -40
  56. package/source/css/_components/pages/article-tech.styl +2 -2
  57. package/source/css/_components/pages/notebook.styl +2 -2
  58. package/source/css/_components/partial/footer.styl +5 -9
  59. package/source/css/_components/partial/navbar.styl +2 -2
  60. package/source/css/_components/partial/related.styl +6 -6
  61. package/source/css/_components/sidebar/footer.styl +1 -1
  62. package/source/css/_components/sidebar/menu.styl +3 -3
  63. package/source/css/_components/sidebar/search.styl +7 -7
  64. package/source/css/_components/sidebar/sidebar.styl +24 -24
  65. package/source/css/_components/tag-plugins/button.styl +2 -2
  66. package/source/css/_components/tag-plugins/chat.styl +44 -8
  67. package/source/css/_components/tag-plugins/checkbox.styl +1 -1
  68. package/source/css/_components/tag-plugins/copy.styl +13 -8
  69. package/source/css/_components/tag-plugins/friends.styl +5 -6
  70. package/source/css/_components/tag-plugins/friends_posts.styl +8 -7
  71. package/source/css/_components/tag-plugins/gallery.styl +1 -2
  72. package/source/css/_components/tag-plugins/image.styl +1 -1
  73. package/source/css/_components/tag-plugins/inline-labels.styl +3 -3
  74. package/source/css/_components/tag-plugins/navbar.styl +18 -8
  75. package/source/css/_components/tag-plugins/note.styl +0 -3
  76. package/source/css/_components/tag-plugins/okr.styl +2 -3
  77. package/source/css/_components/tag-plugins/override.styl +0 -5
  78. package/source/css/_components/tag-plugins/poetry.styl +1 -1
  79. package/source/css/_components/tag-plugins/quot.styl +1 -25
  80. package/source/css/_components/tag-plugins/quote.styl +1 -1
  81. package/source/css/_components/tag-plugins/read/paper.styl +2 -2
  82. package/source/css/_components/tag-plugins/read/reel.styl +4 -4
  83. package/source/css/_components/tag-plugins/sites.styl +14 -11
  84. package/source/css/_components/tag-plugins/tabs.styl +1 -1
  85. package/source/css/_components/tag-plugins/timeline.styl +10 -4
  86. package/source/css/_components/widgets/components.styl +3 -3
  87. package/source/css/_components/widgets/ghuser.styl +3 -3
  88. package/source/css/_components/widgets/list.styl +2 -2
  89. package/source/css/_components/widgets/markdown.styl +9 -3
  90. package/source/css/_components/widgets/related.styl +3 -3
  91. package/source/css/_components/widgets/tagcloud.styl +2 -2
  92. package/source/css/_components/widgets/timeline.styl +2 -2
  93. package/source/css/_components/widgets/toc.styl +3 -4
  94. package/source/css/_components/widgets/widgets.styl +4 -4
  95. package/source/css/_custom.styl +26 -38
  96. package/source/css/_defines/const.styl +1 -5
  97. package/source/css/_defines/func.styl +4 -4
  98. package/source/css/_defines/theme_base.styl +105 -0
  99. package/source/css/_defines/theme_colorful.styl +117 -0
  100. package/source/css/_plugins/comments/artalk.styl +25 -8
  101. package/source/css/_plugins/comments/twikoo.styl +11 -11
  102. package/source/css/_plugins/comments/waline.styl +2 -2
  103. package/source/css/_plugins/index.styl +1 -2
  104. package/source/css/_plugins/lazyload.styl +40 -50
  105. package/source/css/_plugins/mermaid.styl +1 -1
  106. package/source/css/_plugins/swiper.styl +2 -2
  107. package/source/css/_plugins/tianli_gpt.styl +8 -8
  108. package/source/css/main.styl +2 -1
  109. package/source/js/services/friends.js +1 -0
  110. package/source/js/services/friends_and_posts.js +1 -0
  111. package/source/js/services/sites.js +1 -0
  112. package/source/js/services/timeline.js +1 -0
  113. package/layout/_plugins/lazyload.ejs +0 -21
  114. package/source/css/_components/tag-plugins/common.styl +0 -120
  115. package/source/css/_defines/theme.styl +0 -120
package/_config.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  ######## Stellar info ########
2
2
  stellar:
3
- version: '1.32.0'
3
+ version: '1.32.2'
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
@@ -334,19 +334,30 @@ footer:
334
334
  # icon: '<img src="https://gcore.jsdelivr.net/gh/cdn-x/placeholder@1.0.12/social/942ebbf1a4b91.svg"/>'
335
335
  # url: /about/#comments
336
336
  sitemap:
337
- # '博客':
338
- # - '[近期](/)'
339
- # - '[分类](/)'
340
- # - '[标签](/)'
341
- # - '[归档](/)'
342
- # '项目':
343
- # - '[开源库](/)'
344
- # '社交':
345
- # - '[友链](/)'
346
- # - '[留言板](/)'
347
- # '更多':
348
- # - '[关于本站](/)'
349
- # - '[GitHub](/)'
337
+ # - title: 博客
338
+ # items:
339
+ # - '[近期发布](/)'
340
+ # - '[分类](/blog/categories/)'
341
+ # - '[标签](/blog/tags/)'
342
+ # - '[归档](/blog/archives/)'
343
+ # - title: 项目
344
+ # items:
345
+ # - '[xxx](/)'
346
+ # - '[xxx](/)'
347
+ # - '[xxx](/)'
348
+ # - '[xxx](/)'
349
+ # - title: 社交
350
+ # items:
351
+ # - '[xxx](/)'
352
+ # - '[xxx](/)'
353
+ # - '[xxx](/)'
354
+ # - '[xxx](/)'
355
+ # - title: 关于
356
+ # items:
357
+ # - '[xxx](/)'
358
+ # - '[xxx](/)'
359
+ # - '[xxx](/)'
360
+ # - '[xxx](/)'
350
361
  content: | # 支持 Markdown 格式
351
362
  本站由 [{author.name}](/) 使用 [{theme.name} {theme.version}]({theme.tree}) 主题创建。
352
363
  本博客所有文章除特别声明外,均采用 [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) 许可协议,转载请注明出处。
@@ -434,7 +445,10 @@ tag_plugins:
434
445
  dependencies:
435
446
  jquery: https://gcore.jsdelivr.net/npm/jquery@3.7/dist/jquery.min.js
436
447
  marked: https://gcore.jsdelivr.net/npm/marked@13.0/lib/marked.umd.min.js
437
-
448
+ lazyload:
449
+ js: https://gcore.jsdelivr.net/npm/vanilla-lazyload@19.1/dist/lazyload.min.js
450
+ transition: fade # blur, fade
451
+ fix_ratio: true # true / false
438
452
 
439
453
  # 内置服务,按需加载(页面内用到了就会加载,没有用到就不会加载)
440
454
  data_services:
@@ -498,17 +512,6 @@ plugins:
498
512
  service: flying_pages # flying_pages
499
513
  flying_pages: https://gcore.jsdelivr.net/npm/flying-pages@2/flying-pages.min.js
500
514
 
501
- # lazyload
502
- # https://www.npmjs.com/package/vanilla-lazyload
503
- lazyload:
504
- enable: true # [hexo clean && hexo s] is required after changing this value.
505
- js: https://gcore.jsdelivr.net/npm/vanilla-lazyload@19.1/dist/lazyload.min.js
506
- transition: fade # blur, fade
507
- fix_ratio: true # true / false # 是否固定宽高比,true 时会在图片上添加 style="aspect-ratio: 16/9;",false 时则不添加,开启此功能会导致增加首次 hexo s 的耗时
508
- loading_image:
509
- src: # https://github.githubassets.com/images/mona-loading-dark.gif
510
- size: 2rem # 图片宽度
511
-
512
515
  # https://fancyapps.com/docs/ui/fancybox/
513
516
  # available for {% image xxx %}
514
517
  fancybox:
@@ -530,11 +533,11 @@ plugins:
530
533
 
531
534
  # https://scrollrevealjs.org/api/reveal.html
532
535
  scrollreveal:
533
- enable: #true
536
+ enable: #true # 慎用,有些时候打开页面空白
534
537
  js: https://gcore.jsdelivr.net/npm/scrollreveal@4.0/dist/scrollreveal.min.js
535
- distance: 16px
536
- duration: 800 # ms
537
- interval: 100 # ms
538
+ distance: 8px
539
+ duration: 500 # ms
540
+ interval: 50 # ms
538
541
  scale: 1 # 0.1~1
539
542
 
540
543
  # AI 摘要
@@ -608,17 +611,16 @@ plugins:
608
611
 
609
612
  style:
610
613
  prefers_theme: auto # auto / light / dark
611
- smooth_scroll: false # true / false 开启时如果目录过长可能无法准确定位
614
+ smooth_scroll: true # true / false 开启时如果目录过长可能无法准确定位
612
615
  font-size:
613
616
  root: 16px # 改这个会影响全局所有文字的字号
614
617
  body: 17px # 影响正文区域的字号,如果改成 px 则不受 root 影响
615
618
  code: 85% # 相较于其所在行的文本大小,建议用百分比
616
619
  codeblock: 0.8125rem # 13px
617
620
  font-family:
618
- logo: 'system-ui, "Microsoft Yahei", "Segoe UI", -apple-system, Roboto, Ubuntu, "Helvetica Neue", Arial, "WenQuanYi Micro Hei", sans-serif'
619
- body: 'system-ui, "Microsoft Yahei", "Segoe UI", -apple-system, Roboto, Ubuntu, "Helvetica Neue", Arial, "WenQuanYi Micro Hei", sans-serif'
620
- code: 'Menlo, Monaco, Consolas, system-ui, "Courier New", monospace, sans-serif'
621
- codeblock: 'Menlo, Monaco, Consolas, system-ui, "Courier New", monospace, sans-serif'
621
+ body: 'system-ui, "Microsoft Yahei", "Segoe UI", Arial, sans-serif'
622
+ code: 'Menlo, Monaco, Consolas, system-ui, monospace, sans-serif'
623
+ codeblock: 'Menlo, Monaco, Consolas, system-ui, monospace, sans-serif'
622
624
  text-align: left
623
625
  prefix:
624
626
  h2: https://api.iconify.design/ci:heading-h2.svg
@@ -654,18 +656,20 @@ style:
654
656
  start: 'linear-gradient(to right, hsl(215, 95%, 64%), hsl(195, 95%, 60%), hsl(165, 95%, 56%), hsl(165, 95%, 56%), hsl(195 95% 60%), hsl(215, 95%, 64%))'
655
657
  leftbar:
656
658
  # 可以设置:纯色/渐变色/图片作为背景
657
- # background: var(--leftbar-bg)
659
+ background-color: var(--card) # var(--block)
658
660
  background-image: url(https://gcore.jsdelivr.net/gh/cdn-x/placeholder@1.0.13/image/sidebar-bg1@small.jpg)
659
661
  blur-px: 100px
660
- blur-bg: var(--alpha60)
662
+ blur-bg: var(--bg-a60)
663
+ background-opacity: 0.8
661
664
  paginator:
662
665
  prev: https://gcore.jsdelivr.net/gh/cdn-x/placeholder@1.0.12/arrow/f049bbd4e88ec.svg
663
666
  next: https://gcore.jsdelivr.net/gh/cdn-x/placeholder@1.0.12/arrow/064b95430caf4.svg
664
667
  error_page: https://gcore.jsdelivr.net/gh/cdn-x/placeholder@1.0.12/404/1c830bfcd517d.svg
665
- site:
666
- background-image: #'url(https://gcore.jsdelivr.net/gh/cdn-x/placeholder@1.0.14/image/site-bg1@small.webp)' # 未完全适配,慎用
668
+ site: # 使用 background-image 后,blur 设置才有效
669
+ # background-image: 'url(https://gcore.jsdelivr.net/gh/cdn-x/placeholder@1.0.14/image/site-bg1@small.webp)'
667
670
  blur-px: 100px
668
- blur-bg: var(--alpha75)
671
+ blur-bg: var(--bg-a75)
672
+ blur-sat: 300%
669
673
  # 技术文章内页各级标题前面的符号
670
674
  header_prefix:
671
675
  h2: '⌘'
@@ -682,7 +686,9 @@ default:
682
686
  project: https://gcore.jsdelivr.net/gh/cdn-x/placeholder@1.0.12/image/2779789.png
683
687
  banner: https://gcore.jsdelivr.net/gh/cdn-x/placeholder@1.0.12/banner/books.jpg
684
688
  topic: https://gcore.jsdelivr.net/gh/cdn-x/placeholder@1.0.12/image/10433048.png
685
- image_onerror: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E"
689
+ loading: https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd
690
+ # loading: https://github.githubassets.com/images/mona-loading-dark.gif
691
+ image_onerror: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E"
686
692
 
687
693
  api_host:
688
694
  ghapi: api.github.com
@@ -28,11 +28,7 @@ function div() {
28
28
  }
29
29
  el += '<div class="l_cover post' + scrollreveal(' ') + '">';
30
30
  el += '<div class="cover">';
31
- if (theme.plugins.lazyload && theme.plugins.lazyload.enable) {
32
- el += '<div class="lazy img bg" data-bg="' + url + '"></div>';
33
- } else {
34
- el += '<div class="lazy img bg" style="background-image:url(&quot;' + url + '&quot;)"></div>';
35
- }
31
+ el += '<div class="lazy img bg" data-bg="' + url + '"></div>';
36
32
  el += '</div>';
37
33
  el += '</div>';
38
34
  return el;
@@ -18,7 +18,7 @@ function layoutWikiCover() {
18
18
  if (coverpage === true) {
19
19
  coverpage = ['cover', 'title', 'description'];
20
20
  }
21
- el += '<div class="l_cover wiki' + scrollreveal(' ') + '">';
21
+ el += '<div class="l_cover wiki">';
22
22
  el += '<article class="cover-wrap md-text">';
23
23
 
24
24
  if (cover?.length > 0 && coverpage.includes('cover')) {
@@ -5,15 +5,25 @@ function layoutDiv() {
5
5
  el += `<footer class="page-footer${scrollreveal(' ')} footnote">`
6
6
  el += '<hr>'
7
7
  // sitemap
8
- if (theme.footer.sitemap && Object.keys(theme.footer.sitemap).length > 0) {
9
- el += '<div class="sitemap">'
10
- for (let group of Object.keys(theme.footer.sitemap)) {
11
- let items = theme.footer.sitemap[group]
8
+ const sitemap = theme.footer.sitemap
9
+ if (sitemap?.length > 0) {
10
+ var count = sitemap.length
11
+ if ([6].includes(count)) {
12
+ count = 3
13
+ } else if ([7,8].includes(count)) {
14
+ count = 4
15
+ } else {
16
+ count = Math.min(sitemap.length, 5)
17
+ }
18
+ el += `<div class="sitemap" style="column-count:${count};">`
19
+ for (let group of sitemap) {
20
+ let title = group.title
21
+ let items = group.items
12
22
  if (items == undefined || items.length == 0) {
13
23
  continue
14
24
  }
15
25
  el += '<div class="sitemap-group">'
16
- el += '<span class="fs15">' + group + '</span>'
26
+ el += '<span class="fs15">' + title + '</span>'
17
27
  items.forEach((item, i) => {
18
28
  el += '<a href="' + url_for(md_link(item)) + '">'
19
29
  el += __(md_text(item))
@@ -107,7 +107,7 @@ function layoutDiv() {
107
107
  const top = layoutBreadcrumb()
108
108
  var style = ``
109
109
  var el = ``
110
- el += `<div class="article banner top">`
110
+ el += `<div class="article banner${scrollreveal(' ')} top">`
111
111
  if (banner.url?.length > 0) {
112
112
  el += `<img class="bg" src="${banner.url}">`
113
113
  if (banner.color) {
@@ -5,7 +5,7 @@ function layoutDiv() {
5
5
  return ''
6
6
  }
7
7
  return `
8
- <div class="right ghrepo ds-ghinfo" api="https://${theme.api_host.ghapi}/repos/${repo}">
8
+ <div class="right ghrepo data-service data-service ds-ghinfo" api="https://${theme.api_host.ghapi}/repos/${repo}">
9
9
  <a class="repo-link bold" href="https://github.com/${repo}">
10
10
  ${icon('github:repo')}
11
11
  <span type="text">${repo}</span>
@@ -33,10 +33,12 @@
33
33
  const def = {
34
34
  avatar: `<%- theme.default.avatar %>`,
35
35
  cover: `<%- theme.default.cover %>`,
36
+ loading: `<%- theme.default.loading %>`,
36
37
  };
37
38
  const deps = {
38
39
  jquery: `<%- url_for(theme.dependencies.jquery) %>`,
39
- marked: `<%- url_for(theme.dependencies.marked) %>`
40
+ marked: `<%- url_for(theme.dependencies.marked) %>`,
41
+ lazyload: `<%- url_for(theme.dependencies.lazyload) %>`
40
42
  }
41
43
 
42
44
 
@@ -0,0 +1,67 @@
1
+ <script async src="<%- url_for(theme.dependencies.lazyload.js) %>"></script>
2
+ <script>
3
+ // https://www.npmjs.com/package/vanilla-lazyload
4
+ // Set the options globally
5
+ // to make LazyLoad self-initialize
6
+ window.lazyLoadOptions = {
7
+ elements_selector: ".lazy",
8
+ callback_loaded: (el) => {
9
+ el.classList.add('loaded');
10
+ const wrapper = el.closest('.lazy-box');
11
+ const icon = wrapper?.querySelector('.lazy-icon');
12
+ if (icon) icon.remove();
13
+ }
14
+ };
15
+ // Listen to the initialization event
16
+ // and get the instance of LazyLoad
17
+ window.addEventListener(
18
+ "LazyLoad::Initialized",
19
+ function (event) {
20
+ window.lazyLoadInstance = event.detail.instance;
21
+ },
22
+ false
23
+ );
24
+ document.addEventListener('DOMContentLoaded', function () {
25
+ window.lazyLoadInstance?.update();
26
+ });
27
+
28
+ window.wrapLazyloadImages = (container) => {
29
+ if (typeof container === 'string') {
30
+ container = document.querySelector(container);
31
+ }
32
+ if (!container) return;
33
+
34
+ const images = container.querySelectorAll('img');
35
+ images.forEach((img) => {
36
+ if (img.classList.contains('lazy')) return;
37
+
38
+ const src = img.getAttribute('src');
39
+ if (!src) return;
40
+
41
+ const wrapper = document.createElement('div');
42
+ wrapper.className = 'lazy-box';
43
+
44
+ const newImg = img.cloneNode();
45
+ newImg.removeAttribute('src');
46
+ newImg.setAttribute('data-src', src);
47
+ newImg.classList.add('lazy');
48
+
49
+ const icon = document.createElement('div');
50
+ icon.className = 'lazy-icon';
51
+ if (def.loading) {
52
+ icon.style.backgroundImage = `url("${def.loading}")`;
53
+ }
54
+
55
+ wrapper.appendChild(newImg);
56
+ wrapper.appendChild(icon);
57
+
58
+ img.replaceWith(wrapper);
59
+ });
60
+
61
+ // 通知 LazyLoad 更新
62
+ if (window.lazyLoadInstance?.update) {
63
+ window.lazyLoadInstance.update();
64
+ }
65
+ }
66
+
67
+ </script>
@@ -1,4 +1,5 @@
1
1
  <script type="text/javascript">
2
+
2
3
  function RunItem() {
3
4
  this.list = []; // 存放回调函数
4
5
  this.start = () => {
@@ -19,6 +19,7 @@ function custom_inject() {
19
19
  <%- partial('scripts/utils') %>
20
20
  <%- partial('scripts/sidebar') %>
21
21
  <%- partial('scripts/tagtree') %>
22
+ <%- partial('scripts/lazyload') %>
22
23
 
23
24
  <!-- required -->
24
25
  <script src="<%- url_for(`${theme.stellar.main_js}?v=${stellar_info('version')}`) %>" defer></script>
@@ -62,7 +62,7 @@ if (typeof page.leftbar == 'string') {
62
62
  function layoutTitle(main, url, sub) {
63
63
  var el = '';
64
64
  el += '<a class="title" href="' + url_for(url || "/") + '">';
65
- el += '<div class="main" ff="title">' + main + '</div>';
65
+ el += '<div class="main">' + main + '</div>';
66
66
  if (sub) {
67
67
  let arr = sub.split('|');
68
68
  if (arr.length > 1) {
@@ -23,7 +23,7 @@ if (page.logo) {
23
23
  function layoutTitle(main, url, sub) {
24
24
  var el = ''
25
25
  el += `<a class="title" href="${url_for(url || "/")}">`
26
- el += `<div class="main" ff="title">${main}</div>`
26
+ el += `<div class="main">${main}</div>`
27
27
  if (sub) {
28
28
  const arr = sub.split('|')
29
29
  if (arr.length > 1) {
@@ -21,7 +21,7 @@ function layoutDiv() {
21
21
  el += `<div class="avatar"><img type="img" id="avatar_url" src="${author.avatar}"></div>`
22
22
  }
23
23
  // username
24
- el += `<p class="username" ff="title" type="text" id="name">${author.name}</p>`
24
+ el += `<p class="username" type="text" id="name">${author.name}</p>`
25
25
  el += `<p class="bio" type="text" id="bio">${author.desc || author.description}</p>`
26
26
 
27
27
  // homepage
@@ -26,7 +26,7 @@ function layoutDiv() {
26
26
  el += '</div>';
27
27
  }
28
28
  el += '<div class="widget-body fs14">';
29
- el += '<div class="tag-plugin timeline ds-timeline"';
29
+ el += '<div class="tag-plugin timeline data-service ds-timeline"';
30
30
  ['api', 'user'].forEach(key => {
31
31
  if (item[key]) {
32
32
  el += ' ' + key + '="' + item[key] + '"';
@@ -19,7 +19,7 @@ function layoutDiv() {
19
19
  el += `<widget class="widget-wrapper${scrollreveal(' ')} ghrepo">`
20
20
  // body
21
21
  el += '<div class="widget-body">';
22
- el += '<div class="items ds-ghinfo" api="https://' + theme.api_host.ghapi + '/repos/' + repo + '">';
22
+ el += '<div class="items data-service ds-ghinfo" api="https://' + theme.api_host.ghapi + '/repos/' + repo + '">';
23
23
  el += '<a class="repo" href="https://github.com/' + repo + '" target="_blank" rel="external nofollow noopener noreferrer">';
24
24
  el += '<div class="repo-name flex-row">';
25
25
  el += icon('github:repo')
@@ -35,7 +35,7 @@ function layoutDiv() {
35
35
  el += icon('github:fork')
36
36
  el += '<span type="text" id="forks_count"></span>';
37
37
  el += '</div>';
38
- el += '<div class="flex-row ds-ghinfo" index="0" api="https://' + theme.api_host.ghapi + '/repos/' + repo + '/tags">';
38
+ el += '<div class="flex-row data-service ds-ghinfo" index="0" api="https://' + theme.api_host.ghapi + '/repos/' + repo + '/tags">';
39
39
  el += icon('github:tag')
40
40
  el += '<span type="text" id="latest-tag-name">0</span>';
41
41
  el += '</div>';
@@ -24,12 +24,12 @@ function layoutDiv() {
24
24
 
25
25
  el += `<widget class="widget-wrapper${scrollreveal(' ')} user-card ghuser">`
26
26
  // body
27
- el += '<div class="widget-body ds-ghinfo" api="https://' + theme.api_host.ghapi + '/users/' + item.username + '">';
27
+ el += '<div class="widget-body data-service ds-ghinfo" api="https://' + theme.api_host.ghapi + '/users/' + item.username + '">';
28
28
  if (item.avatar) {
29
29
  el += '<div class="avatar" ><img no-lazy type="img" id="avatar_url" src="' + config.avatar + '"></div>';
30
30
  }
31
31
  // username
32
- el += '<p class="username" ff="title" type="text" id="name">&nbsp;</p>';
32
+ el += '<p class="username" type="text" id="name">&nbsp;</p>';
33
33
  el += '<p class="bio" type="text" id="bio">&nbsp;</p>';
34
34
 
35
35
  el += '<div class="buttons">';
@@ -13,7 +13,7 @@ function layoutDiv() {
13
13
  el += markdown(item.content);
14
14
  }
15
15
  if (item.src) {
16
- el += `<div class="ds-mdrender" src="${item.src}"></div>`
16
+ el += `<div class="data-service ds-mdrender" src="${item.src}"></div>`
17
17
  }
18
18
  if (item.linklist) {
19
19
  el += partial('components/linklist', {item: item.linklist})
@@ -1,6 +1,6 @@
1
1
  <%
2
2
  function layoutDiv() {
3
- var el = `<widget class="widget-wrapper${scrollreveal(' ')} post-list">`
3
+ var el = `<widget class="widget-wrapper${scrollreveal(' ')} recent post-list">`
4
4
  // header
5
5
  el += '<div class="widget-header dis-select">';
6
6
  el += '<span class="name">' + __("meta.recent_update") + '</span>';
@@ -8,7 +8,7 @@ function relatedPostsInTopic() {
8
8
  return ''
9
9
  }
10
10
  var el = ''
11
- el += `<widget class="widget-wrapper${scrollreveal(' ')} post-list">`
11
+ el += `<widget class="widget-wrapper related post-list">`
12
12
  el += `<div class="widget-header dis-select">`
13
13
  el += `<span class="name">${__('btn.topic') + __('symbol.colon') + (topic.name || topic.title)}</span>`
14
14
  el += `</div>`
@@ -11,7 +11,7 @@ function layoutDiv() {
11
11
  el += '</div>';
12
12
  }
13
13
  el += '<div class="widget-body fs14">';
14
- el += `<div class="tag-plugin timeline ds-${(item.type || 'timeline')}"`;
14
+ el += `<div class="tag-plugin timeline data-service ds-${(item.type || 'timeline')}"`;
15
15
  ['api', 'user', 'hide', 'limit'].forEach(key => {
16
16
  if (item[key]) {
17
17
  el += ' ' + key + '="' + item[key] + '"';
@@ -60,7 +60,7 @@ function layoutDiv(fallback) {
60
60
  }
61
61
  }
62
62
  if (el.trim().length > 0) {
63
- return `<widget class="widget-wrapper${scrollreveal(' ')} post-list">${el}</widget>`
63
+ return `<widget class="widget-wrapper doc-tree post-list">${el}</widget>`
64
64
  } else {
65
65
  return ''
66
66
  }
@@ -9,8 +9,8 @@
9
9
  opacity: 0,
10
10
  easing: "ease-out"
11
11
  };
12
- ScrollReveal().reveal('.l_left .slide-up', slideUp);
13
- ScrollReveal().reveal('.l_main .slide-up', slideUp);
14
- ScrollReveal().reveal('.l_right .slide-up', slideUp);
12
+ ScrollReveal({ container: '.widgets' }).reveal('.slide-up', { ...slideUp });
13
+ ScrollReveal({ container: '.l_main' }).reveal('.slide-up', { ...slideUp });
14
+ ScrollReveal({ container: '.l_right' }).reveal('.slide-up', { ...slideUp });
15
15
  });
16
16
  </script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hexo-theme-stellar",
3
- "version": "1.32.0",
3
+ "version": "1.32.2",
4
4
  "description": "Elegant and powerful theme for Hexo.",
5
5
  "main": "package.json",
6
6
  "scripts": {
@@ -10,25 +10,44 @@ let cache = fs.existsSync(outputPath)
10
10
  ? JSON.parse(fs.readFileSync(outputPath))
11
11
  : {};
12
12
 
13
- // 提取 Markdown 中的图片链接
14
- function extractImageUrls(content) {
15
- const urls = [];
13
+ // 提取 Markdown 中的图片链接并处理已有 ratio
14
+ function extractImageUrlsAndCacheRatio(content, relative, ctx) {
15
+ const urlsToProbe = [];
16
16
  const tagImgRegex = /{%\s+image\s+[^%]*?\b(https?:\/\/[^\s%]+)[^%]*?%}/g;
17
17
 
18
+ if (!cache[relative]) cache[relative] = {};
19
+
18
20
  let match;
19
21
  while ((match = tagImgRegex.exec(content)) !== null) {
20
- urls.push(match[1]);
22
+ const fullTag = match[0];
23
+ const url = match[1];
24
+
25
+ // ratio:xxx 或 ratio:xxx/yyy
26
+ const ratioMatch = fullTag.match(/ratio:([0-9./]+)/);
27
+ const ratioStr = ratioMatch ? ratioMatch[1] : null;
28
+
29
+ if (cache[relative][url]) {
30
+ // 已有缓存,跳过
31
+ continue;
32
+ }
33
+
34
+ if (ratioStr) {
35
+ cache[relative][url] = ratioStr;
36
+ // 🧠 实时写入
37
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
38
+ fs.writeFileSync(outputPath, JSON.stringify(cache, null, 2));
39
+ } else {
40
+ urlsToProbe.push(url);
41
+ }
21
42
  }
22
43
 
23
- return urls;
44
+ return urlsToProbe;
24
45
  }
25
46
 
26
-
27
- // 远程图片尺寸探测
47
+ // 探测远程图片尺寸
28
48
  async function getImageRatio(ctx, url) {
29
- if (!url.startsWith('http')) {
30
- return null;
31
- }
49
+ if (!url.startsWith('http')) return null;
50
+
32
51
  try {
33
52
  const result = await probe(url);
34
53
  return `${result.width}/${result.height}`;
@@ -38,46 +57,49 @@ async function getImageRatio(ctx, url) {
38
57
  }
39
58
  }
40
59
 
41
-
42
60
  // 主逻辑
43
61
  module.exports = async (ctx, options) => {
44
62
  const cacheExists = fs.existsSync(outputPath);
45
- if (cacheExists) {
46
- ctx.log.info('正在获取图片长宽比。缓存已存在,开始增量更新...');
47
- } else {
48
- ctx.log.info('正在获取图片长宽比。首次可能耗时较久,请耐心等待...');
49
- }
63
+ ctx.log.info(
64
+ cacheExists
65
+ ? '正在获取图片长宽比。缓存已存在,开始增量更新...'
66
+ : '正在获取图片长宽比。首次可能耗时较久,请耐心等待...'
67
+ );
50
68
 
51
69
  const mdFiles = glob.sync('source/**/*.md');
52
70
 
53
71
  for (const file of mdFiles) {
54
72
  const relative = path.relative(process.cwd(), file);
55
73
  const content = fs.readFileSync(file, 'utf8');
56
- const imageUrls = extractImageUrls(content);
57
- const currentUrls = new Set(imageUrls);
58
74
 
59
- // 初始化或清理旧数据
75
+ // 初始化文件级缓存(提取前先建好结构)
60
76
  if (!cache[relative]) {
61
77
  cache[relative] = {};
62
- } else {
63
- for (const oldUrl of Object.keys(cache[relative])) {
64
- if (!currentUrls.has(oldUrl)) {
65
- delete cache[relative][oldUrl];
66
- }
78
+ }
79
+
80
+ const imageUrls = extractImageUrlsAndCacheRatio(content, relative, ctx);
81
+ const currentUrls = new Set([
82
+ ...imageUrls,
83
+ ...Object.keys(cache[relative])
84
+ ]);
85
+
86
+ // 清理已被 Markdown 中移除的旧记录
87
+ for (const oldUrl of Object.keys(cache[relative])) {
88
+ if (!currentUrls.has(oldUrl)) {
89
+ delete cache[relative][oldUrl];
67
90
  }
68
91
  }
69
92
 
70
- // 探测新图片
93
+ // 探测未缓存图片
71
94
  for (const url of imageUrls) {
72
- if (cache[relative][url]) {
73
- continue;
74
- }
95
+ if (cache[relative][url]) continue;
96
+
75
97
  const ratio = await getImageRatio(ctx, url);
76
98
  if (ratio) {
77
99
  cache[relative][url] = ratio;
78
- ctx.log.info(`已添加: ${url} → ${ratio}`);
100
+ ctx.log.info(`✅ 探测添加: ${url} → ${ratio}`);
79
101
 
80
- // 🧠 实时写入(增量更新,支持中断恢复)
102
+ // 实时落盘
81
103
  fs.mkdirSync(path.dirname(outputPath), { recursive: true });
82
104
  fs.writeFileSync(outputPath, JSON.stringify(cache, null, 2));
83
105
  }
@@ -85,4 +107,4 @@ module.exports = async (ctx, options) => {
85
107
  }
86
108
 
87
109
  ctx.log.info('[image-ratios.json] 生成完成');
88
- };
110
+ };
@@ -40,7 +40,7 @@ hexo.extend.tag.register('navbar', require('./lib/navbar')(hexo))
40
40
  hexo.extend.tag.register('note', require('./lib/note')(hexo))
41
41
  hexo.extend.tag.register('poetry', require('./lib/poetry')(hexo), true)
42
42
  hexo.extend.tag.register('quot', require('./lib/quot')(hexo))
43
- hexo.extend.tag.register('quote', require('./lib/quote')(hexo), true)
43
+ hexo.extend.tag.register('blockquote', require('./lib/blockquote')(hexo), true)
44
44
  hexo.extend.tag.register('hashtag', require('./lib/hashtag')(hexo))
45
45
  hexo.extend.tag.register('okr', require('./lib/okr')(hexo), {ends: true})
46
46
  hexo.extend.tag.register('audio', require('./lib/audio')(hexo))