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
@@ -0,0 +1,113 @@
1
+ function getVoteKey(id) {
2
+ return `vote-${id}`;
3
+ }
4
+
5
+ function hasVoted(id) {
6
+ return !!localStorage.getItem(getVoteKey(id));
7
+ }
8
+
9
+ function getVotedValue(id) {
10
+ return localStorage.getItem(getVoteKey(id)); // 'up' 或 'down'
11
+ }
12
+
13
+ function storeVote(id, value) {
14
+ localStorage.setItem(getVoteKey(id), value);
15
+ }
16
+
17
+ function removeVote(id) {
18
+ localStorage.removeItem(getVoteKey(id));
19
+ }
20
+
21
+ function markVoted(el, value) {
22
+ el.classList.add('voted');
23
+ if (value === 'up') {
24
+ el.querySelector('.vote-up')?.classList.add('active');
25
+ } else if (value === 'down') {
26
+ el.querySelector('.vote-down')?.classList.add('active');
27
+ }
28
+ }
29
+
30
+ function revertVote(el, value) {
31
+ const id = el.dataset.id;
32
+
33
+ // 回退数值
34
+ if (value === 'up') {
35
+ const upEl = el.querySelector('.up');
36
+ if (upEl) upEl.textContent = Math.max(0, parseInt(upEl.textContent || '1') - 1);
37
+ } else if (value === 'down') {
38
+ const downEl = el.querySelector('.down');
39
+ if (downEl) downEl.textContent = Math.max(0, parseInt(downEl.textContent || '1') - 1);
40
+ }
41
+
42
+ // 回退样式和状态
43
+ el.classList.remove('voted', 'active');
44
+ el.querySelector('.vote-up')?.classList.remove('active');
45
+ el.querySelector('.vote-down')?.classList.remove('active');
46
+ removeVote(id);
47
+ }
48
+
49
+ async function loadVote(el) {
50
+ const id = el.dataset.id;
51
+ const api = el.dataset.api;
52
+ if (!id || !api) return;
53
+
54
+ try {
55
+ const res = await fetch(`${api}/info?id=${encodeURIComponent(id)}`);
56
+ const data = await res.json();
57
+
58
+ el.querySelector('.up').textContent = data.votes?.up ?? 0;
59
+ el.querySelector('.down').textContent = data.votes?.down ?? 0;
60
+ } catch (e) {
61
+ console.warn(`[vote] 加载失败: id=${id}`, e);
62
+ }
63
+ }
64
+
65
+ function submitVote(el, value) {
66
+ const id = el.dataset.id;
67
+ const api = el.dataset.api;
68
+ if (!id || !api || hasVoted(id)) return;
69
+
70
+ const upEl = el.querySelector('.up');
71
+ const downEl = el.querySelector('.down');
72
+
73
+ // 乐观更新
74
+ if (value === 'up' && upEl) upEl.textContent = parseInt(upEl.textContent || '0') + 1;
75
+ if (value === 'down' && downEl) downEl.textContent = parseInt(downEl.textContent || '0') + 1;
76
+
77
+ storeVote(id, value);
78
+ markVoted(el, value);
79
+
80
+ // 后台同步
81
+ fetch(`${api}/update?id=${encodeURIComponent(id)}&value=${encodeURIComponent(value)}`, {
82
+ method: 'POST'
83
+ }).catch(e => {
84
+ console.warn(`[vote] 后台同步失败,撤销投票: id=${id}`, e);
85
+ revertVote(el, value);
86
+ });
87
+ }
88
+
89
+ function initVotes() {
90
+ document.querySelectorAll('.ds-vote').forEach(el => {
91
+ const { id, api } = el.dataset;
92
+ if (!id || !api) return;
93
+
94
+ loadVote(el);
95
+
96
+ const votedValue = getVotedValue(id);
97
+ if (votedValue) markVoted(el, votedValue);
98
+
99
+ el.querySelector('.vote-up')?.addEventListener('click', () => {
100
+ if (!el.classList.contains('active')) submitVote(el, 'up');
101
+ });
102
+
103
+ el.querySelector('.vote-down')?.addEventListener('click', () => {
104
+ if (!el.classList.contains('active')) submitVote(el, 'down');
105
+ });
106
+ });
107
+ }
108
+
109
+ if (document.readyState === 'loading') {
110
+ window.addEventListener('DOMContentLoaded', initVotes);
111
+ } else {
112
+ initVotes();
113
+ }
@@ -4,7 +4,7 @@ utils.jq(() => {
4
4
  for (var i = 0; i < els.length; i++) {
5
5
  const el = els[i];
6
6
  const limit = parseInt(el.getAttribute('limit')) || 10;
7
- const apiBase = el.getAttribute('api');
7
+ const apiBase = el.dataset.api;
8
8
  if (apiBase == null) {
9
9
  continue;
10
10
  }
@@ -3,7 +3,7 @@ utils.jq(() => {
3
3
  const els = document.getElementsByClassName('ds-weibo');
4
4
  for (var i = 0; i < els.length; i++) {
5
5
  const el = els[i];
6
- const api = el.getAttribute('api');
6
+ const api = el.dataset.api;
7
7
  if (api == null) {
8
8
  continue;
9
9
  }
@@ -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() %>