hexo-theme-stellar 1.32.3 → 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 (93) hide show
  1. package/_config.yml +27 -7
  2. package/_data/icons.yml +9 -2
  3. package/languages/en.yml +1 -0
  4. package/languages/zh-CN.yml +1 -0
  5. package/languages/zh-TW.yml +1 -0
  6. package/layout/_partial/comments/artalk/script.ejs +8 -7
  7. package/layout/_partial/head.ejs +7 -5
  8. package/layout/_partial/main/article/article_footer.ejs +8 -0
  9. package/layout/_partial/main/article/contributors.ejs +34 -0
  10. package/layout/_partial/main/article/read_next.ejs +2 -2
  11. package/layout/_partial/main/navbar/article_banner.ejs +1 -1
  12. package/layout/_partial/main/navbar/breadcrumb/blog.ejs +3 -3
  13. package/layout/_partial/main/navbar/breadcrumb/note.ejs +2 -2
  14. package/layout/_partial/main/navbar/breadcrumb/page.ejs +1 -1
  15. package/layout/_partial/main/navbar/breadcrumb/wiki.ejs +2 -2
  16. package/layout/_partial/main/navbar/dateinfo.ejs +1 -1
  17. package/layout/_partial/main/navbar/ghinfo.ejs +1 -1
  18. package/layout/_partial/main/navbar/nav_tabs_blog.ejs +12 -12
  19. package/layout/_partial/main/navbar/nav_tabs_wiki.ejs +2 -2
  20. package/layout/_partial/main/notebook/note_tags.ejs +1 -1
  21. package/layout/_partial/scripts/lazyload.ejs +1 -1
  22. package/layout/_partial/scripts/utils.ejs +69 -37
  23. package/layout/_partial/sidebar/index_leftbar.ejs +1 -1
  24. package/layout/_partial/sidebar/logo.ejs +1 -1
  25. package/layout/_partial/sidebar/menu.ejs +1 -1
  26. package/layout/_partial/widgets/components/link.ejs +1 -1
  27. package/layout/_partial/widgets/ghissues.ejs +7 -1
  28. package/layout/_partial/widgets/ghrepo.ejs +2 -2
  29. package/layout/_partial/widgets/ghuser.ejs +1 -1
  30. package/layout/_partial/widgets/recent.ejs +1 -1
  31. package/layout/_partial/widgets/related.ejs +2 -2
  32. package/layout/_partial/widgets/tagtree.ejs +1 -1
  33. package/layout/_partial/widgets/timeline.ejs +7 -1
  34. package/layout/_partial/widgets/toc.ejs +1 -3
  35. package/layout/_partial/widgets/tree.ejs +1 -1
  36. package/layout/archive.ejs +1 -1
  37. package/layout/categories.ejs +1 -1
  38. package/layout/index.ejs +1 -1
  39. package/layout/index_topic.ejs +1 -1
  40. package/layout/index_wiki.ejs +2 -2
  41. package/layout/notebooks.ejs +1 -1
  42. package/layout/notes.ejs +1 -1
  43. package/layout/tags.ejs +1 -1
  44. package/package.json +1 -1
  45. package/scripts/events/index.js +1 -1
  46. package/scripts/events/lib/config.js +7 -1
  47. package/scripts/filters/pretty_urls.js +25 -0
  48. package/scripts/generators/notebooks.js +27 -26
  49. package/scripts/helpers/json_ld.js +5 -4
  50. package/scripts/helpers/pretty_url.js +22 -0
  51. package/scripts/tags/index.js +2 -0
  52. package/scripts/tags/lib/albums.js +1 -1
  53. package/scripts/tags/lib/chat.js +1 -1
  54. package/scripts/tags/lib/friends.js +1 -1
  55. package/scripts/tags/lib/link.js +1 -1
  56. package/scripts/tags/lib/posters.js +1 -1
  57. package/scripts/tags/lib/rating.js +32 -0
  58. package/scripts/tags/lib/sites.js +1 -1
  59. package/scripts/tags/lib/timeline.js +2 -1
  60. package/scripts/tags/lib/vote.js +28 -0
  61. package/source/css/_common/button.styl +1 -0
  62. package/source/css/_common/loading.styl +1 -0
  63. package/source/css/_components/pages/article-story.styl +2 -0
  64. package/source/css/_components/partial/article-footer.styl +48 -0
  65. package/source/css/_components/partial/paginator.styl +1 -1
  66. package/source/css/_components/tag-plugins/friends.styl +1 -1
  67. package/source/css/_components/tag-plugins/image.styl +1 -0
  68. package/source/css/_components/tag-plugins/rating.styl +39 -0
  69. package/source/css/_components/tag-plugins/timeline.styl +7 -4
  70. package/source/css/_components/tag-plugins/vote.styl +49 -0
  71. package/source/css/_components/widgets/timeline.styl +1 -1
  72. package/source/css/_defines/theme_base.styl +1 -0
  73. package/source/css/_plugins/comments/artalk.styl +53 -10
  74. package/source/css/_plugins/lazyload.styl +4 -0
  75. package/source/js/main.js +12 -5
  76. package/source/js/search/local-search.js +3 -1
  77. package/source/js/services/artalk_latest_comment.js +1 -1
  78. package/source/js/services/contributors.js +56 -0
  79. package/source/js/services/fcircle.js +1 -1
  80. package/source/js/services/friends.js +1 -1
  81. package/source/js/services/friends_and_posts.js +1 -1
  82. package/source/js/services/ghinfo.js +1 -1
  83. package/source/js/services/giscus_latest_comment.js +1 -1
  84. package/source/js/services/memos.js +1 -1
  85. package/source/js/services/rating.js +142 -0
  86. package/source/js/services/siteinfo.js +1 -1
  87. package/source/js/services/sites.js +1 -1
  88. package/source/js/services/timeline.js +1 -1
  89. package/source/js/services/twikoo_latest_comment.js +1 -1
  90. package/source/js/services/vote.js +113 -0
  91. package/source/js/services/waline_latest_comment.js +1 -1
  92. package/source/js/services/weibo.js +1 -1
  93. package/layout/_partial/widgets/components/edit.ejs +0 -24
