hexo-theme-stellar 1.32.2 → 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.
Files changed (38) hide show
  1. package/README.md +22 -4
  2. package/_config.yml +16 -3
  3. package/_data/icons.yml +1 -1
  4. package/languages/en.yml +1 -0
  5. package/languages/zh-CN.yml +1 -0
  6. package/languages/zh-TW.yml +1 -0
  7. package/layout/_partial/comments/artalk/layout.ejs +1 -1
  8. package/layout/_partial/comments/artalk/script.ejs +22 -1
  9. package/layout/_partial/head.ejs +2 -6
  10. package/layout/_partial/main/article/article_footer.ejs +8 -0
  11. package/layout/_partial/main/article/contributors.ejs +34 -0
  12. package/layout/_partial/main/navbar/article_banner.ejs +2 -2
  13. package/layout/_partial/scripts/lazyload.ejs +1 -1
  14. package/layout/_partial/scripts/utils.ejs +4 -2
  15. package/layout/_partial/sidebar/logo.ejs +1 -1
  16. package/layout/_partial/widgets/toc.ejs +1 -3
  17. package/layout/_plugins/fancybox.ejs +7 -3
  18. package/package.json +1 -1
  19. package/scripts/generators/notebooks.js +27 -26
  20. package/scripts/tags/lib/friends.js +4 -1
  21. package/scripts/tags/lib/posters.js +1 -1
  22. package/scripts/tags/lib/sites.js +2 -2
  23. package/source/css/_common/loading.styl +1 -0
  24. package/source/css/_components/main.styl +2 -2
  25. package/source/css/_components/pages/article-story.styl +1 -1
  26. package/source/css/_components/partial/article-footer.styl +48 -0
  27. package/source/css/_components/partial/paginator.styl +1 -1
  28. package/source/css/_components/tag-plugins/friends.styl +1 -1
  29. package/source/css/_components/tag-plugins/quot.styl +1 -0
  30. package/source/css/_components/tag-plugins/timeline.styl +3 -0
  31. package/source/css/_components/widgets/toc.styl +6 -1
  32. package/source/css/_custom.styl +1 -1
  33. package/source/css/_defines/theme_base.styl +2 -2
  34. package/source/css/_defines/theme_colorful.styl +8 -8
  35. package/source/css/_plugins/comments/artalk.styl +7 -1
  36. package/source/css/_plugins/lazyload.styl +3 -2
  37. package/source/js/services/contributors.js +56 -0
  38. package/layout/_partial/widgets/components/edit.ejs +0 -24
package/README.md CHANGED
@@ -1,6 +1,24 @@
1
1
  # Stellar - 每个人的独立博客
2
2
 
