hexo-theme-stellar 1.32.4 → 1.33.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 (84) hide show
  1. package/_config.yml +22 -7
  2. package/_data/icons.yml +8 -1
  3. package/languages/en.yml +1 -1
  4. package/languages/zh-CN.yml +1 -1
  5. package/languages/zh-TW.yml +1 -1
  6. package/layout/_partial/comments/artalk/script.ejs +7 -6
  7. package/layout/_partial/head.ejs +7 -1
  8. package/layout/_partial/main/article/contributors.ejs +1 -1
  9. package/layout/_partial/main/article/read_next.ejs +2 -2
  10. package/layout/_partial/main/navbar/article_banner.ejs +1 -1
  11. package/layout/_partial/main/navbar/breadcrumb/blog.ejs +3 -3
  12. package/layout/_partial/main/navbar/breadcrumb/note.ejs +2 -2
  13. package/layout/_partial/main/navbar/breadcrumb/page.ejs +1 -1
  14. package/layout/_partial/main/navbar/breadcrumb/wiki.ejs +2 -2
  15. package/layout/_partial/main/navbar/dateinfo.ejs +1 -1
  16. package/layout/_partial/main/navbar/ghinfo.ejs +1 -1
  17. package/layout/_partial/main/navbar/nav_tabs_blog.ejs +12 -12
  18. package/layout/_partial/main/navbar/nav_tabs_wiki.ejs +2 -2
  19. package/layout/_partial/main/notebook/note_tags.ejs +1 -1
  20. package/layout/_partial/scripts/lazyload.ejs +1 -1
  21. package/layout/_partial/scripts/utils.ejs +69 -39
  22. package/layout/_partial/sidebar/index_leftbar.ejs +1 -1
  23. package/layout/_partial/sidebar/menu.ejs +1 -1
  24. package/layout/_partial/widgets/components/link.ejs +1 -1
  25. package/layout/_partial/widgets/ghissues.ejs +7 -1
  26. package/layout/_partial/widgets/ghrepo.ejs +2 -2
  27. package/layout/_partial/widgets/ghuser.ejs +1 -1
  28. package/layout/_partial/widgets/recent.ejs +1 -1
  29. package/layout/_partial/widgets/related.ejs +2 -2
  30. package/layout/_partial/widgets/tagtree.ejs +1 -1
  31. package/layout/_partial/widgets/timeline.ejs +7 -1
  32. package/layout/_partial/widgets/tree.ejs +1 -1
  33. package/layout/archive.ejs +1 -1
  34. package/layout/categories.ejs +1 -1
  35. package/layout/index.ejs +1 -1
  36. package/layout/index_topic.ejs +1 -1
  37. package/layout/index_wiki.ejs +2 -2
  38. package/layout/notebooks.ejs +1 -1
  39. package/layout/notes.ejs +1 -1
  40. package/layout/tags.ejs +1 -1
  41. package/package.json +1 -1
  42. package/scripts/events/index.js +1 -1
  43. package/scripts/events/lib/config.js +7 -1
  44. package/scripts/filters/pretty_urls.js +25 -0
  45. package/scripts/helpers/json_ld.js +5 -4
  46. package/scripts/helpers/pretty_url.js +22 -0
  47. package/scripts/tags/index.js +2 -0
  48. package/scripts/tags/lib/albums.js +1 -1
  49. package/scripts/tags/lib/chat.js +1 -1
  50. package/scripts/tags/lib/friends.js +1 -1
  51. package/scripts/tags/lib/link.js +1 -1
  52. package/scripts/tags/lib/posters.js +1 -1
  53. package/scripts/tags/lib/rating.js +32 -0
  54. package/scripts/tags/lib/sites.js +1 -1
  55. package/scripts/tags/lib/timeline.js +2 -1
  56. package/scripts/tags/lib/vote.js +28 -0
  57. package/source/css/_common/button.styl +1 -0
  58. package/source/css/_components/pages/article-story.styl +2 -0
  59. package/source/css/_components/tag-plugins/image.styl +1 -0
  60. package/source/css/_components/tag-plugins/rating.styl +39 -0
  61. package/source/css/_components/tag-plugins/timeline.styl +5 -5
  62. package/source/css/_components/tag-plugins/vote.styl +49 -0
  63. package/source/css/_components/widgets/timeline.styl +1 -1
  64. package/source/css/_defines/theme_base.styl +1 -0
  65. package/source/css/_plugins/comments/artalk.styl +51 -11
  66. package/source/css/_plugins/lazyload.styl +3 -0
  67. package/source/js/main.js +12 -5
  68. package/source/js/search/local-search.js +3 -1
  69. package/source/js/services/artalk_latest_comment.js +1 -1
  70. package/source/js/services/contributors.js +1 -1
  71. package/source/js/services/fcircle.js +1 -1
  72. package/source/js/services/friends.js +1 -1
  73. package/source/js/services/friends_and_posts.js +1 -1
  74. package/source/js/services/ghinfo.js +1 -1
  75. package/source/js/services/giscus_latest_comment.js +1 -1
  76. package/source/js/services/memos.js +1 -1
  77. package/source/js/services/rating.js +142 -0
  78. package/source/js/services/siteinfo.js +1 -1
  79. package/source/js/services/sites.js +1 -1
  80. package/source/js/services/timeline.js +1 -1
  81. package/source/js/services/twikoo_latest_comment.js +1 -1
  82. package/source/js/services/vote.js +113 -0
  83. package/source/js/services/waline_latest_comment.js +1 -1
  84. package/source/js/services/weibo.js +1 -1