@@ -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') %>
@@ -12,7 +12,13 @@ function layoutDiv() {
12
12
  }
13
13
  el += '<div class="widget-body fs14">';
14
14
  el += `<div class="tag-plugin timeline data-service ds-${(item.type || 'timeline')}"`;
15
- ['api', 'user', 'hide', 'limit'].forEach(key => {
15
+ // dataset
16
+ ['api'].forEach(key => {
17
+ if (item[key]) {
18
+ el += ' data-' + key + '="' + item[key] + '"';
19
+ }
20
+ });
21
+ ['user', 'hide', 'limit'].forEach(key => {
16
22
  if (item[key]) {
17
23
  el += ' ' + key + '="' + item[key] + '"';
18
24
  }
@@ -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>`
@@ -20,7 +20,7 @@ function layoutDocTree(pages) {
20
20
  isActive += ' active'
21
21
  }
22
22
  if (proj.pages.length > 1) {
23
- let href = url_for(p.path);
23
+ let href = pretty_url(p.path);
24
24
  if (p.is_homepage) {
25
25
  href += '#start'
26
26
  }
@@ -39,7 +39,7 @@ function layoutArchiveList() {
39
39
  return post.year == year
40
40
  }).each(function(post) {
41
41
  el += `<div class="archive-list">`
42
- el +=`<a class='post fs14' href="${url_for(post.link || post.path)}">`
42
+ el +=`<a class='post fs14' href="${pretty_url(post.link || post.path)}">`
43
43
  el += `<time>${date(post.date, 'MM-DD')}</time>`
44
44
  el += `<span>`
45
45
  if (post.title) {
@@ -12,7 +12,7 @@ if (page.menu_id == undefined) {
12
12
  <article class='<%- scrollreveal("") %>' id='cats'>
13
13
  <% site.categories.sort('path').each(function(category){ %>
14
14
  <div>
15
- <a class="<%= category.parent ? 'cat child' : 'cat'%>" href="<%= url_for(category.path) %>">
15
+ <a class="<%= category.parent ? 'cat child' : 'cat'%>" href="<%= pretty_url(category.path) %>">
16
16
  <span class='name'><%- icon('default:category') %><%- category.name %></span>
17
17
  <span class='badge'>(<%- category.posts.length %>)</span>
18
18
  </a>
package/layout/index.ejs CHANGED
@@ -12,7 +12,7 @@ function layout_post_card(layout, post, content) {
12
12
  if (layout == 'post' && post.cover != undefined && post.poster != undefined) {
13
13
  layout += ' photo';
14
14
  }
15
- el += `<a class="post-card${scrollreveal(' ')} ${layout}" href="${url_for(post.link || post.path)}">`
15
+ el += `<a class="post-card${scrollreveal(' ')} ${layout}" href="${pretty_url(post.link || post.path)}">`
16
16
  el += content;
17
17
  el += '</a>';
18
18
  return el;
@@ -8,7 +8,7 @@ function layout_topic_list(partial) {
8
8
  continue
9
9
  }
10
10
  el += `<div class="post-list wiki topic">`
11
- el += `<a class="post-card wiki topic${scrollreveal(' ')}" href="${url_for(topic.homepage?.path || '/')}">`
11
+ el += `<a class="post-card wiki topic${scrollreveal(' ')}" href="${pretty_url(topic.homepage?.path || '/')}">`
12
12
  el += partial(topic)
13
13
  el += `</a>`
14
14
  el += `</div>`
@@ -13,14 +13,14 @@ function layout_wiki_list(partial) {
13
13
  if (page.filter === false) {
14
14
  // wikiList
15
15
  el += `<div class="post-list wiki">`
16
- el += `<a class="post-card wiki${scrollreveal(' ')}" href="${url_for(proj.homepage?.path || '/')}">`
16
+ el += `<a class="post-card wiki${scrollreveal(' ')}" href="${pretty_url(proj.homepage?.path || '/')}">`
17
17
  el += partial(proj)
18
18
  el += `</a>`
19
19
  el += `</div>`
20
20
  } else if (proj.tags && proj.tags.includes(page.tagName) === true) {
21
21
  // filtered wikiList
22
22
  el += `<div class="post-list wiki filter">`
23
- el += `<a class="post-card wiki${scrollreveal(' ')}" href="${url_for(proj.homepage?.path || '/')}">`
23
+ el += `<a class="post-card wiki${scrollreveal(' ')}" href="${pretty_url(proj.homepage?.path || '/')}">`
24
24
  el += partial(proj)
25
25
  el += `</a>`
26
26
  el += `</div>`
@@ -1,7 +1,7 @@
1
1
  <%# 笔记本列表页主体部分 %>
2
2
  <% for (const notebook of Object.values(theme.notebooks.tree)) { %>
3
3
  <div class="post-list wiki">
4
- <a class="post-card wiki<%= scrollreveal(' ') %>" href="<%= url_for(notebook.base_dir) %>">
4
+ <a class="post-card wiki<%= scrollreveal(' ') %>" href="<%= pretty_url(notebook.base_dir) %>">
5
5
  <%- include('_partial/main/notebook/notebook_card', { notebook: notebook }) %>
6
6
  </a>
7
7
  </div>
package/layout/notes.ejs CHANGED
@@ -3,7 +3,7 @@
3
3
  <% page.posts.each(post => { %>
4
4
  <a
5
5
  class="post-card<%= scrollreveal(' ') %> post"
6
- href="<%= url_for(post.link || post.path) %><%= page.activeTag ? `?tag=${page.activeTag}` : '' %>"
6
+ href="<%= pretty_url(post.link || post.path) %><%= page.activeTag ? `?tag=${page.activeTag}` : '' %>"
7
7
  >
8
8
  <%- include('_partial/main/notebook/note_card', { note: post }) %>
9
9
  </a>
package/layout/tags.ejs CHANGED
@@ -11,7 +11,7 @@ if (page.menu_id == undefined) {
11
11
  <div class='post-list'>
12
12
  <article class='<%- scrollreveal("") %>' id='tags'>
13
13
  <% site.tags.sort('length', -1).each(function(tag){ %>
14
- <a class='tag' href="<%= url_for(tag.path) %>">
14
+ <a class='tag' href="<%= pretty_url(tag.path) %>">
15
15
  <span class='name'><%= tag.name %></span>
16
16
  </a>
17
17
  <% }) %>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hexo-theme-stellar",
3
- "version": "1.32.3",
3
+ "version": "1.33.0",
4
4
  "description": "Elegant and powerful theme for Hexo.",
5
5
  "main": "package.json",
6
6
  "scripts": {
@@ -53,7 +53,7 @@ hexo.extend.filter.register('before_generate', async () => {
53
53
  hasRun = true;
54
54
 
55
55
  // 读取主题配置开关
56
- const enabled = hexo.theme.config.plugins?.lazyload?.fix_ratio === true;
56
+ const enabled = hexo.theme.config.dependencies.lazyload?.fix_ratio === true;
57
57
 
58
58
  const generateImageRatios = require('./lib/get_image_ratios');
59
59
  const fixMarkdownImages = require('./lib/fix_image_tags');
@@ -11,11 +11,17 @@ const path = require('path')
11
11
 
12
12
  module.exports = ctx => {
13
13
 
14
- const { cache, language_switcher } = ctx.theme.config
14
+ const { cache, language_switcher, system } = ctx.theme.config
15
15
  const warning = function(...args) {
16
16
  ctx.log.warn(`Since ${args[0]} is turned on, the ${args[1]} is disabled to avoid potential hazards.`)
17
17
  }
18
18
 
19
+ if (system.override_pretty_urls !== false) {
20
+ // 强制修改 pretty_urls 配置项
21
+ ctx.config.pretty_urls.trailing_index = false
22
+ ctx.config.pretty_urls.trailing_html = false
23
+ }
24
+
19
25
  if (cache && cache.enable && language_switcher) {
20
26
  warning('language_switcher', 'caching')
21
27
  cache.enable = false
@@ -0,0 +1,25 @@
1
+ // scripts/override-page-generator.js
2
+
3
+ hexo.extend.generator.register('page', function (locals) {
4
+ return locals.pages.map(page => {
5
+ const path = (
6
+ page.path.endsWith('.html') && !page.path.endsWith('/index.html')
7
+ )
8
+ ? page.path.replace(/\.html$/, '/index.html')
9
+ : page.path;
10
+
11
+ const layout = (
12
+ page.layout === false ||
13
+ page.layout === 'false' ||
14
+ typeof page.layout === 'undefined'
15
+ ) ? false : page.layout;
16
+
17
+ const isRawOutput = /\.(txt|json|xml|js|css)$/.test(path);
18
+
19
+ return {
20
+ path,
21
+ layout,
22
+ data: isRawOutput ? page.content : page
23
+ };
24
+ });
25
+ });
@@ -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
 
@@ -13,6 +13,7 @@ hexo.extend.helper.register('json_ld', function(args) {
13
13
  const structured_data = this.theme.structured_data;
14
14
  const authorEmail = config.email;
15
15
  const authorImage = config.avatar || (authorEmail ? this.gravatar(authorEmail) : null);
16
+ const isPage = page.layout == 'page';
16
17
 
17
18
  const author = {
18
19
  '@type': 'Person',
@@ -45,10 +46,10 @@ hexo.extend.helper.register('json_ld', function(args) {
45
46
  headline: page.title,
46
47
  mainEntityOfPage: {
47
48
  '@type': 'WebPage',
48
- '@id': this.url_for(page.permalink)
49
+ '@id': this.pretty_url(page.permalink)
49
50
  },
50
51
  publisher,
51
- url: this.url_for(page.permalink)
52
+ url: this.pretty_url(page.permalink)
52
53
  };
53
54
 
54
55
  if (page.tags && page.tags.length > 0) {
@@ -69,9 +70,9 @@ hexo.extend.helper.register('json_ld', function(args) {
69
70
  schema.thumbnailUrl = page.cover || page.banner;
70
71
  schema.image = images;
71
72
 
72
- } else if (this.is_page() || this.is_home()) {
73
+ } else if (isPage || this.is_home()) {
73
74
 
74
- const url = this.is_home() ? config.url : this.url_for(page.permalink);
75
+ const url = this.is_home() ? config.url : this.pretty_url(page.permalink);
75
76
  schema = {
76
77
  '@context': 'https://schema.org',
77
78
  '@type': 'Website',
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ hexo.extend.helper.register('pretty_url', function (path = '') {
4
+ let url = this.url_for(path);
5
+
6
+ // 替换 /index.html → /
7
+ url = url.replace(/\/index\.html$/, '/');
8
+
9
+ // 替换 /about.html → /about/
10
+ url = url.replace(/\.html$/, '/');
11
+
12
+ // 如果没有扩展名,并且不以 / 结尾,补一个 /
13
+ const hasExtension = /\.[a-zA-Z0-9]+$/.test(url);
14
+ if (!hasExtension && !url.endsWith('/')) {
15
+ url += '/';
16
+ }
17
+
18
+ // 去除多余斜杠(避免 // 出现,但保留://协议部分)
19
+ url = url.replace(/([^:]\/)\/+/g, '$1');
20
+
21
+ return url;
22
+ });
@@ -45,6 +45,8 @@ 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))
47
47
  hexo.extend.tag.register('video', require('./lib/video')(hexo))
48
+ hexo.extend.tag.register('rating', require('./lib/rating')(hexo))
49
+ hexo.extend.tag.register('vote', require('./lib/vote')(hexo))
48
50
 
49
51
  // read 阅读类
50
52
  hexo.extend.tag.register('reel', require('./lib/read/reel')(hexo), true)
@@ -23,7 +23,7 @@ module.exports = ctx => function(args) {
23
23
  var el = ''
24
24
  el += `<div class="tag-plugin albums-wrap">`
25
25
  if (api) {
26
- el += `<div class="data-service ds-friends" api="${api}"><div class="tag-plugin gallery grid-box" layout="grid" ratio="square" ${ctx.args.joinTags(args, ['size']).join(' ')}></div></div>`
26
+ el += `<div class="data-service ds-friends" data-api="${api}"><div class="tag-plugin gallery grid-box" layout="grid" ratio="square" ${ctx.args.joinTags(args, ['size']).join(' ')}></div></div>`
27
27
  } else if (args.group) {
28
28
  const links = ctx.theme.config.links || {}
29
29
  el += `<div class="tag-plugin gallery grid-box" layout="grid" ratio="square" ${ctx.args.joinTags(args, ['size']).join(' ')}>`
@@ -410,7 +410,7 @@ module.exports = ctx => function(args, content) {
410
410
  let urlTarget = cell['link'].includes('://') ? ' target="_blank" rel="external nofollow noopener noreferrer"' : ''
411
411
  let linkFrom = cell['from'] || 'QQ小程序'
412
412
  el += `
413
- <a class="link-card rich" href="${cell['link']}"${urlTarget} api="${ctx.theme.config.tag_plugins.chat?.api + '?url=' + cell['link']}" cardlink autofill="title,icon,desc">
413
+ <a class="link-card rich" href="${cell['link']}"${urlTarget} data-api="${ctx.theme.config.tag_plugins.chat?.api + '?url=' + cell['link']}" cardlink autofill="title,icon,desc">
414
414
  <div class="top">
415
415
  <span class="title">${cell['link']}</span>
416
416
  </div>
@@ -19,7 +19,7 @@ module.exports = ctx => function(args) {
19
19
 
20
20
  var el = `<div class="tag-plugin ${args.posts ? 'users-posts-wrap' : 'users-wrap'}">`
21
21
  if (api) {
22
- el += `<div class="data-service ds-friends${args.posts ? '_and_posts' : ''}" ${ctx.args.joinTags(args, ['size']).join(' ')} api="${api}"><div class="grid-box"></div></div>`
22
+ el += `<div class="data-service ds-friends${args.posts ? '_and_posts' : ''}" ${ctx.args.joinTags(args, ['size']).join(' ')} data-api="${api}"><div class="grid-box"></div></div>`
23
23
  } else if (args.group) {
24
24
  const links = ctx.theme.config.links || {}
25
25
  el += '<div class="grid-box">'
@@ -36,7 +36,7 @@ module.exports = ctx => function(args) {
36
36
  }
37
37
  el += ' cardlink'
38
38
  if (args.api) {
39
- el += ` api="${args.api}"`
39
+ el += ` data-api="${args.api}"`
40
40
  }
41
41
  el += ' autofill="'
42
42
  el += autofill.join(',')
@@ -23,7 +23,7 @@ module.exports = ctx => function(args) {
23
23
  var el = ''
24
24
  el += `<div class="tag-plugin posters-wrap">`
25
25
  if (api) {
26
- el += `<div class="data-service ds-friends" api="${api}"><div class="tag-plugin gallery grid-box" layout="grid" ratio="square" ${ctx.args.joinTags(args, ['size']).join(' ')}></div></div>`
26
+ el += `<div class="data-service ds-friends" data-api="${api}"><div class="tag-plugin gallery grid-box" layout="grid" ratio="square" ${ctx.args.joinTags(args, ['size']).join(' ')}></div></div>`
27
27
  } else if (args.group) {
28
28
  const links = ctx.theme.config.links || {}
29
29
  el += `<div class="tag-plugin gallery grid-box" layout="grid" ratio="portrait" ${ctx.args.joinTags(args, ['size']).join(' ')}>`
@@ -0,0 +1,32 @@
1
+ /**
2
+ * rating.js v1.0 | https://github.com/xaoxuu/hexo-theme-stellar/
3
+ * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4
+ *
5
+ * rating:
6
+ * {% rating id %}
7
+ *
8
+ */
9
+
10
+ 'use strict'
11
+
12
+ module.exports = ctx => function (args) {
13
+ args = ctx.args.map(args, ['id', 'icon'], ['title'])
14
+
15
+ const api = ctx.theme.config.data_services.rating.api
16
+ const id = args.id || 'default'
17
+
18
+ const star = ctx.utils.icon(args.icon || 'rating:star')
19
+ let el = `<div class="tag-plugin ds-rating" data-api="${api}" data-api="${api}" data-id="${id}">`
20
+ if (args.title) {
21
+ el += `<div class="header"><span>${args.title}</span></div>`
22
+ }
23
+ el += `<div class="body">`
24
+ for (let i = 1; i <= 5; i++) {
25
+ el += `<button class="star" data-value="${i}">${star}</button>`
26
+ }
27
+ el += `<span class="avg">(0.0)</span>`
28
+ el += `</div>`
29
+ el += `<div class="footer"><span class="count">0</span><span class="suffix">人已评分</span></div>`
30
+ el += `</div>`
31
+ return el.trim()
32
+ }
@@ -20,7 +20,7 @@ module.exports = ctx => function(args) {
20
20
  var el = '<div class="tag-plugin sites-wrap">'
21
21
  if (api) {
22
22
  el += '<div class="data-service ds-sites"'
23
- el += ' api="' + api + '"'
23
+ el += ' data-api="' + api + '"'
24
24
  el += '>'
25
25
  el += '<div class="grid-box"></div>'
26
26
  el += '</div>'
@@ -36,13 +36,14 @@ function layoutNodeContent(ctx, content) {
36
36
 
37
37
  module.exports = ctx => function(args, content = '') {
38
38
  args = ctx.args.map(args, ['api', 'user', 'type', 'limit', 'hide', 'avatar'])
39
+ args['data-api'] = args.api
39
40
  var el = ''
40
41
  if (!args.type) {
41
42
  args.type = 'timeline'
42
43
  }
43
44
  if (args.api && args.api.length > 0) {
44
45
  el += `<div class="tag-plugin timeline data-service ds-${args.type}"`
45
- el += ' ' + ctx.args.joinTags(args, ['api', 'user', 'limit', 'hide', 'avatar']).join(' ')
46
+ el += ' ' + ctx.args.joinTags(args, ['data-api', 'user', 'limit', 'hide', 'avatar']).join(' ')
46
47
  el += '>'
47
48
  } else {
48
49
  el += '<div class="tag-plugin timeline">'
@@ -0,0 +1,28 @@
1
+ /**
2
+ * vote.js v1.0 | https://github.com/xaoxuu/hexo-theme-stellar/
3
+ * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4
+ *
5
+ * vote:
6
+ * {% vote id %}
7
+ *
8
+ */
9
+
10
+ 'use strict'
11
+
12
+ module.exports = ctx => function (args) {
13
+ args = ctx.args.map(args, ['id', 'yes', 'no'], ['title'])
14
+
15
+ const api = ctx.theme.config.data_services.vote.api
16
+ const id = args.id || 'default'
17
+
18
+ var el = `<div class="tag-plugin ds-vote" data-api="${api}" data-id="${id}">`
19
+ if (args.title) {
20
+ el += `<div class="header"><span>${args.title}</span></div>`
21
+ }
22
+ el += `<div class="body">`
23
+ el += `<button class="vote-up">${ctx.utils.icon(args.yes || 'vote:thumbsup')} <span class="up">0</span></button>`
24
+ el += `<button class="vote-down">${ctx.utils.icon(args.no || 'vote:thumbsdown')} <span class="down">0</span></button>`
25
+ el += `</div>`
26
+ el += `</div>`
27
+ return el.trim()
28
+ }
@@ -4,6 +4,7 @@ button
4
4
  outline: none
5
5
  disable-select()
6
6
  cursor: pointer
7
+ background: none
7
8
 
8
9
  a.button
9
10
  font-weight: 500
@@ -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
@@ -91,6 +91,8 @@
91
91
 
92
92
  p>img, .tag-plugin.image .image-bg
93
93
  border-radius: $border-image-l
94
+ >img
95
+ border-radius: $border-image-l
94
96
 
95
97
 
96
98
  .l_body[type=story] .md-text.content>blockquote, .md-text.content .tag-plugin.blockquote
@@ -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