hexo-theme-stellar 1.33.0 → 1.34.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 (83) hide show
  1. package/CLAUDE.md +217 -0
  2. package/README.md +2 -0
  3. package/_config.yml +35 -26
  4. package/docs/audits/2026-08-08-stellar-analysis.md +267 -0
  5. package/docs/release-process.md +59 -0
  6. package/layout/_partial/comments/artalk/script.ejs +56 -41
  7. package/layout/_partial/comments/beaudar/script.ejs +35 -20
  8. package/layout/_partial/comments/giscus/script.ejs +34 -19
  9. package/layout/_partial/comments/twikoo/script.ejs +29 -14
  10. package/layout/_partial/comments/utterances/script.ejs +35 -20
  11. package/layout/_partial/comments/waline/script.ejs +40 -28
  12. package/layout/_partial/cover/wiki_cover.ejs +1 -1
  13. package/layout/_partial/head.ejs +53 -4
  14. package/layout/_partial/scripts/defines.ejs +21 -3
  15. package/layout/_partial/scripts/lazyload.ejs +2 -2
  16. package/layout/_partial/scripts/services.ejs +101 -41
  17. package/layout/_partial/scripts/utils.ejs +144 -5
  18. package/layout/_partial/sidebar/index_leftbar.ejs +3 -0
  19. package/layout/_partial/sidebar/search.ejs +3 -0
  20. package/layout/_partial/widgets/ghuser.ejs +18 -4
  21. package/layout/_partial/widgets/linklist.ejs +1 -1
  22. package/layout/_partial/widgets/timeline.ejs +36 -33
  23. package/layout/_plugins/copycode.ejs +9 -5
  24. package/layout/_plugins/mathjax.ejs +47 -18
  25. package/layout/_plugins/mermaid.ejs +28 -24
  26. package/layout/_plugins/pjax.ejs +8 -0
  27. package/layout/_plugins/scrollreveal.ejs +14 -13
  28. package/layout/_plugins/swiper.ejs +2 -2
  29. package/layout/index.ejs +4 -2
  30. package/layout/index_topic.ejs +4 -2
  31. package/layout/index_wiki.ejs +6 -4
  32. package/layout/layout.ejs +3 -2
  33. package/package.json +2 -1
  34. package/scripts/commands/new-note.js +49 -0
  35. package/scripts/filters/lib/img_onerror.js +0 -2
  36. package/scripts/helpers/json_ld.js +11 -8
  37. package/scripts/helpers/pretty_url.js +5 -0
  38. package/scripts/tags/lib/chat.js +16 -9
  39. package/scripts/tags/lib/frame.js +2 -1
  40. package/scripts/tags/lib/image.js +6 -5
  41. package/scripts/tags/lib/link.js +6 -4
  42. package/scripts/tags/lib/timeline.js +101 -79
  43. package/source/css/_common/canonical.styl +11 -6
  44. package/source/css/_common/highlight.styl +4 -1
  45. package/source/css/_common/html.styl +4 -0
  46. package/source/css/_common/loading.styl +5 -1
  47. package/source/css/_components/md.styl +3 -0
  48. package/source/css/_components/pages/article-story.styl +2 -0
  49. package/source/css/_components/partial/article-footer.styl +1 -0
  50. package/source/css/_components/partial/navbar.styl +4 -2
  51. package/source/css/_components/sidebar/menu.styl +1 -0
  52. package/source/css/_components/sidebar/search.styl +4 -0
  53. package/source/css/_components/sidebar/sidebar.styl +12 -5
  54. package/source/css/_components/tag-plugins/chat.styl +6 -8
  55. package/source/css/_components/tag-plugins/folding.styl +1 -0
  56. package/source/css/_components/tag-plugins/gallery.styl +2 -0
  57. package/source/css/_components/tag-plugins/grid.styl +1 -0
  58. package/source/css/_components/tag-plugins/image.styl +3 -0
  59. package/source/css/_components/tag-plugins/link.styl +1 -0
  60. package/source/css/_components/tag-plugins/navbar.styl +15 -13
  61. package/source/css/_components/tag-plugins/note.styl +1 -0
  62. package/source/css/_components/tag-plugins/okr.styl +1 -0
  63. package/source/css/_components/tag-plugins/sites.styl +2 -0
  64. package/source/css/_components/tag-plugins/timeline.styl +9 -0
  65. package/source/css/_components/tag-plugins/vote.styl +2 -0
  66. package/source/css/_components/widgets/components.styl +1 -0
  67. package/source/css/_components/widgets/ghuser.styl +19 -3
  68. package/source/css/_components/widgets/list.styl +1 -0
  69. package/source/css/_components/widgets/toc.styl +3 -0
  70. package/source/css/_components/widgets/widgets.styl +4 -0
  71. package/source/css/_custom.styl +6 -3
  72. package/source/css/_defines/func.styl +24 -8
  73. package/source/css/_defines/theme_base.styl +1 -1
  74. package/source/css/_plugins/comments/artalk.styl +6 -2
  75. package/source/css/_plugins/comments/twikoo.styl +2 -0
  76. package/source/css/_plugins/index.styl +2 -0
  77. package/source/css/_plugins/pjax.styl +61 -0
  78. package/source/js/main.js +80 -26
  79. package/source/js/plugins/copycode.js +34 -27
  80. package/source/js/plugins/pjax.js +646 -0
  81. package/source/js/search/local-search.js +5 -2
  82. package/source/js/services/memos.js +42 -4
  83. package/source/js/services/rss.js +200 -0