package/_config.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  ######## Stellar info ########
2
2
  stellar:
3
- version: '1.32.4'
3
+ version: '1.33.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
@@ -392,12 +392,12 @@ tag_plugins:
392
392
  prefix: ph:seal-question-fill
393
393
  # {% emoji %}
394
394
  emoji:
395
- default: https://gcore.jsdelivr.net/gh/cdn-x/emoji/qq/{name}.gif
395
+ default: https://gcore.jsdelivr.net/gh/cdn-x/emoji@2.1.2/qq/{name}.gif
396
396
  twemoji: https://gcore.jsdelivr.net/gh/twitter/twemoji/assets/svg/{name}.svg
397
- qq: https://gcore.jsdelivr.net/gh/cdn-x/emoji/qq/{name}.gif
398
- aru: https://gcore.jsdelivr.net/gh/cdn-x/emoji/aru-l/{name}.gif
399
- tieba: https://gcore.jsdelivr.net/gh/cdn-x/emoji/tieba/{name}.png
400
- blobcat: https://gcore.jsdelivr.net/gh/norevi/waline-blobcatemojis@1.0/blobs/{name}.png
397
+ qq: https://gcore.jsdelivr.net/gh/cdn-x/emoji@2.1.2/qq/{name}.gif
398
+ aru: https://gcore.jsdelivr.net/gh/cdn-x/emoji@2.1.2/aru/{name}.gif
399
+ tieba: https://gcore.jsdelivr.net/gh/cdn-x/emoji@2.1.2/tieba/{name}.png
400
+ blobcat: https://gcore.jsdelivr.net/gh/cdn-x/emoji@2.1.2/blobcat/{name}.gif
401
401
  icon:
402
402
  # 留空时,图标和文字颜色相同
403
403
  default_color: accent # theme, accent, red, orange, yellow, green, cyan, blue, purple
@@ -471,6 +471,14 @@ data_services:
471
471
  api:
472
472
  ghinfo:
473
473
  js: /js/services/ghinfo.js
474
+ # 打分
475
+ rating:
476
+ js: /js/services/rating.js
477
+ api: https://star-vote.xaox.cc/api/rating
478
+ # 投票
479
+ vote:
480
+ js: /js/services/vote.js
481
+ api: https://star-vote.xaox.cc/api/vote
474
482
  # 网格布局类
475
483
  sites:
476
484
  js: /js/services/sites.js
@@ -708,4 +716,11 @@ api_host:
708
716
  ghraw: raw.githubusercontent.com
709
717
  gist: gist.github.com
710
718
  # https://github.com/anuraghazra/github-readme-stats
711
- ghcard: github-readme-stats.vercel.app
719
+ ghcard: github-readme-stats.vercel.app
720
+
721
+
722
+
723
+ # 非资深hexo开发者尽量不要动这块
724
+ system:
725
+ # hexo 支持输出的链接格式众多,维护成本高,为了减少出错,统一成推荐的格式。
726
+ override_pretty_urls: true
package/_data/icons.yml CHANGED
@@ -49,4 +49,11 @@ share:link: https://gcore.jsdelivr.net/gh/cdn-x/placeholder@1.0.12/social/8411ed
49
49
  # quot icons
50
50
  ph:seal-question-fill: <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 256 256"><path fill="currentColor" d="M225.86 102.82c-3.77-3.94-7.67-8-9.14-11.57c-1.36-3.27-1.44-8.69-1.52-13.94c-.15-9.76-.31-20.82-8-28.51s-18.75-7.85-28.51-8c-5.25-.08-10.67-.16-13.94-1.52c-3.56-1.47-7.63-5.37-11.57-9.14C146.28 23.51 138.44 16 128 16s-18.27 7.51-25.18 14.14c-3.94 3.77-8 7.67-11.57 9.14c-3.25 1.36-8.69 1.44-13.94 1.52c-9.76.15-20.82.31-28.51 8s-7.8 18.75-8 28.51c-.08 5.25-.16 10.67-1.52 13.94c-1.47 3.56-5.37 7.63-9.14 11.57C23.51 109.72 16 117.56 16 128s7.51 18.27 14.14 25.18c3.77 3.94 7.67 8 9.14 11.57c1.36 3.27 1.44 8.69 1.52 13.94c.15 9.76.31 20.82 8 28.51s18.75 7.85 28.51 8c5.25.08 10.67.16 13.94 1.52c3.56 1.47 7.63 5.37 11.57 9.14c6.9 6.63 14.74 14.14 25.18 14.14s18.27-7.51 25.18-14.14c3.94-3.77 8-7.67 11.57-9.14c3.27-1.36 8.69-1.44 13.94-1.52c9.76-.15 20.82-.31 28.51-8s7.85-18.75 8-28.51c.08-5.25.16-10.67 1.52-13.94c1.47-3.56 5.37-7.63 9.14-11.57c6.63-6.9 14.14-14.74 14.14-25.18s-7.51-18.27-14.14-25.18M128 192a12 12 0 1 1 12-12a12 12 0 0 1-12 12m8-48.72v.72a8 8 0 0 1-16 0v-8a8 8 0 0 1 8-8c13.23 0 24-9 24-20s-10.77-20-24-20s-24 9-24 20v4a8 8 0 0 1-16 0v-4c0-19.85 17.94-36 40-36s40 16.15 40 36c0 17.38-13.76 31.93-32 35.28"/></svg>
