hexo-theme-stellar 1.32.3 → 1.32.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/_config.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  ######## Stellar info ########
2
2
  stellar:
3
- version: '1.32.3'
3
+ version: '1.32.4'
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
@@ -503,6 +503,11 @@ data_services:
503
503
  js: /js/services/artalk_latest_comment.js
504
504
  giscus:
505
505
  js: /js/services/giscus_latest_comment.js
506
+ contributors:
507
+ edit_this_page: # 从开头开始匹配替换
508
+ '_posts/': # https://github.com/xaoxuu/xaoxuu.com/blob/main/source/_posts/
509
+ 'wiki/stellar/': https://github.com/xaoxuu/hexo-theme-stellar-docs/blob/main/
510
+ js: /js/services/contributors.js
506
511
 
507
512
 
508
513
  # 扩展插件接入方法:(插件名下面用 #plugin# 代替)
package/_data/icons.yml CHANGED
@@ -30,7 +30,7 @@ default:calendar: <svg style="margin-bottom:2px" xmlns="http://www.w3.org/2000/s
30
30
  default:category: <svg style="margin-bottom:1px" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M2 6.95c0-.883 0-1.324.07-1.692A4 4 0 0 1 5.257 2.07C5.626 2 6.068 2 6.95 2c.386 0 .58 0 .766.017a4 4 0 0 1 2.18.904c.144.119.28.255.554.529L11 4c.816.816 1.224 1.224 1.712 1.495a4 4 0 0 0 .848.352C14.098 6 14.675 6 15.828 6h.374c2.632 0 3.949 0 4.804.77c.079.07.154.145.224.224c.77.855.77 2.172.77 4.804V14c0 3.771 0 5.657-1.172 6.828C19.657 22 17.771 22 14 22h-4c-3.771 0-5.657 0-6.828-1.172C2 19.657 2 17.771 2 14z" opacity=".5"/><path fill="currentColor" d="M20 6.238c0-.298-.005-.475-.025-.63a3 3 0 0 0-2.583-2.582C17.197 3 16.965 3 16.5 3H9.988c.116.104.247.234.462.45L11 4c.816.816 1.224 1.224 1.712 1.495a4 4 0 0 0 .849.352C14.098 6 14.675 6 15.829 6h.373c1.78 0 2.957 0 3.798.238"/><path fill="currentColor" fill-rule="evenodd" d="M12.25 10a.75.75 0 0 1 .75-.75h5a.75.75 0 0 1 0 1.5h-5a.75.75 0 0 1-.75-.75" clip-rule="evenodd"/></svg>
31
31
 
32
32
  # 编辑本文