@@ -28,7 +28,7 @@ utils.jq(() => {
28
28
  <div class="header">${!users.length && !hide.includes('user') ? await versionHandler.buildUser(item, memos, default_avatar) : ''}
29
29
  <span>${versionHandler.buildDate(item).toLocaleString()}</span></div>
30
30
  <div class="body">${marked.parse(item.content || '')}
31
- <div class="tag-plugin image">${versionHandler.buildImages(item, host).join('')}</div>
31
+ <p>${versionHandler.buildImages(item, host).join('')}</p>
32
32
  </div></div>`;
33
33
  }
34
34
 
@@ -39,7 +39,7 @@ utils.jq(() => {
39
39
  `<div class="user-info">${default_avatar ? `<img src="${default_avatar}">` : ''}<span>${item.creatorName}</span></div>`,
40
40
  buildDate: item => new Date(item.createdTs * 1000),
41
41
  buildImages: (item, host) => (item.resourceList || []).filter(res => res.type?.includes('image/')).map(res =>
42
- `<div class="image-bg"><img src="${res.externalLink || `https://${host}/o/r/${res.id}`}"></div>`
42
+ `<p><img src="${res.externalLink || `https://${host}/o/r/${res.id}`}"></p>`
43
43
  )
44
44
  },
45
45
  "22+": {
@@ -69,7 +69,38 @@ utils.jq(() => {
69
69
  },
70
70
  buildDate: item => new Date(item.createTime),
71
71
  buildImages: (item) => (item.resources || []).filter(res => res.type?.includes('image/')).map(res =>
72
- `<div class="image-bg"><img src="${res.externalLink || `https://${host}/o/r/${res.id}`}"></div>`
72
+ `<p><img src="${res.externalLink || `https://${host}/o/r/${res.id}`}"></p>`
73
+ )
74
+ },
75
+ "25+": {
76
+ buildUser: async (item, memos, default_avatar) => {
77
+ const creatorId = item?.creator.split('/')[1];
78
+ let user = memos.users.find(user => user.name.split('/')[1] === creatorId);
79
+ if (!user) {
80
+ if (!memos.requests[creatorId]) {
81
+ memos.requests[creatorId] = fetch(`${memos.site}/api/v1/users/${creatorId}`)
82
+ .then(response => response.json())
83
+ .then(data => {
84
+ if (data.username) {
85
+ user = data;
86
+ memos.users.push(data);
87
+ } else {
88
+ user = null;
89
+ }
90
+ })
91
+ .finally(() => delete memos.requests[creatorId]);
92
+ }
93
+ await memos.requests[creatorId];
94
+ user = memos.users.find(user => user.name.split('/')[1] === creatorId);
95
+ }
96
+ console.log(JSON.stringify(user));
97
+ const name = user ? user.displayName || user.username : 'memos';
98
+ const avatarUrl = user?.avatarUrl ? `${memos.site}${user.avatarUrl}` : default_avatar || '';
99
+ return `<div class="user-info">${avatarUrl ? `<img src="${avatarUrl}">` : ''}<span>${name}</span></div>`;
100
+ },
101
+ buildDate: item => new Date(item.createTime),
102
+ buildImages: (item) => (item.attachments || []).filter(res => res.type?.includes('image/')).map(res =>
103
+ `<div class="image-bg"><img src="${res.externalLink || `https://${host}/file/${res.name}/${res.filename}`}"></div>`
73
104
  )
74
105
  },
75
106
  "feature": {
@@ -82,9 +113,15 @@ utils.jq(() => {
82
113
  if (Array.isArray(data)) {
83
114
  memos.version = "22-";
84
115
  memos.data = data;
85
- } else if (data.memos) {
116
+ console.log("当前Memos版本为22-");
117
+ } else if (data.memos && !data.memos[0].attachments) {
86
118
  memos.version = "22+";
87
119
  memos.data = data.memos;
120
+ console.log("当前Memos版本为22+");
121
+ } else if (data.memos && data.memos[0].attachments) {
122
+ memos.version = "25+";
123
+ memos.data = data.memos;
124
+ console.log("当前Memos版本为25+");
88
125
  } else {
89
126
  memos.version = "feature";
90
127
  console.log("当前Memos版本过高,请到Stellar社区反馈");
@@ -94,3 +131,4 @@ utils.jq(() => {
94
131
  };
95
132
  });
96
133
  });
134
+
@@ -0,0 +1,200 @@
1
+ utils.jq(() => {
2
+ $(function () {
3
+ const els = document.getElementsByClassName('ds-rss');
4
+
5
+ for (let i = 0; i < els.length; i++) {
6
+ const el = els[i];
7
+ const api = el.dataset.api;
8
+ if (!api) continue;
9
+
10
+ utils.request(el, api, async resp => {
11
+ const text = await resp.text();
12
+
13
+ const head = text.slice(0, 1024).trim();
14
+ const content_type = el.getAttribute('content_type') || 'content';
15
+ const show_title = el.getAttribute('show_title') !== 'false';
16
+ const show_content = el.getAttribute('show_content') !== 'false';
17
+ const limit = parseInt(el.getAttribute('limit')) || 10;
18
+ // JSON Feed
19
+ if (head.startsWith('{') && head.includes('jsonfeed.org/version')) {
20
+ handleJsonFeed(el, JSON.parse(text), content_type, show_title, show_content, limit);
21
+ return;
22
+ }
23
+
24
+ const parser = new DOMParser();
25
+ const doc = parser.parseFromString(text, 'text/xml');
26
+
27
+ if (doc.querySelector('parsererror')) {
28
+ console.error('Failed to parse feed XML.');
29
+ return;
30
+ }
31
+ // Atom Feed
32
+ if (doc.documentElement.nodeName === 'feed' &&
33
+ doc.documentElement.namespaceURI === 'http://www.w3.org/2005/Atom') {
34
+ handleAtom(el, doc, content_type, show_title, show_content, limit);
35
+ return;
36
+ }
37
+ // RSS 2.0 Feed
38
+ if (doc.documentElement.nodeName === 'rss') {
39
+ handleRSS2(el, doc, content_type, show_title, show_content, limit);
40
+ return;
41
+ }
42
+ // RSS 1.0 Feed
43
+ if (doc.documentElement.nodeName === 'rdf:RDF') {
44
+ handleRSS1(el, doc, content_type, show_title, show_content, limit);
45
+ return;
46
+ }
47
+ });
48
+ }
49
+ });
50
+ });
51
+
52
+
53
+ function handleAtom(el, doc, content_type, show_title, show_content, limit) {
54
+ const entries = doc.querySelectorAll('entry');
55
+ const feedAuthorName = doc.querySelector('feed > author > name')?.textContent || '匿名';
56
+
57
+ const limitedEntries = Array.from(entries).slice(0, limit);
58
+
59
+ const htmlBuffer = limitedEntries.map((item, i) => {
60
+ const title = item.querySelector('title')?.textContent || '无题';
61
+ const link = item.querySelector('link')?.getAttribute('href') || '#';
62
+ const published = item.querySelector('published, updated')?.textContent;
63
+ const content = item.querySelector('content')?.textContent || '';
64
+ const summary = item.querySelector('summary')?.textContent || '';
65
+ const authorName = item.querySelector('author > name')?.textContent || feedAuthorName;
66
+
67
+ let cell = `<div class="timenode" index="${i}">`;
68
+ cell += `<div class="header">`;
69
+ cell += `<span class="user-info"><strong>${authorName}</strong></span>`;
70
+
71
+ if (published) {
72
+ const date = new Date(published);
73
+ cell += `<span>${date.toLocaleString()}</span>`;
74
+ }
75
+ cell += `</div>`;
76
+
77
+ cell += `<div class="body">`;
78
+ if (show_title) {
79
+ const titleAttr = show_content ? '' : ' style="border-bottom:none;"' ;
80
+ const linkAttr = show_content ? '' : ' style="padding-bottom:0"' ;
81
+ cell += `<p class="title"${titleAttr}><a href="${link}"${linkAttr} target="_blank" rel="external nofollow noopener noreferrer">${title}</a></p>`;
82
+ }
83
+ if (show_content){
84
+ cell += `<div class="content">${content_type === 'summary' ? summary : content}</div>`;
85
+ }
86
+ cell += `</div></div>`;
87
+
88
+ return cell;
89
+ }).join('');
90
+
91
+ $(el).append(htmlBuffer);
92
+ }
93
+
94
+ function handleRSS2(el, doc, content_type, show_title, show_content, limit) {
95
+ const items = Array.from(doc.querySelectorAll('item')).slice(0, limit);
96
+ const feedAuthorName = doc.querySelector('channel > managingEditor, channel > webMaster, channel > title')?.textContent || '匿名';
97
+
98
+ const htmlBuffer = items.map((item, i) => {
99
+ const title = item.querySelector('title')?.textContent || '无题';
100
+ const link = item.querySelector('link')?.textContent || '#';
101
+ const pubDate = item.querySelector('pubDate')?.textContent;
102
+ const content = item.getElementsByTagName('content:encoded')[0]?.textContent || '';
103
+ const description = item.getElementsByTagName('description')[0]?.textContent || '';
104
+ const authorName = item.querySelector('author, creator')?.textContent || feedAuthorName;
105
+
106
+ let cell = `<div class="timenode" index="${i}">`;
107
+ cell += `<div class="header">`;
108
+ cell += `<span class="user-info"><strong>${authorName}</strong></span>`;
109
+ if (pubDate) {
110
+ const date = new Date(pubDate);
111
+ cell += `<span>${isNaN(date) ? pubDate : date.toLocaleString()}</span>`;
112
+ }
113
+ cell += `</div>`;
114
+ cell += `<div class="body">`;
115
+ if (show_title) {
116
+ const titleAttr = show_content ? '' : ' style="border-bottom:none;"' ;
117
+ const linkAttr = show_content ? '' : ' style="padding-bottom:0"' ;
118
+ cell += `<p class="title"${titleAttr}><a href="${link}"${linkAttr} target="_blank" rel="external nofollow noopener noreferrer">${title}</a></p>`;
119
+ }
120
+ if (show_content){
121
+ cell += `<div class="content">${content_type === 'summary' ? description : content}</div>`;
122
+ }
123
+ cell += `</div></div>`;
124
+ return cell;
125
+ }).join('');
126
+
127
+ $(el).append(htmlBuffer);
128
+ }
129
+ function handleRSS1(el, doc, content_type, show_title, show_content, limit) {
130
+ const items = Array.from(doc.querySelectorAll('item')).slice(0, limit);
131
+ const feedTitle = doc.querySelector('channel > title')?.textContent || '匿名';
132
+
133
+ const htmlBuffer = items.map((item, i) => {
134
+ const title = item.querySelector('title')?.textContent || '无题';
135
+ const link = item.querySelector('link')?.textContent || '#';
136
+ const pubDate = (item.querySelector('pubDate') || item.getElementsByTagName('dc:date')[0])?.textContent;
137
+ const content = item.getElementsByTagName('content:encoded')[0]?.textContent || '';
138
+ const description = (item.querySelector('description') || item.querySelector('summary'))?.textContent || '';
139
+ const authorName = (item.querySelector('author') || item.getElementsByTagName('dc:creator')[0])?.textContent || feedTitle;
140
+
141
+ let cell = `<div class="timenode" index="${i}">`;
142
+ cell += `<div class="header">`;
143
+ cell += `<span class="user-info"><strong>${authorName}</strong></span>`;
144
+ if (pubDate) {
145
+ const date = new Date(pubDate);
146
+ cell += `<span>${isNaN(date) ? pubDate : date.toLocaleString()}</span>`;
147
+ }
148
+ cell += `</div>`;
149
+ cell += `<div class="body">`;
150
+ if (show_title) {
151
+ const titleAttr = show_content ? '' : ' style="border-bottom:none;"' ;
152
+ const linkAttr = show_content ? '' : ' style="padding-bottom:0"' ;
153
+ cell += `<p class="title"${titleAttr}><a href="${link}"${linkAttr} target="_blank" rel="external nofollow noopener noreferrer">${title}</a></p>`;
154
+ }
155
+ if (show_content){
156
+ cell += `<div class="content">${content_type === 'summary' ? description : content}</div>`;
157
+ }
158
+ cell += `</div></div>`;
159
+ return cell;
160
+ }).join('');
161
+
162
+ $(el).append(htmlBuffer);
163
+ }
164
+ function handleJsonFeed(el, data, content_type, show_title, show_content, limit) {
165
+ const items = (data.items || []).slice(0, limit);
166
+ const feedAuthorName = data.authors?.[0]?.name || data.title || '匿名';
167
+
168
+ const htmlBuffer = items.map((item, i) => {
169
+ const id = item.id || i;
170
+ const title = item.title || '无题';
171
+ const link = item.url || '#';
172
+ const pubDate = item.date_published;
173
+ const content = item.content_html || '';
174
+ const summary = item.summary || '';
175
+
176
+ let authorName = (item.authors || []).map(a => a.name).filter(Boolean).join(' ') || feedAuthorName;
177
+
178
+ let cell = `<div class="timenode" index="${id}">`;
179
+ cell += `<div class="header">`;
180
+ cell += `<span class="user-info"><strong>${authorName}</strong></span>`;
181
+ if (pubDate) {
182
+ const date = new Date(pubDate);
183
+ cell += `<span>${isNaN(date.getTime()) ? pubDate : date.toLocaleString()}</span>`;
184
+ }
185
+ cell += `</div>`;
186
+ cell += `<div class="body">`;
187
+ if (show_title) {
188
+ const titleAttr = show_content ? '' : ' style="border-bottom:none;"' ;
189
+ const linkAttr = show_content ? '' : ' style="padding-bottom:0"' ;
190
+ cell += `<p class="title"${titleAttr}><a href="${link}"${linkAttr} target="_blank" rel="external nofollow noopener noreferrer">${title}</a></p>`;
191
+ }
192
+ if (show_content){
193
+ cell += `<div class="content">${content_type === 'summary' ? summary : content}</div>`;
194
+ }
195
+ cell += `</div></div>`;
196
+ return cell;
197
+ }).join('');
198
+
199
+ $(el).append(htmlBuffer);
200
+ }