3
- Stellar 是一个内置文档系统的简约商务风 Hexo 主题,支持丰富的标签和动态数据组件,请配合 [文档](https://xaoxuu.com/wiki/stellar/) 使用。
3
+ Stellar 是一个极为强大的综合型 Hexo 主题,包含博客系统、知识库系统、专栏系统、笔记系统,内置海量的标签和动态数据组件。
4
+
5
+ ## 亮点
6
+
7
+ - 支持技术/生活文章布局风格,为不同类型的文章使用不同的风格。
8
+ - 内置 wiki 系统,可以展示多个项目文档,或个人知识库。
9
+ - 内置专栏系统,可以沉浸式阅读专栏系列文章。
10
+ - 内置笔记系统,更方便梳理笔记。
11
+ - 内置海量的灵活而强大的标签组件,互相之间还可以混搭嵌套。
12
+ - 内置多种动态数据组件,静态博客更新不再依赖于重新部署。
13
+ - 动态时间线(像发朋友圈一样发布短文,也可以订阅他人的时间线)
14
+ - 自动化的动态友链(自动检测友链状态、打标签、友链文章订阅)
15
+ - 渲染远程 markdown 文件(例如渲染项目仓库的 README 以减少重复工作)
16
+ - 模块化设计,内置多种复用性极强的小组件,自由搭配布局。
17
+ - 图片懒加载占位固定原图长宽比,不会发生高度跳变,体验更佳。
18
+ - 支持一站多作者,可以为不同文章指定不同的作者,每位作者都有专属主页。
19
+ - 社区文化:可订阅「探索号」时间线数据源获取社区用户的宝贵经验分享。
20
+
21
+ 上述亮点都可以在 [文档](https://xaoxuu.com/wiki/stellar/) 中找到详细使用方法。
4
22
 
5
23
  [![Stargazers over time](https://starchart.cc/xaoxuu/hexo-theme-stellar.svg)](https://starchart.cc/xaoxuu/hexo-theme-stellar)
6
24
 
@@ -10,10 +28,10 @@ Stellar 是一个内置文档系统的简约商务风 Hexo 主题,支持丰富
10
28
  Check your environment:
11
29
 
12
30
  ```yaml
13
- Hexo: 6.3.0 ~ 7.0.0
31
+ Hexo: 6.3.0 ~ latest
14
32
  hexo-cli: 4.3.0 ~ latest
15
- node: 14.17.3 ~ 20.10.0 # 建议选择 LTS 版本,过高的版本 hexo 还没有进行兼容。
16
- npm: 6.14.13 ~ 10.2.3
33
+ node: 14.17.3 ~ latest LTS # 建议选择 LTS 版本,过高的版本 hexo 还没有进行兼容。
34
+ npm: 6.14.13 ~ latest
17
35
  ```
18
36
 
19
37
  Edit your `_config.yml`:
package/_config.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  ######## Stellar info ########
2
2
  stellar:
3
- version: '1.32.2'
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
@@ -315,7 +315,15 @@ comments:
315
315
  js: https://unpkg.com/artalk@2.9/dist/Artalk.js
316
316
  server: # 后端服务地址
317
317
  site: '' # 站点名称
318
- darkMode: auto
318
+ darkMode: auto # 其它模式都有问题,慎用,否则后果自负
319
+ fancybox: true # 是否为评论区启用 fancybox
320
+ # artalk 有三类图片:1.上传至服务端 2.上传至服务端后利用upgit上传至图床 3.前端上传至图床(本配置为此类)
321
+ # 配置此项时将覆盖服务端上传能力
322
+ imageUploader:
323
+ # 以兰空图床为例,下列填写内容为:
324
+ # api: 'https://xxxxxx/api/v1/upload' # 图床上传地址
325
+ # token: 'Bearer xxxxxxxxxxxxxx' # 图床验证
326
+ # resp: 'data.links.url' # 图片地址返回值的字段
319
327
 
320
328
 
321
329
  ######## Footer ########
@@ -495,6 +503,11 @@ data_services:
495
503
  js: /js/services/artalk_latest_comment.js
496
504
  giscus:
497
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
498
511
 
499
512
 
500
513
  # 扩展插件接入方法:(插件名下面用 #plugin# 代替)
@@ -666,7 +679,7 @@ style:
666
679
  next: https://gcore.jsdelivr.net/gh/cdn-x/placeholder@1.0.12/arrow/064b95430caf4.svg
667
680
  error_page: https://gcore.jsdelivr.net/gh/cdn-x/placeholder@1.0.12/404/1c830bfcd517d.svg
668
681
  site: # 使用 background-image 后,blur 设置才有效
669
- # background-image: 'url(https://gcore.jsdelivr.net/gh/cdn-x/placeholder@1.0.14/image/site-bg1@small.webp)'
682
+ # background-image: url(https://gcore.jsdelivr.net/gh/cdn-x/placeholder@1.0.14/image/site-bg1@small.webp)
670
683
  blur-px: 100px
671
684
  blur-bg: var(--bg-a75)
672
685
  blur-sat: 300%
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: 分鐘前
@@ -1,7 +1,7 @@
1
1
  <%
2
2
  function layoutDiv() {
3
3
  var el = '';
4
- el += '<div id="artalk_container"';
4
+ el += `<div class="artalk ${theme.comments.artalk?.fancybox ? 'with-fancybox' : ''}" id="artalk_container"`;
5
5
  let cfg = {};
6
6
  if (page.comment_id) {
7
7
  cfg['comment_id'] = page.comment_id;
@@ -6,6 +6,10 @@
6
6
  if (!el) return;
7
7
  utils.css('<%- theme.comments.artalk.css %>');
8
8
  utils.js('<%- theme.comments.artalk.js %>', {defer: true}).then(function () {
9
+ // 监听 Artalk 事件
10
+ Artalk.use((ctx) => {
11
+ ctx.on('list-loaded', () => window.lazyLoadInstance?.update())
12
+ })
9
13
  const path = el.getAttribute('comment_id') ?? decodeURI(window.location.pathname);
10
14
  const artalk = Artalk.init({
11
15
  el: '#artalk_container',
@@ -13,7 +17,24 @@
13
17
  pageTitle: '<%= page.title %>',
14
18
  server: '<%= theme.comments.artalk.server %>',
15
19
  site: '<%= theme.comments.artalk.site %>',
16
- darkMode: '<%= theme.comments.artalk.darkMode %>'
20
+ darkMode: '<%= theme.comments.artalk.darkMode %>',
21
+ <% if (theme.comments.artalk.imageUploader?.api) { %>
22
+ imgUploader: function(file) {
23
+ let headers = new Headers();
24
+ headers.set('Accept', 'application/json');
25
+ <% if (theme.comments.artalk.imageUploader?.token) { %>
26
+ headers.set('Authorization', '<%= theme.comments.artalk.imageUploader?.token %>')
27
+ <% } %>
28
+ let formData = new FormData();
29
+ formData.append('file', file);
30
+ return fetch('<%= theme.comments.artalk.imageUploader?.api %>',{
31
+ method: 'POST',
32
+ body: formData,
33
+ headers: headers
34
+ }).then((resp) => resp.json())
35
+ .then((resp) => resp.<%= theme.comments.artalk.imageUploader?.resp %>)
36
+ },
37
+ <% } %>
17
38
  });
18
39
  });
19
40
  }
@@ -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() %>
@@ -70,7 +70,7 @@ function layoutSubtitle() {
70
70
 
71
71
  function layoutIcon() {
72
72
  if (banner.avatar?.length > 0 || banner.icon?.length > 0) {
73
- return `<img class="avatar" src="${banner.avatar || banner.icon}">`
73
+ return `<img class="lazy avatar" data-src="${banner.avatar || banner.icon}">`
74
74
  } else {
75
75
  return ''
76
76
  }
@@ -109,7 +109,7 @@ function layoutDiv() {
109
109
  var el = ``
110
110
  el += `<div class="article banner${scrollreveal(' ')} top">`
111
111
  if (banner.url?.length > 0) {
112
- el += `<img class="bg" src="${banner.url}">`
112
+ el += `<img class="lazy bg" data-src="${banner.url}">`
113
113
  if (banner.color) {
114
114
  style += ' style="--text-banner:' + banner.color + '"'
115
115
  }
@@ -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",
7
+ elements_selector: ".lazy, .lazyload",
8
8
  callback_loaded: (el) => {
9
9
  el.classList.add('loaded');
10
10
  const wrapper = el.closest('.lazy-box');
@@ -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>`
@@ -4,14 +4,18 @@
4
4
  css: `<%- conf.css %>`,
5
5
  js: `<%- conf.js %>`
6
6
  };
7
- var selector = '[data-fancybox]:not(.error)';
7
+ var selector = '[data-fancybox]:not(.error), .with-fancybox .atk-content img:not([atk-emoticon])';
8
8
  if (ctx.fancybox.selector) {
9
9
  selector += `, ${ctx.fancybox.selector}`
10
10
  }
11
11
  var needFancybox = document.querySelectorAll(selector).length !== 0;
12
12
  if (!needFancybox) {
13
- const els = document.getElementsByClassName('ds-memos');
14
- if (els != undefined && els.length > 0) {
13
+ const memos = document.getElementsByClassName('ds-memos');
14
+ if (memos != undefined && memos.length > 0) {
15
+ needFancybox = true;
16
+ }
17
+ const fancybox = document.getElementsByClassName('with-fancybox');
18
+ if (fancybox != undefined && fancybox.length > 0) {
15
19
  needFancybox = true;
16
20
  }
17
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hexo-theme-stellar",
3
- "version": "1.32.2",
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
 
@@ -27,7 +27,10 @@ module.exports = ctx => function(args) {
27
27
  if (item?.url && item?.title) {
28
28
  el += `<div class="grid-cell user-card">`
29
29
  el += `<a class="card-link" target="_blank" rel="external nofollow noopener noreferrer" href="${item.url}">`
30
- el += `<img src="${item.icon || item.avatar || ctx.theme.config.default.avatar}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${ctx.theme.config.default.avatar}&quot;;"/>`
30
+ el += `<div class="lazy-box icon">`
31
+ el += `<img class="lazy" data-src="${item.icon || item.avatar || ctx.theme.config.default.avatar}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${ctx.theme.config.default.avatar}&quot;;"/>`
32
+ el += `<div class="lazy-icon" style="background-image:url(&quot;${ctx.theme.config.default.loading}&quot;);"></div>`
33
+ el += `</div>`
31
34
  el += `<div class="name">`
32
35
  el += `<span>${item.title}</span>`
33
36
  el += `</div>`
@@ -11,7 +11,7 @@ module.exports = ctx => function(args) {
11
11
  var args = ctx.args.map(args, ['repo', 'api', 'size'], ['group'])
12
12
  const host = ctx.theme.config.api_host.ghraw
13
13
  if (args.size == null) {
14
- args.size = 'm'
14
+ args.size = 'xs'
15
15
  }
16
16
  var api
17
17
  if (args.api) {
@@ -32,12 +32,12 @@ module.exports = ctx => function(args) {
32
32
  el += `<div class="grid-cell site-card">`
33
33
  el += `<a class="card-link" target="_blank" rel="external nofollow noopener noreferrer" href="${item.url}">`
34
34
  el += `<div class="lazy-box snapshot">`
35
- el += `<img src="${item.cover || item.snapshot || item.screenshot || ('https://image.thum.io/get/width/1280/crop/720/' + item.url)}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${ctx.theme.config.default.cover}&quot;;"/>`
35
+ el += `<img class="lazy" data-src="${item.cover || item.snapshot || item.screenshot || ('https://image.thum.io/get/width/1280/crop/720/' + item.url)}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${ctx.theme.config.default.cover}&quot;;"/>`
36
36
  el += `<div class="lazy-icon" style="background-image:url(&quot;${ctx.theme.config.default.loading}&quot;);"></div>`
37
37
  el += `</div>`
38
38
  el += `<div class="info">`
39
39
  el += `<div class="lazy-box icon">`
40
- el += `<img src="${item.icon || item.avatar || ctx.theme.config.default.link}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${item.icon || item.avatar || ctx.theme.config.default.link}&quot;;"/>`
40
+ el += `<img class="lazy" data-src="${item.icon || item.avatar || ctx.theme.config.default.link}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${item.icon || item.avatar || ctx.theme.config.default.link}&quot;;"/>`
41
41
  el += `<div class="lazy-icon" style="background-image:url(&quot;${ctx.theme.config.default.loading}&quot;);"></div>`
42
42
  el += `</div>`
43
43
  el += `<span class="title">${item.title}</span>`
@@ -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
@@ -20,8 +20,8 @@ body>.sitebg
20
20
  right: 0
21
21
  bottom: 0
22
22
  background-size: cover
23
- if $site-background
24
- background-image: convert($site-background)
23
+ if $site-background-image
24
+ background-image: convert($site-background-image)
25
25
  .siteblur
26
26
  width: 100%
27
27
  height: 100%
@@ -1,7 +1,7 @@
1
1
  .l_body[type=story] .md-text.content
2
2
  --fsp: 'calc(%s + 1px)' % $fs-body
3
3
  --gap-p: 2rem
4
- div.tag-plugin,p>img
4
+ >div.tag-plugin,>p>img
5
5
  margin-top: 2.4rem
6
6
  margin-bottom: 2.4rem
7
7
  // 段落标题
@@ -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
@@ -19,6 +19,7 @@
19
19
  position: relative
20
20
  // override
21
21
  .md-text.content .tag-plugin.quot
22
+ text-wrap: balance
22
23
  h1,h2
23
24
  font-size: $fsh2
24
25
  font-weight: 900
@@ -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
@@ -11,6 +11,11 @@
11
11
  width: 4px
12
12
  background: var(--block)
13
13
  border-radius: 4px
14
+
15
+ if !$site-background-image
16
+ @media screen and (min-width: $device-laptop)
17
+ background: var(--background)
18
+
14
19
 
15
20
  // 目录基本样式
16
21
  .widget-wrapper.toc .toc
@@ -178,6 +183,6 @@
178
183
  color: var(--text-p2)
179
184
  .widget-wrapper.toc .widget-footer
180
185
  a
181
- background: var(--bg-a100)
186
+ background: var(--block)
182
187
  a+a
183
188
  margin-top: 4px
@@ -29,7 +29,7 @@ $fsp2 = 'calc(%s - 2px)' % $fs-body
29
29
  $fsp3 = 'calc(%s - 3px)' % $fs-body
30
30
 
31
31
  // site main
32
- $site-background = hexo-config('style.site.background-image')
32
+ $site-background-image = hexo-config('style.site.background-image')
33
33
  $leftbar-background-image = hexo-config('style.leftbar.background-image')
34
34
  $leftbar-background-color = hexo-config('style.leftbar.background-color')
35
35
 
@@ -66,7 +66,7 @@ x-set-text-colors($scheme, $p0 = 1, $p1 = 0.8, $p2 = 0.7, $p3 = 0.5, $p4 = 0.4)
66
66
  dynamic-theme-light()
67
67
  $hue = $c-base-hue
68
68
  --background: x-hsla($hue, 20%, 98%, 1)
69
- --card: $site-background ? hsla(white, 0.5) : white
69
+ --card: $site-background-image ? hsla(white, 0.5) : white
70
70
 
71
71
  x-set-bg-colors('light')
72
72
  x-set-text-colors('light')
@@ -77,7 +77,7 @@ dynamic-theme-dark()
77
77
  --background: x-hsla($hue, 8%, 12%, 1)
78
78
  @media screen and (max-width: $device-mobile-max)
79
79
  --background: black
80
- --card: $site-background ? hsla(white, 0.25) : x-hsla($hue, 10%, 24%, 1)
80
+ --card: $site-background-image ? hsla(white, 0.25) : x-hsla($hue, 10%, 24%, 1)
81
81
 
82
82
  x-set-bg-colors('dark')
83
83
  x-set-text-colors('dark')
@@ -4,15 +4,15 @@ x-set-theme-colorful($scheme = 'light', $color, $text = true)
4
4
  x-set-theme-with-color($color)
5
5
  x-set-link-with-color($color)
6
6
  if $scheme == 'dark'
7
- --theme-border: x-hsla($hue, 50%, 24%)
7
+ --theme-border: x-hsla($hue, 40%, 24%)
8
8
  --theme-block: x-hsla($hue, 24%, 24%)
9
9
  --theme-mark: x-hsla($hue, 24%, 24%)
10
- --theme-codeblock: x-hsla($hue, 12%, 18%)
10
+ --theme-codeblock: x-hsla($hue, 12%, 24%)
11
11
  if text
12
12
  --text: x-hsla($hue, 50%, 75%, 1)
13
- --text-p1: x-hsla($hue, 50%, 75%, 0.8)
14
- --text-p2: x-hsla($hue, 80%, 80%, 0.7)
15
- --text-p3: x-hsla($hue, 80%, 80%, 0.5)
13
+ --text-p1: x-hsla($hue, 80%, 80%, 1)
14
+ --text-p2: x-hsla($hue, 80%, 80%, 0.8)
15
+ --text-p3: x-hsla($hue, 80%, 80%, 0.6)
16
16
  --text-p4: x-hsla($hue, 80%, 80%, 0.4)
17
17
  --link: x-hsla($hue, 80%, 60%, 1)
18
18
  --link-a20: x-hsla($hue, 80%, 60%, 0.2)
@@ -23,9 +23,9 @@ x-set-theme-colorful($scheme = 'light', $color, $text = true)
23
23
  --theme-codeblock: x-hsla($hue, 50%, 95%)
24
24
  if text
25
25
  --text: x-hsla($hue, 55%, 16%, 1)
26
- --text-p1: x-hsla($hue, 55%, 16%, 0.8)
27
- --text-p2: x-hsla($hue, 90%, 20%, 0.7)
28
- --text-p3: x-hsla($hue, 90%, 20%, 0.5)
26
+ --text-p1: x-hsla($hue, 90%, 20%, 1)
27
+ --text-p2: x-hsla($hue, 90%, 20%, 0.8)
28
+ --text-p3: x-hsla($hue, 90%, 20%, 0.6)
29
29
  --text-p4: x-hsla($hue, 90%, 20%, 0.4)
30
30
  --link: x-hsla($hue, 95%, 32%, 1)
31
31
  --link-a20: x-hsla($hue, 95%, 32%, 0.2)
@@ -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
@@ -70,6 +72,9 @@
70
72
  .atk-error-layer
71
73
  background: none
72
74
 
75
+ .with-fancybox .atk-content img:not([atk-emoticon])
76
+ cursor: zoom-in
77
+
73
78
  .cmt-body
74
79
  .artalk, .atk-layer-wrap, .artalk.atk-dark-mode, .atk-layer-wrap.atk-dark-mode
75
80
  --at-color-font: var(--text-p1)
@@ -77,7 +82,7 @@
77
82
  --at-color-sub: var(--text-p3)
78
83
  --at-color-grey: var(--text-p4)
79
84
  --at-color-meta: var(--text-p4)
80
- if $site-background
85
+ if $site-background-image
81
86
  --at-color-border: transparent
82
87
  else
83
88
  --at-color-border: var(--block-border)
@@ -98,6 +103,7 @@
98
103
  background: none
99
104
  .md-text .atk-main-editor>.atk-textarea-wrap>.atk-textarea
100
105
  background: none
106
+ padding: 0.5rem 1rem
101
107
 
102
108
  .md-text .atk-notify-wrap .atk-notify
103
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
@@ -47,7 +48,7 @@ img[data-ll-status].loaded
47
48
 
48
49
  // 加载失败
49
50
  img[data-ll-status].error
50
- min-width: $loadingImageSize
51
- min-height: $loadingImageSize
51
+ width: $loadingImageSize
52
+ height: $loadingImageSize
52
53
  &+.lazy-icon
53
54
  display: none
@@ -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() %>