33
- default:edit: <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --><g fill="none"><path stroke="currentColor" stroke-linecap="round" stroke-width="1.5" d="m16.652 3.455l.649-.649A2.753 2.753 0 0 1 21.194 6.7l-.65.649m-3.892-3.893s.081 1.379 1.298 2.595c1.216 1.217 2.595 1.298 2.595 1.298m-3.893-3.893L10.687 9.42c-.404.404-.606.606-.78.829q-.308.395-.524.848c-.121.255-.211.526-.392 1.068L8.412 13.9m12.133-6.552l-2.983 2.982m-2.982 2.983c-.404.404-.606.606-.829.78a4.6 4.6 0 0 1-.848.524c-.255.121-.526.211-1.068.392l-1.735.579m0 0l-1.123.374a.742.742 0 0 1-.939-.94l.374-1.122m1.688 1.688L8.412 13.9"/><path fill="currentColor" d="M22.75 12a.75.75 0 0 0-1.5 0zM12 2.75a.75.75 0 0 0 0-1.5zM7.376 20.013a.75.75 0 1 0-.752 1.298zm-4.687-2.638a.75.75 0 1 0 1.298-.75zM21.25 12A9.25 9.25 0 0 1 12 21.25v1.5c5.937 0 10.75-4.813 10.75-10.75zM12 1.25C6.063 1.25 1.25 6.063 1.25 12h1.5A9.25 9.25 0 0 1 12 2.75zM6.624 21.311A10.7 10.7 0 0 0 12 22.75v-1.5a9.2 9.2 0 0 1-4.624-1.237zM1.25 12a10.7 10.7 0 0 0 1.439 5.375l1.298-.75A9.2 9.2 0 0 1 2.75 12z"/></g></svg>
33
+ default:edit: <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --><circle cx="12" cy="12" r="10" fill="currentColor" opacity=".5"/><path fill="currentColor" d="M13.926 14.302c.245-.191.467-.413.912-.858l5.54-5.54c.134-.134.073-.365-.106-.427a6.1 6.1 0 0 1-2.3-1.449a6.1 6.1 0 0 1-1.45-2.3c-.061-.18-.292-.24-.426-.106l-5.54 5.54c-.445.444-.667.667-.858.912a5 5 0 0 0-.577.932c-.133.28-.233.579-.431 1.175l-.257.77l-.409 1.226l-.382 1.148a.817.817 0 0 0 1.032 1.033l1.15-.383l1.224-.408l.77-.257c.597-.199.895-.298 1.175-.432q.498-.237.933-.576m8.187-8.132a3.028 3.028 0 0 0-4.282-4.283l-.179.178a.73.73 0 0 0-.206.651c.027.15.077.37.168.633a4.9 4.9 0 0 0 1.174 1.863a4.9 4.9 0 0 0 1.862 1.174c.263.09.483.141.633.168c.24.043.48-.035.652-.207z"/></svg>
34
34
  default:upup: <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="1.5"><path stroke-linejoin="round" d="m9 15.5l3-3l3 3m-6-4l3-3l3 3"/><path d="M7 3.338A9.95 9.95 0 0 1 12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12c0-1.821.487-3.53 1.338-5"/></g></svg>
35
35
  default:tocomment: <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="1.5" d="M8 10.5h8M8 14h5.5M17 3.338A9.95 9.95 0 0 0 12 2C6.477 2 2 6.477 2 12c0 1.6.376 3.112 1.043 4.453c.178.356.237.763.134 1.148l-.595 2.226a1.3 1.3 0 0 0 1.591 1.592l2.226-.596a1.63 1.63 0 0 1 1.149.133A9.96 9.96 0 0 0 12 22c5.523 0 10-4.477 10-10c0-1.821-.487-3.53-1.338-5"/></svg>
36
36
  default:theme: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="currentColor" fill-rule="evenodd" d="M582.4 326.4c-140.8 0-256 115.2-256 256s115.2 256 256 256 256-115.2 256-256-115.2-256-256-256z m0 448c-70.4 0-131.2-36.8-164.8-92.8 12.8 3.2 27.2 4.8 40 4.8 121.6 0 219.2-99.2 219.2-219.2 0-17.6-1.6-35.2-6.4-52.8 60.8 32 102.4 96 102.4 169.6 1.6 104-84.8 190.4-190.4 190.4zM582.4 262.4c17.6 0 32-14.4 32-32v-128c0-17.6-14.4-32-32-32s-32 14.4-32 32v128c0 17.6 14.4 32 32 32zM262.4 582.4c0-17.6-14.4-32-32-32h-128c-17.6 0-32 14.4-32 32s14.4 32 32 32h128c17.6 0 32-14.4 32-32zM310.4 356.8c6.4 6.4 14.4 9.6 22.4 9.6 8 0 16-3.2 22.4-9.6 12.8-12.8 12.8-32 0-44.8l-91.2-91.2c-12.8-12.8-32-12.8-44.8 0-12.8 12.8-12.8 32 0 44.8l91.2 91.2zM944 220.8c-12.8-12.8-32-12.8-44.8 0l-91.2 91.2c-12.8 12.8-12.8 32 0 44.8 6.4 6.4 14.4 9.6 22.4 9.6 8 0 16-3.2 22.4-9.6l91.2-91.2c12.8-12.8 12.8-33.6 0-44.8zM310.4 808l-91.2 91.2c-12.8 12.8-12.8 32 0 44.8 6.4 6.4 14.4 9.6 22.4 9.6 8 0 16-3.2 22.4-9.6l91.2-91.2c12.8-12.8 12.8-32 0-44.8-11.2-11.2-32-11.2-44.8 0z"></path></svg>