51
51
  bxs:quote-left: <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from BoxIcons Solid by Atisa - https://creativecommons.org/licenses/by/4.0/ --><path fill="currentColor" d="M3.691 6.292C5.094 4.771 7.217 4 10 4h1v2.819l-.804.161c-1.37.274-2.323.813-2.833 1.604A2.9 2.9 0 0 0 6.925 10H10a1 1 0 0 1 1 1v7c0 1.103-.897 2-2 2H3a1 1 0 0 1-1-1v-5l.003-2.919c-.009-.111-.199-2.741 1.688-4.789M20 20h-6a1 1 0 0 1-1-1v-5l.003-2.919c-.009-.111-.199-2.741 1.688-4.789C16.094 4.771 18.217 4 21 4h1v2.819l-.804.161c-1.37.274-2.323.813-2.833 1.604A2.9 2.9 0 0 0 17.925 10H21a1 1 0 0 1 1 1v7c0 1.103-.897 2-2 2"/></svg>
52
- bxs:quote-right: <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from BoxIcons Solid by Atisa - https://creativecommons.org/licenses/by/4.0/ --><path fill="currentColor" d="M20.309 17.708C22.196 15.66 22.006 13.03 22 13V5a1 1 0 0 0-1-1h-6c-1.103 0-2 .897-2 2v7a1 1 0 0 0 1 1h3.078a2.9 2.9 0 0 1-.429 1.396c-.508.801-1.465 1.348-2.846 1.624l-.803.16V20h1c2.783 0 4.906-.771 6.309-2.292m-11.007 0C11.19 15.66 10.999 13.03 10.993 13V5a1 1 0 0 0-1-1h-6c-1.103 0-2 .897-2 2v7a1 1 0 0 0 1 1h3.078a2.9 2.9 0 0 1-.429 1.396c-.508.801-1.465 1.348-2.846 1.624l-.803.16V20h1c2.783 0 4.906-.771 6.309-2.292"/></svg>
52
+ bxs:quote-right: <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from BoxIcons Solid by Atisa - https://creativecommons.org/licenses/by/4.0/ --><path fill="currentColor" d="M20.309 17.708C22.196 15.66 22.006 13.03 22 13V5a1 1 0 0 0-1-1h-6c-1.103 0-2 .897-2 2v7a1 1 0 0 0 1 1h3.078a2.9 2.9 0 0 1-.429 1.396c-.508.801-1.465 1.348-2.846 1.624l-.803.16V20h1c2.783 0 4.906-.771 6.309-2.292m-11.007 0C11.19 15.66 10.999 13.03 10.993 13V5a1 1 0 0 0-1-1h-6c-1.103 0-2 .897-2 2v7a1 1 0 0 0 1 1h3.078a2.9 2.9 0 0 1-.429 1.396c-.508.801-1.465 1.348-2.846 1.624l-.803.16V20h1c2.783 0 4.906-.771 6.309-2.292"/></svg>
53
+
54
+ # vote
55
+ vote:thumbsup: <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16"><!-- Icon from Codicons by Microsoft Corporation - https://github.com/microsoft/vscode-codicons/blob/main/LICENSE --><path fill="currentColor" d="M1 8.5v5c0 .827.673 1.5 1.5 1.5H4V7H2.5C1.673 7 1 7.673 1 8.5m13.571-1.685A1.98 1.98 0 0 0 12.959 6H9.526l.464-2.32a2.24 2.24 0 0 0-.464-1.86A2.23 2.23 0 0 0 7.794 1c-.444 0-.839.298-.951.693l-.464 1.286a16 16 0 0 1-1.378 2.876V15h6.397a1.99 1.99 0 0 0 1.909-1.404l1.562-5a1.98 1.98 0 0 0-.298-1.781"/></svg>
56
+ vote:thumbsdown: <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16"><!-- Icon from Codicons by Microsoft Corporation - https://github.com/microsoft/vscode-codicons/blob/main/LICENSE --><path fill="currentColor" d="M15 7.5v-5c0-.827-.673-1.5-1.5-1.5H12v8h1.5c.827 0 1.5-.673 1.5-1.5M1.429 9.185c.38.518.969.815 1.612.815h3.433l-.464 2.32a2.24 2.24 0 0 0 .464 1.86c.427.521 1.059.82 1.732.82c.444 0 .839-.298.95-.693l.465-1.286a16 16 0 0 1 1.378-2.876V1H4.602a1.99 1.99 0 0 0-1.91 1.404l-1.561 5a1.98 1.98 0 0 0 .297 1.781"/></svg>
57
+
58
+ # rating
59
+ rating:star: <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 48 48"><!-- Icon from Fluent UI System Color Icons by Microsoft Corporation - https://github.com/microsoft/fluentui-system-icons/blob/main/LICENSE --><g fill="none"><path fill="url(#fluentColorStar480)" d="M21.803 6.086c.899-1.821 3.495-1.821 4.394 0l4.852 9.832l10.85 1.576c2.01.293 2.813 2.762 1.358 4.18l-7.85 7.653l1.853 10.806c.343 2.001-1.758 3.528-3.555 2.583L24 37.614l-9.705 5.102c-1.797.945-3.898-.582-3.555-2.583l1.854-10.806l-7.851-7.654c-1.455-1.417-.652-3.886 1.357-4.178l10.85-1.577z"/><defs><linearGradient id="fluentColorStar480" x1="43.995" x2="2.879" y1="43.856" y2="5.054" gradientUnits="userSpaceOnUse"><stop stop-color="#FF6F47"/><stop offset="1" stop-color="#FFCD0F"/></linearGradient></defs></g></svg>
package/languages/en.yml CHANGED
@@ -36,7 +36,7 @@ meta:
36
36
  updated: 'Updated on'
37
37
  license: License
38
38
  share: Share
39
- contributors: Contributors
39
+ contributors: Page Contributors
40
40
  date_suffix:
41
41
  just: Just
42
42
  min: minutes ago
@@ -36,7 +36,7 @@ meta:
36
36
  updated: 更新于
37
37
  license: 许可协议
38
38
  share: 分享文章
39
- contributors: 贡献者
39
+ contributors: 本文贡献者
40
40
  date_suffix:
41
41
  just: 刚刚
42
42
  min: 分钟前
@@ -36,7 +36,7 @@ meta:
36
36
  updated: 更新於
37
37
  license: 授權條款
38
38
  share: 分享文章
39
- contributors: 貢獻者
39
+ contributors: 本文貢獻者
40
40
  date_suffix:
41
41
  just: 剛剛
42
42
  min: 分鐘前
@@ -7,9 +7,10 @@
7
7
  utils.css('<%- theme.comments.artalk.css %>');
8
8
  utils.js('<%- theme.comments.artalk.js %>', {defer: true}).then(function () {
9
9
  // 监听 Artalk 事件
10
- Artalk.use((ctx) => {
11
- ctx.on('list-loaded', () => window.lazyLoadInstance?.update())
12
- })
10
+ // FIXME: Artalk开启了懒加载会导致评论输入预览框里的图也变成data-src,导致看不到图
11
+ // Artalk.use((ctx) => {
12
+ // ctx.on('list-loaded', () => window.lazyLoadInstance?.update())
13
+ // })
13
14
  const path = el.getAttribute('comment_id') ?? decodeURI(window.location.pathname);
14
15
  const artalk = Artalk.init({
15
16
  el: '#artalk_container',
@@ -23,16 +24,16 @@
23
24
  let headers = new Headers();
24
25
  headers.set('Accept', 'application/json');
25
26
  <% if (theme.comments.artalk.imageUploader?.token) { %>
26
- headers.set('Authorization', '<%= theme.comments.artalk.imageUploader?.token %>')
27
+ headers.set('Authorization', '<%- theme.comments.artalk.imageUploader?.token %>')
27
28
  <% } %>
28
29
  let formData = new FormData();
29
30
  formData.append('file', file);
30
- return fetch('<%= theme.comments.artalk.imageUploader?.api %>',{
31
+ return fetch('<%- theme.comments.artalk.imageUploader?.api %>',{
31
32
  method: 'POST',
32
33
  body: formData,
33
34
  headers: headers
34
35
  }).then((resp) => resp.json())
35
- .then((resp) => resp.<%= theme.comments.artalk.imageUploader?.resp %>)
36
+ .then((resp) => resp.<%- theme.comments.artalk.imageUploader?.resp %>)
36
37
  },
37
38
  <% } %>
38
39
  });
@@ -95,7 +95,13 @@ function custom_inject() {
95
95
  function generate_canonical() {
96
96
  const originalHost = theme.canonical.originalHost || '';
97
97
  if (originalHost.length > 0) {
98
- const path = url_for(page.path).replace(/index\.html$/, '') || '/';
98
+ var path = pretty_url(page.path) || '/';
99
+ if (path.startsWith('/404') || path.startsWith('404')) {
100
+ return '';
101
+ }
102
+ if (path.endsWith('.html') && path.length > 5) {
103
+ path = path.split('.html')[0];
104
+ }
99
105
  return `<link rel="canonical" href="https://${originalHost}${path}">`;
100
106
  }
101
107
  return '';
@@ -24,7 +24,7 @@ function layoutDiv() {
24
24
  </a>
25
25
  </div>
26
26
  <div class="body">
27
- <div class="ds-contributors users-wrap" api-type="commits" api="${commits}"><div class="grid-box"></div></div>
27
+ <div class="ds-contributors users-wrap" api-type="commits" data-api="${commits}"><div class="grid-box"></div></div>
28
28
  </div>
29
29
  </section>
30
30
  `
@@ -39,7 +39,7 @@ function layoutDiv() {
39
39
  el += '<div class="item" id="prev">';
40
40
  if (prev) {
41
41
  el += '<div class="note">' + title_prev + '</div>';
42
- el += '<a href="' + url_for(prev.path) + '">';
42
+ el += '<a href="' + pretty_url(prev.path) + '">';
43
43
  el += prev.title || prev.wiki || date(prev.date, config.date_format);
44
44
  el += '</a>';
45
45
  }
@@ -48,7 +48,7 @@ function layoutDiv() {
48
48
  el += '<div class="item" id="next">';
49
49
  if (next) {
50
50
  el += '<div class="note">' + title_next + '</div>';
51
- el += '<a href="' + url_for(next.path) + '">';
51
+ el += '<a href="' + pretty_url(next.path) + '">';
52
52
  el += next.title || next.wiki || date(next.date, config.date_format);
53
53
  el += '</a>';
54
54
  }
@@ -31,7 +31,7 @@ function layoutBreadcrumb() {
31
31
  // 3.left.top: 面包屑导航
32
32
  el += `<div class="flex-row" id="breadcrumb">`
33
33
  // 首页
34
- el += `<a class="cap breadcrumb" href="${url_for(config.root)}">${__("btn.home")}</a>`
34
+ el += `<a class="cap breadcrumb" href="${pretty_url(config.root)}">${__("btn.home")}</a>`
35
35
  if (theme.wiki.tree[page.wiki]) {
36
36
  el += partial('breadcrumb/wiki')
37
37
  } else if (page.notebook) {
@@ -4,11 +4,11 @@ function layoutDiv() {
4
4
  var el = ''
5
5
  if (topicObject) { // 专栏文章
6
6
  // 专栏列表页
7
- const topicIndexPageURL = url_for(theme.site_tree.index_topic.base_dir)
7
+ const topicIndexPageURL = pretty_url(theme.site_tree.index_topic.base_dir)
8
8
  el += `<span class="sep"></span>`
9
9
  el += `<a class="cap breadcrumb" id="menu" href="${topicIndexPageURL}">${__("btn.topic")}</a>`
10
10
  // 专栏项目名
11
- const topicHomePageURL = url_for(topicObject.homepage?.path);
11
+ const topicHomePageURL = pretty_url(topicObject.homepage?.path);
12
12
  if (topicHomePageURL != topicIndexPageURL) {
13
13
  el += `<span class="sep"></span>`
14
14
  el += `<a class="cap breadcrumb" id="proj" href="${topicHomePageURL}">${topicObject.name || topicObject.title}</a>`
@@ -16,7 +16,7 @@ function layoutDiv() {
16
16
  } else { // 普通文章
17
17
  // 文章列表页
18
18
  el += `<span class="sep"></span>`
19
- el += `<a class="cap breadcrumb" href="${url_for(config.index_generator.path)}">${__("btn.blog")}</a>`
19
+ el += `<a class="cap breadcrumb" href="${pretty_url(config.index_generator.path)}">${__("btn.blog")}</a>`
20
20
  // 分类列表页
21
21
  if (page.categories?.length > 0) {
22
22
  el += `<span class="sep"></span>`
@@ -1,8 +1,8 @@
1
1
  <%# 笔记页面的面包屑 %>
2
2
  <span class="sep"></span>
3
- <a class="cap breadcrumb" href="<%= url_for(theme.site_tree.notebooks.base_dir) %>"><%= __('btn.notebook') %></a>
3
+ <a class="cap breadcrumb" href="<%= pretty_url(theme.site_tree.notebooks.base_dir) %>"><%= __('btn.notebook') %></a>
4
4
  <% const notebook = theme.notebooks.tree[page.notebook] %>
5
5
  <% if (notebook) { %>
6
6
  <span class="sep"></span>
7
- <a class="cap breadcrumb" href="<%= url_for(notebook.base_dir) %>"><%= notebook.name || notebook.title %></a>
7
+ <a class="cap breadcrumb" href="<%= pretty_url(notebook.base_dir) %>"><%= notebook.name || notebook.title %></a>
8
8
  <% } %>
@@ -3,7 +3,7 @@ function layoutDiv() {
3
3
  // 普通页面就显示当前页面
4
4
  return `
5
5
  <span class="sep"></span>
6
- <a class="cap breadcrumb" href="${url_for(page.path)}">${page.title}</a>
6
+ <a class="cap breadcrumb" href="${pretty_url(page.path)}">${page.title}</a>
7
7
  `
8
8
  }
9
9
  %>
@@ -4,11 +4,11 @@ function layoutDiv() {
4
4
  var el = ''
5
5
  // 文档列表页
6
6
  el += `<span class="sep"></span>`
7
- const wikiIndexPageURL = url_for(theme.site_tree.index_wiki.base_dir)
7
+ const wikiIndexPageURL = pretty_url(theme.site_tree.index_wiki.base_dir)
8
8
  el += `<a class="cap breadcrumb" id="menu" href="${wikiIndexPageURL}">${__("btn.wiki")}</a>`
9
9
  if (wikiObject) {
10
10
  // 文档项目名
11
- const wikiHomePageURL = url_for(wikiObject.homepage?.path);
11
+ const wikiHomePageURL = pretty_url(wikiObject.homepage?.path);
12
12
  if (wikiHomePageURL != wikiIndexPageURL) {
13
13
  el += `<span class="sep"></span>`
14
14
  el += `<a class="cap breadcrumb" id="proj" href="${wikiHomePageURL}">${wikiObject.name || wikiObject.title}</a>`
@@ -21,7 +21,7 @@ function layoutDiv() {
21
21
  } else {
22
22
  const author = theme.authors ? (theme.authors[page.author] || theme.default_author) : null
23
23
  if (author) {
24
- el += `<a class="author" href="${url_for(author.path)}">${author.name}</a>`
24
+ el += `<a class="author" href="${pretty_url(author.path)}">${author.name}</a>`
25
25
  el += `<span class="sep"></span>`
26
26
  }
27
27
  // 发布日期
@@ -5,7 +5,7 @@ function layoutDiv() {
5
5
  return ''
6
6
  }
7
7
  return `
8
- <div class="right ghrepo data-service data-service ds-ghinfo" api="https://${theme.api_host.ghapi}/repos/${repo}">
8
+ <div class="right ghrepo data-service data-service ds-ghinfo" data-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>
@@ -6,44 +6,44 @@ function layoutDiv() {
6
6
  el += '<div class="navbar-container">';
7
7
  el += '<nav class="post">';
8
8
  if (is_home()) {
9
- el += '<a class="active" href="' + url_for(config.index_generator.path) + '">' + __("btn.recent_publish") + '</a>';
9
+ el += '<a class="active" href="' + pretty_url(config.index_generator.path) + '">' + __("btn.recent_publish") + '</a>';
10
10
  } else {
11
- el += '<a href="' + url_for(config.index_generator.path) + '">' + __("btn.recent_publish") + '</a>';
11
+ el += '<a href="' + pretty_url(config.index_generator.path) + '">' + __("btn.recent_publish") + '</a>';
12
12
  }
13
13
 
14
14
  if (site.categories && site.categories.length > 0) {
15
15
  if (page.category) {
16
- el += '<a class="active" href="' + url_for(config.category_dir) + '/">' + __("btn.category") + __("symbol.colon") + page.category + '</a>';
16
+ el += '<a class="active" href="' + pretty_url(config.category_dir) + '">' + __("btn.category") + __("symbol.colon") + page.category + '</a>';
17
17
  } else if (page.layout == "categories") {
18
- el += '<a class="active" href="' + url_for(config.category_dir) + '/">' + __("btn.categories") + '</a>';
18
+ el += '<a class="active" href="' + pretty_url(config.category_dir) + '">' + __("btn.categories") + '</a>';
19
19
  } else {
20
- el += '<a href="' + url_for(config.category_dir) + '/">' + __("btn.categories") + '</a>';
20
+ el += '<a href="' + pretty_url(config.category_dir) + '">' + __("btn.categories") + '</a>';
21
21
  }
22
22
  }
23
23
 
24
24
  if (site.tags && site.tags.length > 0) {
25
25
  if (page.tag) {
26
- el += '<a class="active" href="' + url_for(config.tag_dir) + '/">' + __("btn.tag") + __("symbol.colon") + page.tag + '</a>';
26
+ el += '<a class="active" href="' + pretty_url(config.tag_dir) + '">' + __("btn.tag") + __("symbol.colon") + page.tag + '</a>';
27
27
  } else if (page.layout == "tags") {
28
- el += '<a class="active" href="' + url_for(config.tag_dir) + '/">' + __("btn.tags") + '</a>';
28
+ el += '<a class="active" href="' + pretty_url(config.tag_dir) + '">' + __("btn.tags") + '</a>';
29
29
  } else {
30
- el += '<a href="' + url_for(config.tag_dir) + '/">' + __("btn.tags") + '</a>';
30
+ el += '<a href="' + pretty_url(config.tag_dir) + '">' + __("btn.tags") + '</a>';
31
31
  }
32
32
  }
33
33
 
34
34
  if (theme.topic?.publish_list?.length > 0) {
35
35
  if (page.layout == 'index_topic') {
36
- el += '<a class="active" href="' + url_for(theme.site_tree.index_topic.base_dir) + '/">' + __("btn.topic") + '</a>';
36
+ el += '<a class="active" href="' + pretty_url(theme.site_tree.index_topic.base_dir) + '">' + __("btn.topic") + '</a>';
37
37
  } else {
38
- el += '<a href="' + url_for(theme.site_tree.index_topic.base_dir) + '/">' + __("btn.topic") + '</a>';
38
+ el += '<a href="' + pretty_url(theme.site_tree.index_topic.base_dir) + '">' + __("btn.topic") + '</a>';
39
39
  }
40
40
  }
41
41
 
42
42
  if (site.posts && site.posts.length > 0) {
43
43
  if (is_archive()) {
44
- el += '<a class="active" href="' + url_for(config.archive_dir) + '/">' + __("btn.archives") + '</a>';
44
+ el += '<a class="active" href="' + pretty_url(config.archive_dir) + '">' + __("btn.archives") + '</a>';
45
45
  } else {
46
- el += '<a href="' + url_for(config.archive_dir) + '/">' + __("btn.archives") + '</a>';
46
+ el += '<a href="' + pretty_url(config.archive_dir) + '">' + __("btn.archives") + '</a>';
47
47
  }
48
48
  }
49
49
 
@@ -10,7 +10,7 @@ function layoutDiv() {
10
10
  if (page.filter === false) {
11
11
  el += ' class="active"';
12
12
  }
13
- el += ' href="' + url_for(theme.site_tree.index_wiki.base_dir) + '">' + __("btn.all_wiki") + '</a>';
13
+ el += ' href="' + pretty_url(theme.site_tree.index_wiki.base_dir) + '">' + __("btn.all_wiki") + '</a>';
14
14
  el += '</a>';
15
15
  // 项目分类
16
16
  const { shelf, all_tags } = theme.wiki;
@@ -22,7 +22,7 @@ function layoutDiv() {
22
22
  if (tag.name && tag.name.length > 0 && page.tagName === tag.name) {
23
23
  el += ' class="active"';
24
24
  }
25
- el += ' href="' + url_for(tag.path) + '">' + tag.name + '</a>';
25
+ el += ' href="' + pretty_url(tag.path) + '">' + tag.name + '</a>';
26
26
  el += '</a>';
27
27
  }
28
28
  }
@@ -3,7 +3,7 @@
3
3
  <div class="tag-list">
4
4
  <% for (const t of page.tags) { %>
5
5
  <% const tag = notebook.tagTree.get(t.toLowerCase()) %>
6
- <a class="tag" href="<%= url_for(tag.path) %>"><%= tag.name %></a>
6
+ <a class="tag" href="<%= pretty_url(tag.path) %>"><%= tag.name %></a>
7
7
  <% } %>
8
8
  </div>
9
9
  <% } %>
@@ -4,7 +4,7 @@
4
4
  // Set the options globally
5
5
  // to make LazyLoad self-initialize
6
6
  window.lazyLoadOptions = {
7
- elements_selector: ".lazy, .lazyload",
7
+ elements_selector: ".lazy",
8
8
  callback_loaded: (el) => {
9
9
  el.classList.add('loaded');
10
10
  const wrapper = el.closest('.lazy-box');
@@ -150,52 +150,82 @@
150
150
  }
151
151
  },
152
152
  request: (el, url, callback, onFailure) => {
153
- let retryTimes = 3;
154
- utils.onLoading(el);
155
- function req() {
156
- return new Promise((resolve, reject) => {
157
- let status = 0; // 0 等待 1 完成 2 超时
158
- let timer = setTimeout(() => {
159
- if (status === 0) {
160
- status = 2;
161
- timer = null;
153
+ const maxRetry = 3;
154
+ let retryCount = 0;
155
+
156
+ return new Promise((resolve, reject) => {
157
+ const load = () => {
158
+ utils.onLoading?.(el);
159
+
160
+ let timedOut = false;
161
+ const timeout = setTimeout(() => {
162
+ timedOut = true;
163
+ console.warn('[request] 超时:', url);
164
+
165
+ if (++retryCount >= maxRetry) {
166
+ utils.onLoadFailure?.(el);
167
+ onFailure?.();
162
168
  reject('请求超时');
163
- if (retryTimes == 0 && onFailure) {
164
- onFailure();
165
- }
169
+ } else {
170
+ setTimeout(load, 1000);
166
171
  }
167
172
  }, 5000);
168
- fetch(url).then(function (response) {
169
- if (status !== 2) {
170
- clearTimeout(timer);
171
- resolve(response);
172
- timer = null;
173
- status = 1;
174
- }
175
- if (response.ok) {
176
- return response;
177
- }
178
- throw new Error('Network response was not ok.');
179
- }).then(function (data) {
180
- retryTimes = 0;
181
- utils.onLoadSuccess(el);
173
+
174
+ fetch(url).then(resp => {
175
+ if (timedOut) return;
176
+ clearTimeout(timeout);
177
+
178
+ if (!resp.ok) throw new Error('响应失败');
179
+ return resp;
180
+ }).then(data => {
181
+ if (timedOut) return;
182
+ utils.onLoadSuccess?.(el);
182
183
  callback(data);
183
- }).catch(function (error) {
184
- if (retryTimes > 0) {
185
- retryTimes -= 1;
186
- setTimeout(() => {
187
- req();
188
- }, 5000);
184
+ resolve(data);
185
+ }).catch(err => {
186
+ clearTimeout(timeout);
187
+ console.warn('[request] 错误:', err);
188
+
189
+ if (++retryCount >= maxRetry) {
190
+ utils.onLoadFailure?.(el);
191
+ onFailure?.();
192
+ reject(err);
189
193
  } else {
190
- utils.onLoadFailure(el);
191
- if (onFailure) {
192
- onFailure();
193
- }
194
+ setTimeout(load, 1000);
194
195
  }
195
196
  });
196
- });
197
- }
198
- req();
197
+ };
198
+
199
+ load();
200
+ });
201
+ },
202
+ requestWithoutLoading: (url, options = {}, maxRetry = 2, timeout = 5000) => {
203
+ return new Promise((resolve, reject) => {
204
+ let retryCount = 0;
205
+
206
+ const tryRequest = () => {
207
+ let timedOut = false;
208
+ const timer = setTimeout(() => {
209
+ timedOut = true;
210
+ if (++retryCount > maxRetry) reject('timeout');
211
+ else tryRequest();
212
+ }, timeout);
213
+
214
+ fetch(url, options)
215
+ .then(resp => {
216
+ clearTimeout(timer);
217
+ if (!resp.ok) throw new Error('bad response');
218
+ resolve(resp);
219
+ })
220
+ .catch(err => {
221
+ clearTimeout(timer);
222
+ if (++retryCount > maxRetry) reject(err);
223
+ else setTimeout(tryRequest, 500);
224
+ });
225
+ };
226
+
227
+ tryRequest();
228
+ });
199
229
  },
200
230
  /********************** requestAnimationFrame ********************************/
201
231
  // 1、requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。
@@ -61,7 +61,7 @@ if (typeof page.leftbar == 'string') {
61
61
 
62
62
  function layoutTitle(main, url, sub) {
63
63
  var el = '';
64
- el += '<a class="title" href="' + url_for(url || "/") + '">';
64
+ el += '<a class="title" href="' + pretty_url(url || "/") + '">';
65
65
  el += '<div class="main">' + main + '</div>';
66
66
  if (sub) {
67
67
  let arr = sub.split('|');
@@ -6,7 +6,7 @@ function layoutDiv() {
6
6
  if (item == undefined || item.id == null || item.url == null) {
7
7
  continue
8
8
  }
9
- el += `<a class="nav-item${item.id == page.menu_id ? ' active' : ''}" title="${item.title}" href="${url_for(item.url)}"`
9
+ el += `<a class="nav-item${item.id == page.menu_id ? ' active' : ''}" title="${item.title}" href="${pretty_url(item.url)}"`
10
10
  if (item.theme?.length > 0) {
11
11
  el += ` style="color:${item.theme}"`
12
12
  }
@@ -1,7 +1,7 @@
1
1
  <%
2
2
  function layoutDiv() {
3
3
  var el = ''
4
- const isActive = url_for(item.url) == url_for(page.path)
4
+ const isActive = pretty_url(item.url) == pretty_url(page.path)
5
5
  el += `<a class="link${isActive ? ' active' : ''}" title="${item.title}" href="${item.url}">`
6
6
  el += `<div class="flex">`
7
7
  if (item.icon) {
@@ -27,7 +27,13 @@ function layoutDiv() {
27
27
  }
28
28
  el += '<div class="widget-body fs14">';
29
29
  el += '<div class="tag-plugin timeline data-service ds-timeline"';
30
- ['api', 'user'].forEach(key => {
30
+ // dataset
31
+ ['api'].forEach(key => {
32
+ if (item[key]) {
33
+ el += ' data-' + key + '="' + item[key] + '"';
34
+ }
35
+ });
36
+ ['user'].forEach(key => {
31
37
  if (item[key]) {
32
38
  el += ' ' + key + '="' + item[key] + '"';
33
39
  }
@@ -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 data-service ds-ghinfo" api="https://' + theme.api_host.ghapi + '/repos/' + repo + '">';
22
+ el += '<div class="items data-service ds-ghinfo" data-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 data-service 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" data-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,7 +24,7 @@ function layoutDiv() {
24
24
 
25
25
  el += `<widget class="widget-wrapper${scrollreveal(' ')} user-card ghuser">`
26
26
  // body
27
- el += '<div class="widget-body data-service ds-ghinfo" api="https://' + theme.api_host.ghapi + '/users/' + item.username + '">';
27
+ el += '<div class="widget-body data-service ds-ghinfo" data-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
  }
@@ -34,7 +34,7 @@ function layoutDiv() {
34
34
  arr.length = item.limit
35
35
  arr.forEach(post => {
36
36
  if (!post) { return }
37
- el += `<a class="item title" href="${url_for(post.link || post.path)}">`
37
+ el += `<a class="item title" href="${pretty_url(post.link || post.path)}">`
38
38
  el += '<span class="title">'
39
39
  if (post.wiki) {
40
40
  const proj = theme.wiki.tree[post.wiki];
@@ -15,7 +15,7 @@ function relatedPostsInTopic() {
15
15
  el += `<div class="widget-body">`
16
16
  for (let post of topic.pages) {
17
17
  const isActive = post.path == page.path
18
- el += `<a class="item${isActive ? ' active' : ''}" href="${url_for(post.path)}">`
18
+ el += `<a class="item${isActive ? ' active' : ''}" href="${pretty_url(post.path)}">`
19
19
  el += `<span class="title">${post.title}</span>`
20
20
  if (isActive) {
21
21
  el += icon('default:bookmark.active')
@@ -42,7 +42,7 @@ function relatedWiki() {
42
42
  for (let id of relatedItem.items) {
43
43
  // 同一个分组中的其它项目
44
44
  let item = theme.wiki.tree[id]
45
- el += `<a class="item wiki" href="${url_for(item.homepage?.path)}">`
45
+ el += `<a class="item wiki" href="${pretty_url(item.homepage?.path)}">`
46
46
  el += `<span class="title">${item.title}</span>`
47
47
  if (item.description && item.description.length > 0) {
48
48
  el += `<span class="excerpt">${item.description}</span>`
@@ -9,7 +9,7 @@
9
9
 
10
10
  <% function layoutTag(tag, level) { %>
11
11
  <% const active = page.activeTag === tag.id %>
12
- <a class="link<%= active ? ' active' : '' %>" href="<%= url_for(tag.path) %>">
12
+ <a class="link<%= active ? ' active' : '' %>" href="<%= pretty_url(tag.path) %>">
13
13
  <span class="toc-text"<% if (level > 0) { %> style="padding-left: <%= level * 0.875 %>rem;"<% } %>>
14
14
  <% if (tag.id === '') { %>
15
15
  <%= __('meta.all_notes') %>