package/languages/en.yml CHANGED
@@ -36,6 +36,7 @@ meta:
36
36
  updated: 'Updated on'
37
37
  license: License
38
38
  share: Share
39
+ contributors: Contributors
39
40
  date_suffix:
40
41
  just: Just
41
42
  min: minutes ago
@@ -36,6 +36,7 @@ meta:
36
36
  updated: 更新于
37
37
  license: 许可协议
38
38
  share: 分享文章
39
+ contributors: 贡献者
39
40
  date_suffix:
40
41
  just: 刚刚
41
42
  min: 分钟前
@@ -36,6 +36,7 @@ meta:
36
36
  updated: 更新於
37
37
  license: 授權條款
38
38
  share: 分享文章
39
+ contributors: 貢獻者
39
40
  date_suffix:
40
41
  just: 剛剛
41
42
  min: 分鐘前
@@ -33,7 +33,7 @@
33
33
  headers: headers
34
34
  }).then((resp) => resp.json())
35
35
  .then((resp) => resp.<%= theme.comments.artalk.imageUploader?.resp %>)
36
- },
36
+ },
37
37
  <% } %>
38
38
  });
39
39
  });
@@ -95,12 +95,8 @@ function custom_inject() {
95
95
  function generate_canonical() {
96
96
  const originalHost = theme.canonical.originalHost || '';
97
97
  if (originalHost.length > 0) {
98
- if (is_home()) {
99
- return `<link rel="canonical" href="https://${originalHost}/">`;
100
- } else if (page.path?.length > 0) {
101
- const path = page.path.replace(/index\.html$/, '') || '/';
102
- return `<link rel="canonical" href="https://${originalHost}/${path}">`;
103
- }
98
+ const path = url_for(page.path).replace(/index\.html$/, '') || '/';
99
+ return `<link rel="canonical" href="https://${originalHost}${path}">`;
104
100
  }
105
101
  return '';
106
102
  }
@@ -11,6 +11,10 @@ function layoutDiv() {
11
11
  if (theme.article.share?.length > 0) {
12
12
  item.push('share');
13
13
  }
14
+ var contributors = partial('contributors', {map: theme.data_services.contributors.edit_this_page})
15
+ if (contributors?.trim().length > 0) {
16
+ item.push('contributors')
17
+ }
14
18
  if (item.length === 0) {
15
19
  return el;
16
20
  }
@@ -76,6 +80,10 @@ function layoutDiv() {
76
80
  `
77
81
  }
78
82
 
83
+ if (contributors) {
84
+ el += contributors
85
+ }
86
+
79
87
  var showSharePlugin = false
80
88
  if (page.wiki) {
81
89
  const proj = theme.wiki.tree[page.wiki]
@@ -0,0 +1,34 @@
1
+ <%
2
+ function parseURL() {
3
+ const source = page.source || ''
4
+ for (let id of Object.keys(map)) {
5
+ if (source.startsWith(id) && map[id]) {
6
+ return source.replace(id, map[id])
7
+ }
8
+ }
9
+ }
10
+ function layoutDiv() {
11
+ const url = parseURL()
12
+ if (url == null) {
13
+ return ''
14
+ }
15
+ const commits = url.replace('/github.com/', `/${theme.api_host.ghapi}/repos/`).replace('/blob/main/', '/commits?path=');
16
+ var el = `
17
+ <section id="contributors">
18
+ <div class="header">
19
+ <span>${__('meta.contributors')}</span>
20
+ <span class="spacer"></span>
21
+ <a class="edit" href="${url_for(url)}">
22
+ ${icon('default:edit')}
23
+ <span>${__('btn.edit')}</span>
24
+ </a>
25
+ </div>
26
+ <div class="body">
27
+ <div class="ds-contributors users-wrap" api-type="commits" api="${commits}"><div class="grid-box"></div></div>
28
+ </div>
29
+ </section>
30
+ `
31
+ return el
32
+ }
33
+ %>
34
+ <%- layoutDiv() %>
@@ -160,7 +160,7 @@
160
160
  status = 2;
161
161
  timer = null;
162
162
  reject('请求超时');
163
- if (retryTimes == 0) {
163
+ if (retryTimes == 0 && onFailure) {
164
164
  onFailure();
165
165
  }
166
166
  }
@@ -188,7 +188,9 @@
188
188
  }, 5000);
189
189
  } else {
190
190
  utils.onLoadFailure(el);
191
- onFailure();
191
+ if (onFailure) {
192
+ onFailure();
193
+ }
192
194
  }
193
195
  });
194
196
  });
@@ -22,7 +22,7 @@ if (page.logo) {
22
22
  // logo.icon, logo.title, logo.subtitle, logo.url
23
23
  function layoutTitle(main, url, sub) {
24
24
  var el = ''
25
- el += `<a class="title" href="${url_for(url || "/")}">`
25
+ el += `<a class="title" href="${url || "/"}">`
26
26
  el += `<div class="main">${main}</div>`
27
27
  if (sub) {
28
28
  const arr = sub.split('|')
@@ -36,9 +36,8 @@ function layoutToc(fallback) {
36
36
  }
37
37
 
38
38
  function layoutDiv(fallback) {
39
- const editBtn = partial('components/edit', {map: item.edit_this_page})
40
39
  const tocBody = layoutTocBody()
41
- if (tocBody.trim().length == 0 && editBtn.trim().length == 0) {
40
+ if (tocBody.trim().length == 0) {
42
41
  return ''
43
42
  }
44
43
  var el = ''
@@ -50,7 +49,6 @@ function layoutDiv(fallback) {
50
49
  el += `</div>`
51
50
  }
52
51
  el += `<div class="widget-footer">`
53
- el += editBtn
54
52
  el += `<a class="top" onclick="util.scrollTop()">`
55
53
  el += icon('default:upup')
56
54
  el += `<span>${__('btn.top')}</span>`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hexo-theme-stellar",
3
- "version": "1.32.3",
3
+ "version": "1.32.4",
4
4
  "description": "Elegant and powerful theme for Hexo.",
5
5
  "main": "package.json",
6
6
  "scripts": {
@@ -1,38 +1,39 @@
1
1
  /**
2
2
  * notebooks v1
3
3
  */
4
- const pagination = require('hexo-pagination')
5
-
6
- function paginationWithEmpty(base, posts, options={}) {
7
- const { layout, data = {} } = options
8
- if (posts.length === 0) {
9
- base = `${base}/`
10
- return [{
11
- path: base,
12
- layout: layout,
13
- data: {
14
- ...data,
15
- base: base,
16
- total: 1,
17
- current: 1,
18
- current_url: base,
19
- posts: posts,
20
- prev: 0,
21
- prev_link: '',
22
- next: 0,
23
- next_link: '',
24
- }
25
- }]
26
- } else {
27
- return pagination(base, posts, options)
28
- }
29
- }
30
4
 
31
5
  hexo.extend.generator.register('notebooks', function (locals) {
32
6
  const { site_tree, notebooks } = hexo.theme.config
33
7
  if (!notebooks?.tree || Object.keys(notebooks.tree).length === 0) {
34
8
  return []
35
9
  }
10
+ // 不用 blog 和 notebooks 时不必依赖 hexo-pagination
11
+ const pagination = require('hexo-pagination')
12
+
13
+ function paginationWithEmpty(base, posts, options={}) {
14
+ const { layout, data = {} } = options
15
+ if (posts.length === 0) {
16
+ base = `${base}/`
17
+ return [{
18
+ path: base,
19
+ layout: layout,
20
+ data: {
21
+ ...data,
22
+ base: base,
23
+ total: 1,
24
+ current: 1,
25
+ current_url: base,
26
+ posts: posts,
27
+ prev: 0,
28
+ prev_link: '',
29
+ next: 0,
30
+ next_link: '',
31
+ }
32
+ }]
33
+ } else {
34
+ return pagination(base, posts, options)
35
+ }
36
+ }
36
37
 
37
38
  const routes = []
38
39
 
@@ -17,6 +17,7 @@ svg.loading
17
17
  .loading-wrap
18
18
  margin: 0
19
19
  text-align: center
20
+ align-content: center
20
21
  background: var(--block)
21
22
  border-radius: $border-card
22
23
  position relative
@@ -40,6 +40,54 @@
40
40
  padding-top: 1rem
41
41
  border-top: 1px solid var(--block-border)
42
42
 
43
+ .article-footer #contributors
44
+ .header
45
+ display: flex
46
+ justify-content: space-between
47
+ a.edit
48
+ font-size: $fs-14
49
+ display: flex
50
+ align-items: center
51
+ color: inherit
52
+ background: var(--block)
53
+ padding: 0 .5rem
54
+ border-radius: $border-button
55
+ svg
56
+ width 1.2em
57
+ height: 1.2em
58
+ margin-right: 4px
59
+ color: var(--text-p4)
60
+ trans1 color
61
+ span
62
+ line-height: 1.5
63
+ trans1 color
64
+ trans1 background
65
+ &:hover
66
+ color: var(--theme)
67
+ background: var(--theme-a20)
68
+ svg
69
+ color: var(--theme)
70
+ .body
71
+ .loading-wrap
72
+ margin-top: 4px
73
+ padding: 0
74
+ height: 84px
75
+ .users-wrap .grid-box
76
+ margin-top: 4px
77
+ grid-template-columns: repeat(auto-fill, minmax(72px, 1fr))
78
+ grid-gap: 4px
79
+ &:empty
80
+ display: none
81
+ .users-wrap .user-card .card-link
82
+ background: var(--block)
83
+ trans1 all
84
+ &:hover
85
+ background: var(--theme-a20)
86
+ color: var(--theme)
87
+ img
88
+ width 32px
89
+ height: 32px
90
+
43
91
  .article-footer .social-wrap
44
92
  grid-gap: 0.5rem 2rem
45
93
  margin: 1rem 0 0.5rem
@@ -6,7 +6,7 @@
6
6
  font-size: 1rem
7
7
  font-weight: 700
8
8
  background: var(--card)
9
- border-radius: $border-card
9
+ border-radius: $border-card-l
10
10
  overflow: hidden
11
11
  box-shadow: 0 2px 8px 0px rgba(0, 0, 0, 0.03)
12
12
  color: var(--text-p3)
@@ -13,7 +13,7 @@
13
13
  align-items: center
14
14
  text-align: center
15
15
  line-height: 1.2
16
- border-radius: 4px
16
+ border-radius: $border-button
17
17
  overflow: hidden
18
18
  position: relative
19
19
  padding: 1rem 0.5rem
@@ -149,6 +149,9 @@
149
149
  pre code
150
150
  font-size: $fs-13
151
151
 
152
+ aside .tag-plugin.timeline[api] .body .lazy-box
153
+ border-radius: $border-image-s
154
+
152
155
  .tag-plugin.timeline[api] .body .footer
153
156
  margin: 0 0 -0.5rem
154
157
  padding: 0.5rem 0 1rem
@@ -8,6 +8,8 @@
8
8
  border-bottom: 1px dashed var(--block-border)
9
9
  padding: 0.5rem 1rem
10
10
  margin: 0
11
+ .atk-editor-user
12
+ padding: 0
11
13
  .atk-editor-user-wrap:empty
12
14
  visibility: hidden
13
15
  padding: 0
@@ -101,6 +103,7 @@
101
103
  background: none
102
104
  .md-text .atk-main-editor>.atk-textarea-wrap>.atk-textarea
103
105
  background: none
106
+ padding: 0.5rem 1rem
104
107
 
105
108
  .md-text .atk-notify-wrap .atk-notify
106
109
  border-radius: $border-bar
@@ -4,6 +4,7 @@ $loadingImageSize = 2rem
4
4
  .lazy-box
5
5
  position: relative
6
6
  overflow: hidden
7
+ line-height: 0
7
8
  &.async // 通过 data-service 异步创建的懒加载元素,没办法知道宽高
8
9
  background: var(--block)
9
10
  min-height: 4rem
@@ -0,0 +1,56 @@
1
+ utils.jq(() => {
2
+ $(function () {
3
+ function parseGithubFileContributors(data) {
4
+ // 去重贡献者(按 login)
5
+ const contributorsMap = new Map();
6
+
7
+ for (const commit of data) {
8
+ const author = commit.author;
9
+ if (author) {
10
+ const login = author.login;
11
+ if (!contributorsMap.has(login)) {
12
+ contributorsMap.set(login, {
13
+ login: login,
14
+ avatar_url: author.avatar_url,
15
+ html_url: author.html_url,
16
+ count: 1
17
+ });
18
+ } else {
19
+ contributorsMap.get(login).count++;
20
+ }
21
+ }
22
+ }
23
+ // 转为数组并按提交次数排序(降序)
24
+ const sortedContributors = Array.from(contributorsMap.values())
25
+ .sort((a, b) => b.count - a.count);
26
+ return sortedContributors;
27
+ }
28
+
29
+ const els = document.getElementsByClassName('ds-contributors');
30
+ for (var i = 0; i < els.length; i++) {
31
+ const el = els[i];
32
+ const api = el.getAttribute('api');
33
+ if (api == null) {
34
+ continue;
35
+ }
36
+ const default_avatar = def.avatar;
37
+ // layout
38
+ utils.request(el, api, async resp => {
39
+ const data = await resp.json();
40
+ const list = parseGithubFileContributors(data);
41
+ for (let item of list) {
42
+ var cell = `<div class="grid-cell user-card">`;
43
+ cell += `<a class="card-link" target="_blank" rel="external nofollow noopener noreferrer" href="${item.html_url || item.url}">`;;
44
+ cell += `<img src="${item.avatar_url || item.avatar || item.icon || default_avatar}" onerror="javascript:this.removeAttribute(\'data-src\');this.src=\'${default_avatar}\';"/>`;
45
+ cell += `<div class="name image-meta">`;
46
+ cell += `<span class="image-caption">${item.title || item.login}</span>`;
47
+ cell += `</div>`;
48
+ cell += `</a>`;
49
+ cell += `</div>`;
50
+ $(el).find('.grid-box').append(cell);
51
+ }
52
+ window.wrapLazyloadImages(el);
53
+ });
54
+ }
55
+ });
56
+ });
@@ -1,24 +0,0 @@
1
- <%
2
- function parseURL() {
3
- const source = page.source || ''
4
- var url
5
- for (let id of Object.keys(map)) {
6
- if (source.startsWith(id) && map[id]) {
7
- return source.replace(id, map[id])
8
- }
9
- }
10
- }
11
- function layoutDiv() {
12
- const url = parseURL()
13
- if (url == null) {
14
- return ''
15
- }
16
- var el = ''
17
- el += `<a class="edit" href="${url_for(url)}">`
18
- el += icon('default:edit')
19
- el += `<span>${__('btn.edit')}</span>`
20
- el += `</a>`
21
- return el
22
- }
23
- %>
24
- <%- layoutDiv() %>