hexo-theme-stellar 1.39.0 → 1.40.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.
- package/.claude/skills/stellar-theme-dev/SKILL.md +2 -2
- package/CHANGELOG.md +44 -0
- package/_config.yml +8 -2
- package/languages/en.yml +2 -0
- package/languages/zh-CN.yml +2 -0
- package/languages/zh-TW.yml +2 -0
- package/layout/_partial/comments/artalk/script.ejs +8 -0
- package/layout/_partial/cover/post_cover.ejs +1 -1
- package/layout/_partial/head.ejs +11 -2
- package/layout/_partial/main/article/article_footer.ejs +13 -10
- package/layout/_partial/main/navbar/article_banner.ejs +7 -0
- package/layout/_partial/main/navbar/dateinfo.ejs +1 -1
- package/layout/_partial/main/navbar/nav_tabs_blog.ejs +6 -3
- package/layout/_partial/main/navbar/nav_tabs_wiki.ejs +1 -1
- package/layout/_partial/main/pin_slider.ejs +11 -12
- package/layout/_partial/main/post_list/post_card.ejs +24 -13
- package/layout/_partial/sidebar/index_leftbar.ejs +2 -2
- package/layout/_partial/widgets/markdown.ejs +1 -1
- package/layout/_partial/widgets/toc.ejs +8 -1
- package/layout/categories.ejs +18 -8
- package/layout/index.ejs +67 -11
- package/layout/page.ejs +5 -1
- package/package.json +5 -3
- package/scripts/commands/new-note.js +2 -2
- package/scripts/helpers/category_tree.js +11 -0
- package/scripts/helpers/escape_html.js +9 -0
- package/scripts/helpers/json_ld.js +5 -0
- package/scripts/helpers/mdrender.js +29 -0
- package/scripts/helpers/reading_time.js +12 -0
- package/scripts/helpers/related_posts.js +9 -5
- package/scripts/lib/category_tree.js +26 -0
- package/scripts/lib/mdrender_html.js +97 -0
- package/scripts/lib/reading_time.js +16 -0
- package/scripts/lib/wiki_readme.js +57 -0
- package/scripts/tags/index.js +2 -0
- package/scripts/tags/lib/md.js +17 -9
- package/scripts/tags/lib/table.js +22 -0
- package/scripts/tags/lib/timeline.js +9 -9
- package/scripts/tags/lib/tip.js +23 -0
- package/source/css/_common/device.styl +3 -0
- package/source/css/_components/list.styl +11 -5
- package/source/css/_components/pages/archives.styl +7 -3
- package/source/css/_components/partial/bread-nav.styl +9 -1
- package/source/css/_components/sidebar/search.styl +6 -4
- package/source/css/_components/sidebar/sidebar.styl +10 -7
- package/source/css/_components/tag-plugins/table.styl +37 -0
- package/source/css/_components/tag-plugins/tip.styl +27 -0
- package/source/css/_custom.styl +1 -1
- package/source/css/_defines/func.styl +19 -13
- package/source/css/_defines/theme_base.styl +3 -0
- package/source/js/main.js +92 -1
- package/source/js/services/mdrender.js +116 -7
- package/source/js/services/memos.js +60 -8
- package/source/js/services/waline_latest_comment.js +3 -1
|
@@ -63,12 +63,14 @@
|
|
|
63
63
|
filter: saturate(var(--saturate)) blur(var(--blur-px)) opacity(var(--background-opacity))
|
|
64
64
|
:root[data-theme="dark"] &
|
|
65
65
|
--background-opacity: hexo-config('style.leftbar.background-opacity') * 0.75
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
:root:not([data-theme]) &
|
|
67
|
+
@media screen and (prefers-color-scheme: dark)
|
|
68
|
+
--background-opacity: hexo-config('style.leftbar.background-opacity') * 0.75
|
|
68
69
|
else if $leftbar-background-color-light || $leftbar-background-color-dark
|
|
69
70
|
background-color: convert($leftbar-background-color-light)
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
:root:not([data-theme]) &
|
|
72
|
+
@media screen and (prefers-color-scheme: dark)
|
|
73
|
+
background-color: convert($leftbar-background-color-dark)
|
|
72
74
|
|
|
73
75
|
.leftbar-container
|
|
74
76
|
height: calc(100vh - var(--gap-margin) * 2)
|
|
@@ -109,9 +111,10 @@
|
|
|
109
111
|
:root[data-theme="dark"] &
|
|
110
112
|
background: rgba(white, 0.05)
|
|
111
113
|
box-shadow: inset 0 0 32px 1px rgba(white, 0.1)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
:root:not([data-theme]) &
|
|
115
|
+
@media screen and (prefers-color-scheme: dark)
|
|
116
|
+
background: rgba(white, 0.05)
|
|
117
|
+
box-shadow: inset 0 0 32px 1px rgba(white, 0.1)
|
|
115
118
|
|
|
116
119
|
@media screen and (max-width: $device-mobile-max)
|
|
117
120
|
.l_left
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// 表格标签(#466):{% table style:scroll|wrap|compact %}
|
|
2
|
+
.tag-plugin.table
|
|
3
|
+
table
|
|
4
|
+
border-collapse: collapse
|
|
5
|
+
width: 100%
|
|
6
|
+
&.table-scroll
|
|
7
|
+
overflow-x: auto
|
|
8
|
+
-webkit-overflow-scrolling: touch
|
|
9
|
+
scrollbar()
|
|
10
|
+
table
|
|
11
|
+
min-width: max-content
|
|
12
|
+
th, td
|
|
13
|
+
padding: 8px 1em
|
|
14
|
+
&.table-wrap
|
|
15
|
+
table
|
|
16
|
+
table-layout: fixed
|
|
17
|
+
border-collapse: separate
|
|
18
|
+
border-spacing: 0
|
|
19
|
+
border: 1px solid var(--block-border)
|
|
20
|
+
border-radius: $border-card
|
|
21
|
+
overflow: hidden
|
|
22
|
+
tr
|
|
23
|
+
white-space: normal
|
|
24
|
+
th
|
|
25
|
+
border-top: none
|
|
26
|
+
th, td
|
|
27
|
+
word-break: break-word
|
|
28
|
+
overflow-wrap: anywhere
|
|
29
|
+
padding: 8px 1em
|
|
30
|
+
border-left: 1px solid var(--block-border)
|
|
31
|
+
&:first-child
|
|
32
|
+
border-left: none
|
|
33
|
+
&.table-compact
|
|
34
|
+
table
|
|
35
|
+
th, td
|
|
36
|
+
padding: 4px 1em
|
|
37
|
+
line-height: 1.4
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// 气泡注解标签(#602):{% tip text:注解 %}词句{% endtip %}
|
|
2
|
+
.tag-plugin.tip
|
|
3
|
+
position: relative
|
|
4
|
+
display: inline-block
|
|
5
|
+
border-bottom: 1px dashed var(--text-p3)
|
|
6
|
+
cursor: help
|
|
7
|
+
.tip-bubble
|
|
8
|
+
position: absolute
|
|
9
|
+
bottom: 135%
|
|
10
|
+
left: 50%
|
|
11
|
+
transform: translateX(-50%)
|
|
12
|
+
display: block
|
|
13
|
+
width: max-content
|
|
14
|
+
max-width: 18rem
|
|
15
|
+
padding: .35rem .7rem
|
|
16
|
+
bar-glass($border-bar)
|
|
17
|
+
text-shadow: none
|
|
18
|
+
color: var(--text-p1)
|
|
19
|
+
font-size: $fs-14
|
|
20
|
+
line-height: 1.5
|
|
21
|
+
white-space: normal
|
|
22
|
+
opacity: 0
|
|
23
|
+
pointer-events: none
|
|
24
|
+
transition: opacity .2s ease
|
|
25
|
+
z-index: 10
|
|
26
|
+
&:hover .tip-bubble, &:focus .tip-bubble
|
|
27
|
+
opacity: 1
|
package/source/css/_custom.styl
CHANGED
|
@@ -63,7 +63,7 @@ $border-image-l = convert(hexo-config('style.border-radius.image-l'))
|
|
|
63
63
|
$border-image = convert(hexo-config('style.border-radius.image'))
|
|
64
64
|
$border-image-s = convert(hexo-config('style.border-radius.image-s'))
|
|
65
65
|
$border-button = 8px
|
|
66
|
-
$corner-shape = convert(hexo-config('style.corner-shape')
|
|
66
|
+
$corner-shape = convert(hexo-config('style.corner-shape'))
|
|
67
67
|
|
|
68
68
|
// 可以动态变化的属性
|
|
69
69
|
:root
|
|
@@ -143,9 +143,10 @@ sidebar-light($bg = unquote('var(--bg-a50)'), $light = 0.38, $edge = 0.45, $dark
|
|
|
143
143
|
:root[data-theme="dark"] &
|
|
144
144
|
background: var(--leftbar-item-bg, linear-gradient(180deg, rgba(white, $dark-light), rgba(white, 0) 50%), $bg)
|
|
145
145
|
box-shadow: var(--leftbar-item-shadow, inset 0 1px 0 rgba(white, $dark-edge))
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
:root:not([data-theme]) &
|
|
147
|
+
@media (prefers-color-scheme: dark)
|
|
148
|
+
background: var(--leftbar-item-bg, linear-gradient(180deg, rgba(white, $dark-light), rgba(white, 0) 50%), $bg)
|
|
149
|
+
box-shadow: var(--leftbar-item-shadow, inset 0 1px 0 rgba(white, $dark-edge))
|
|
149
150
|
|
|
150
151
|
newblur($radius = 64px)
|
|
151
152
|
box-shadow: 0 0 2px rgba(black, .04), 0 2px 8px rgba(black, .04), 0 4px 16px rgba(black, .04)
|
|
@@ -153,14 +154,16 @@ newblur($radius = 64px)
|
|
|
153
154
|
text-shadow: 0 0 4px rgba(white, 1), 0 0 8px rgba(white, 0.8), 0 0 16px rgba(white, 0.6)
|
|
154
155
|
:root[data-theme="dark"] &
|
|
155
156
|
text-shadow: 0 0 2px rgba(black, 0.2), 0 0 4px rgba(black, 0.4)
|
|
156
|
-
|
|
157
|
-
|
|
157
|
+
:root:not([data-theme]) &
|
|
158
|
+
@media (prefers-color-scheme: dark)
|
|
159
|
+
text-shadow: 0 0 2px rgba(black, 0.2), 0 0 4px rgba(black, 0.4)
|
|
158
160
|
nav a:not(.active,:hover)
|
|
159
161
|
filter: drop-shadow(0 0 0 white) drop-shadow(0 0 4px white)
|
|
160
162
|
:root[data-theme="dark"] &
|
|
161
163
|
filter: none
|
|
162
|
-
|
|
163
|
-
|
|
164
|
+
:root:not([data-theme]) &
|
|
165
|
+
@media (prefers-color-scheme: dark)
|
|
166
|
+
filter: none
|
|
164
167
|
&:before,&:after
|
|
165
168
|
z-index -1
|
|
166
169
|
position: absolute
|
|
@@ -179,8 +182,9 @@ newblur($radius = 64px)
|
|
|
179
182
|
margin: 1px
|
|
180
183
|
:root[data-theme="dark"] &
|
|
181
184
|
background: rgba(white, 0.2)
|
|
182
|
-
|
|
183
|
-
|
|
185
|
+
:root:not([data-theme]) &
|
|
186
|
+
@media (prefers-color-scheme: dark)
|
|
187
|
+
background: rgba(white, 0.2)
|
|
184
188
|
&:after
|
|
185
189
|
--blur-px: 16px
|
|
186
190
|
--blur-sat: 300%
|
|
@@ -192,8 +196,9 @@ newblur($radius = 64px)
|
|
|
192
196
|
trans1 all
|
|
193
197
|
:root[data-theme="dark"] &
|
|
194
198
|
box-shadow: inset 0 0 2px 1px rgba(white, 0.2)
|
|
195
|
-
|
|
196
|
-
|
|
199
|
+
:root:not([data-theme]) &
|
|
200
|
+
@media (prefers-color-scheme: dark)
|
|
201
|
+
box-shadow: inset 0 0 2px 1px rgba(white, 0.2)
|
|
197
202
|
|
|
198
203
|
// 长条圆角玻璃 UI(navbar top 与 float-panel 共用)
|
|
199
204
|
bar-glass($radius = $border-bar-container)
|
|
@@ -220,8 +225,9 @@ bar-item-active()
|
|
|
220
225
|
background: var(--bg-a60)
|
|
221
226
|
:root[data-theme="dark"] &
|
|
222
227
|
background: rgba(white, 0.25)
|
|
223
|
-
|
|
224
|
-
|
|
228
|
+
:root:not([data-theme]) &
|
|
229
|
+
@media (prefers-color-scheme: dark)
|
|
230
|
+
background: rgba(white, 0.25)
|
|
225
231
|
box-shadow: 0 0 1px rgba(0, 0, 0, 0.04), 0 0 2px rgba(0, 0, 0, 0.04), 0 0 4px rgba(0, 0, 0, 0.08)
|
|
226
232
|
backdrop-filter: saturate(300%)
|
|
227
233
|
-webkit-backdrop-filter: saturate(300%)
|
package/source/js/main.js
CHANGED
|
@@ -132,14 +132,105 @@ function smoothScrollTo(targetY) {
|
|
|
132
132
|
window.addEventListener('wheel', cancelSmoothScroll, { passive: true });
|
|
133
133
|
window.addEventListener('touchstart', cancelSmoothScroll, { passive: true });
|
|
134
134
|
|
|
135
|
+
// 远程 md(mdrender 服务)渲染完成后重建右栏 TOC:结构与服务端 toc() 输出一致
|
|
136
|
+
let tocClickBound = false;
|
|
137
|
+
function rebuildToc(scope) {
|
|
138
|
+
const widget = document.querySelector('#data-toc');
|
|
139
|
+
if (!widget) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const body = widget.querySelector('.widget-body');
|
|
143
|
+
if (!body) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const article = scope && scope.closest ? scope.closest('article.md-text') : null;
|
|
147
|
+
const root = article || document.querySelector('article.md-text');
|
|
148
|
+
if (!root) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const headings = root.querySelectorAll('h1,h2,h3,h4,h5,h6');
|
|
152
|
+
if (headings.length === 0) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const ol = document.createElement('ol');
|
|
156
|
+
ol.className = 'toc';
|
|
157
|
+
const stack = [];
|
|
158
|
+
headings.forEach(function (h) {
|
|
159
|
+
const id = h.id;
|
|
160
|
+
if (!id) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const level = parseInt(h.tagName.substring(1), 10);
|
|
164
|
+
const li = document.createElement('li');
|
|
165
|
+
li.className = 'toc-item toc-level-' + level;
|
|
166
|
+
const a = document.createElement('a');
|
|
167
|
+
a.className = 'toc-link';
|
|
168
|
+
a.href = '#' + encodeURIComponent(id);
|
|
169
|
+
const span = document.createElement('span');
|
|
170
|
+
span.className = 'toc-text';
|
|
171
|
+
span.textContent = h.textContent.trim();
|
|
172
|
+
a.appendChild(span);
|
|
173
|
+
li.appendChild(a);
|
|
174
|
+
while (stack.length > 0 && stack[stack.length - 1].level >= level) {
|
|
175
|
+
stack.pop();
|
|
176
|
+
}
|
|
177
|
+
if (stack.length === 0) {
|
|
178
|
+
ol.appendChild(li);
|
|
179
|
+
} else {
|
|
180
|
+
const parent = stack[stack.length - 1];
|
|
181
|
+
if (!parent.childOl) {
|
|
182
|
+
parent.childOl = document.createElement('ol');
|
|
183
|
+
parent.childOl.className = 'toc-child';
|
|
184
|
+
parent.li.appendChild(parent.childOl);
|
|
185
|
+
}
|
|
186
|
+
parent.childOl.appendChild(li);
|
|
187
|
+
}
|
|
188
|
+
stack.push({ level: level, li: li });
|
|
189
|
+
});
|
|
190
|
+
body.innerHTML = '';
|
|
191
|
+
body.appendChild(ol);
|
|
192
|
+
bindTocClick(widget);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function bindTocClick(widget) {
|
|
196
|
+
if (tocClickBound) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
tocClickBound = true;
|
|
200
|
+
widget.addEventListener('click', function (e) {
|
|
201
|
+
const link = e.target.closest('a.toc-link');
|
|
202
|
+
if (!link) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const href = link.getAttribute('href');
|
|
206
|
+
const id = href && href.indexOf('#') === 0 ? decodeURIComponent(href.slice(1)) : null;
|
|
207
|
+
const target = id && document.getElementById(id);
|
|
208
|
+
if (target) {
|
|
209
|
+
e.preventDefault();
|
|
210
|
+
const offset = 32;
|
|
211
|
+
const targetY = target.getBoundingClientRect().top + window.scrollY - offset;
|
|
212
|
+
smoothScrollTo(targetY);
|
|
213
|
+
if (window.history && window.history.pushState) {
|
|
214
|
+
window.history.pushState(null, '', href);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// 远程 md 渲染完成后由页面层重建右栏 TOC
|
|
221
|
+
document.addEventListener('stellar:mdrender', function (e) {
|
|
222
|
+
rebuildToc(e.detail && e.detail.target);
|
|
223
|
+
});
|
|
224
|
+
|
|
135
225
|
|
|
136
226
|
const init = {
|
|
137
227
|
toc: () => {
|
|
138
228
|
const scrollOffset = 32;
|
|
139
229
|
// 滚动位置取整后标题顶可能落在偏移线下方 1~2px,加容差避免高亮回跳到上一条
|
|
140
230
|
const scrollTolerance = 4;
|
|
141
|
-
var segs = utils.qsa("article.md-text h1, article.md-text h2, article.md-text h3, article.md-text h4, article.md-text h5, article.md-text h6");
|
|
142
231
|
function activeTOC() {
|
|
232
|
+
// 每次滚动动态查询:远程 md 内容渲染后标题才存在
|
|
233
|
+
var segs = utils.qsa("article.md-text h1, article.md-text h2, article.md-text h3, article.md-text h4, article.md-text h5, article.md-text h6");
|
|
143
234
|
var scrollTop = window.scrollY;
|
|
144
235
|
var topSeg = null;
|
|
145
236
|
for (var i = 0; i < segs.length; i++) {
|
|
@@ -1,12 +1,121 @@
|
|
|
1
|
+
/* global module */
|
|
2
|
+
/**
|
|
3
|
+
* mdrender.js v2 | https://github.com/xaoxuu/hexo-theme-stellar/
|
|
4
|
+
*
|
|
5
|
+
* 远程 Markdown 渲染底层组件的客户端部分:
|
|
6
|
+
* - 无 data-replace:渲染结果填充进占位元素(容器保留,兼容现状);
|
|
7
|
+
* - data-replace="true":占位元素原地替换为渲染结果(最终 DOM 无外部容器);
|
|
8
|
+
* - data-base:相对图片/链接解析到该基址(GitHub 镜像优先);
|
|
9
|
+
* - data-heading="true":标题适配本地文章格式(补 id + headerlink;h1 为页面标题直接隐藏);
|
|
10
|
+
* - 渲染完成后派发 stellar:mdrender 事件(detail.target 为渲染后的占位元素),
|
|
11
|
+
* 页面层(main.js)监听后重建右栏 TOC。
|
|
12
|
+
*/
|
|
1
13
|
(function () {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
14
|
+
'use strict';
|
|
15
|
+
|
|
16
|
+
// 与 hexo-util slugize(transform=1)保持一致:去 HTML、转小写、特殊字符转 -、
|
|
17
|
+
// 连续分隔符合并、首尾分隔符去除(未做 diacritic 归一化,常见标题不受影响)。
|
|
18
|
+
function slugifyHeading(text) {
|
|
19
|
+
return String(text)
|
|
20
|
+
.replace(/<[^>]*>/g, '')
|
|
21
|
+
// eslint-disable-next-line no-control-regex
|
|
22
|
+
.replace(/[\u0000-\u001f]/g, '')
|
|
23
|
+
.replace(/[\s~`!@#$%^&*()\-_+=[\]{}|\\;:"'<>,.?/]+/g, '-')
|
|
24
|
+
.replace(/-{2,}/g, '-')
|
|
25
|
+
.replace(/^-+|-+$/g, '')
|
|
26
|
+
.toLowerCase();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 标题适配:h1 为页面标题(banner 已展示),直接隐藏不降级;其余标题按序补齐 id(冲突加后缀)
|
|
30
|
+
function normalizeHeadings(container, usedIds) {
|
|
31
|
+
const headings = container.querySelectorAll('h1,h2,h3,h4,h5,h6');
|
|
32
|
+
if (headings.length === 0) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
headings.forEach(function (h) {
|
|
36
|
+
if (h.tagName === 'H1') {
|
|
37
|
+
h.remove();
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
container.querySelectorAll('h1,h2,h3,h4,h5,h6').forEach(function (h) {
|
|
41
|
+
const base = slugifyHeading(h.textContent) || 'section';
|
|
42
|
+
let id = base;
|
|
43
|
+
let suffix = 1;
|
|
44
|
+
while (usedIds.has(id)) {
|
|
45
|
+
id = base + '-' + suffix;
|
|
46
|
+
suffix++;
|
|
47
|
+
}
|
|
48
|
+
usedIds.add(id);
|
|
49
|
+
h.id = id;
|
|
50
|
+
// 与本地 hexo-renderer-marked 输出一致:标题内追加 headerlink 锚点
|
|
51
|
+
const anchor = document.createElement('a');
|
|
52
|
+
anchor.setAttribute('href', '#' + id);
|
|
53
|
+
anchor.className = 'headerlink';
|
|
54
|
+
anchor.setAttribute('title', h.textContent.trim());
|
|
55
|
+
h.insertBefore(anchor, h.firstChild);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 相对图片/链接解析到 data-base(# 锚点、绝对 URL、协议相对地址等不动)
|
|
60
|
+
function rewriteRelativeUrls(container, base) {
|
|
61
|
+
container.querySelectorAll('img[src],a[href]').forEach(function (el) {
|
|
62
|
+
const attr = el.tagName === 'IMG' ? 'src' : 'href';
|
|
63
|
+
const val = el.getAttribute(attr);
|
|
64
|
+
if (!val) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (/^(#|https?:|mailto:|tel:|data:|javascript:)/i.test(val) || val.startsWith('//')) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
el.setAttribute(attr, new URL(val, base).href);
|
|
72
|
+
} catch (e) {
|
|
73
|
+
// 无法解析的相对地址保持原样
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function render(el) {
|
|
79
|
+
const src = el.getAttribute('src');
|
|
80
|
+
if (!src) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const url = src + '?t=' + new Date().getTime();
|
|
84
|
+
utils.request(el, url, async resp => {
|
|
8
85
|
const data = await resp.text();
|
|
9
|
-
|
|
86
|
+
const tmp = document.createElement('div');
|
|
87
|
+
tmp.innerHTML = marked.parse(data);
|
|
88
|
+
const base = el.getAttribute('data-base');
|
|
89
|
+
if (base) {
|
|
90
|
+
rewriteRelativeUrls(tmp, base);
|
|
91
|
+
}
|
|
92
|
+
if (el.getAttribute('data-heading') === 'true') {
|
|
93
|
+
const usedIds = new Set();
|
|
94
|
+
const scope = el.closest('article, .md-text') || document;
|
|
95
|
+
scope.querySelectorAll('[id]').forEach(function (node) {
|
|
96
|
+
usedIds.add(node.id);
|
|
97
|
+
});
|
|
98
|
+
normalizeHeadings(tmp, usedIds);
|
|
99
|
+
}
|
|
100
|
+
if (el.getAttribute('data-replace') === 'true') {
|
|
101
|
+
el.replaceWith.apply(el, Array.from(tmp.childNodes));
|
|
102
|
+
} else {
|
|
103
|
+
el.innerHTML = tmp.innerHTML;
|
|
104
|
+
}
|
|
105
|
+
document.dispatchEvent(new CustomEvent('stellar:mdrender', { detail: { target: el } }));
|
|
10
106
|
});
|
|
11
107
|
}
|
|
108
|
+
|
|
109
|
+
// Node 单测入口:仅导出纯函数,浏览器环境不命中此分支
|
|
110
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
111
|
+
module.exports = {
|
|
112
|
+
slugifyHeading: slugifyHeading
|
|
113
|
+
};
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const els = document.getElementsByClassName('ds-mdrender');
|
|
118
|
+
for (var i = 0; i < els.length; i++) {
|
|
119
|
+
render(els[i]);
|
|
120
|
+
}
|
|
12
121
|
})();
|
|
@@ -112,6 +112,49 @@
|
|
|
112
112
|
`<div class="image-bg"><img src="${res.externalLink || `https://${host}/file/${res.name}/${res.filename}`}"></div>`
|
|
113
113
|
)
|
|
114
114
|
},
|
|
115
|
+
"v1": {
|
|
116
|
+
buildUser: async (item, memos, default_avatar) => {
|
|
117
|
+
const creator = item?.creator || '';
|
|
118
|
+
const creatorId = creator.split('/')[1] || creator;
|
|
119
|
+
const findUser = () => memos.users.find(user =>
|
|
120
|
+
(user.name || '').split('/')[1] === creatorId || String(user.id) === creatorId
|
|
121
|
+
);
|
|
122
|
+
let user = findUser();
|
|
123
|
+
if (!user && !memos.requests[creatorId]) {
|
|
124
|
+
memos.requests[creatorId] = utils.requestWithoutLoading(`${memos.site}/api/v1/users/${encodeURIComponent(creator)}`, { service: 'memos-user' })
|
|
125
|
+
.then(response => response.json())
|
|
126
|
+
.then(data => {
|
|
127
|
+
if (data.username) {
|
|
128
|
+
memos.users.push(data);
|
|
129
|
+
user = data;
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
.catch(() => {})
|
|
133
|
+
.finally(() => delete memos.requests[creatorId]);
|
|
134
|
+
}
|
|
135
|
+
if (memos.requests[creatorId]) {
|
|
136
|
+
await memos.requests[creatorId];
|
|
137
|
+
user = findUser();
|
|
138
|
+
}
|
|
139
|
+
const name = user ? user.displayName || user.username : 'memos';
|
|
140
|
+
const avatarUrl = user?.avatarUrl
|
|
141
|
+
? (user.avatarUrl.startsWith('http') ? user.avatarUrl : `${memos.site}${user.avatarUrl}`)
|
|
142
|
+
: default_avatar || '';
|
|
143
|
+
return `<div class="user-info">${avatarUrl ? `<img src="${avatarUrl}">` : ''}<span>${name}</span></div>`;
|
|
144
|
+
},
|
|
145
|
+
buildDate: item => {
|
|
146
|
+
const d = new Date(item.createTime);
|
|
147
|
+
return isNaN(d.getTime()) ? new Date() : d;
|
|
148
|
+
},
|
|
149
|
+
buildImages: (item, host) => {
|
|
150
|
+
const list = (item.attachments || item.resources || []).filter(res =>
|
|
151
|
+
res && res.type && String(res.type).includes('image/')
|
|
152
|
+
);
|
|
153
|
+
return list.map(res =>
|
|
154
|
+
`<div class="image-bg"><img src="${res.externalLink || res.url || `https://${host}/file/${res.name}/${res.filename}`}"></div>`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
},
|
|
115
158
|
"feature": {
|
|
116
159
|
buildUser: async () => "memos",
|
|
117
160
|
buildDate: () => new Date(),
|
|
@@ -123,14 +166,23 @@
|
|
|
123
166
|
memos.version = "22-";
|
|
124
167
|
memos.data = data;
|
|
125
168
|
console.log("当前Memos版本为22-");
|
|
126
|
-
} else if (data.memos &&
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
169
|
+
} else if (data && Array.isArray(data.memos) && data.memos.length > 0) {
|
|
170
|
+
const first = data.memos[0];
|
|
171
|
+
const creatorId = String(first.creator || '').split('/')[1] || '';
|
|
172
|
+
const isV1 = first.createTime != null && creatorId.length > 0 && !/^\d+$/.test(creatorId);
|
|
173
|
+
if (isV1) {
|
|
174
|
+
memos.version = "v1";
|
|
175
|
+
memos.data = data.memos;
|
|
176
|
+
console.log("当前Memos版本为v1");
|
|
177
|
+
} else if (first.attachments) {
|
|
178
|
+
memos.version = "25+";
|
|
179
|
+
memos.data = data.memos;
|
|
180
|
+
console.log("当前Memos版本为25+");
|
|
181
|
+
} else {
|
|
182
|
+
memos.version = "22+";
|
|
183
|
+
memos.data = data.memos;
|
|
184
|
+
console.log("当前Memos版本为22+");
|
|
185
|
+
}
|
|
134
186
|
} else {
|
|
135
187
|
memos.version = "feature";
|
|
136
188
|
console.log("当前Memos版本过高,请到Stellar社区反馈");
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
const api = apiBase + '/comment?type=recent&count=' + limit;
|
|
11
11
|
const default_avatar = def.avatar;
|
|
12
12
|
utils.request(el, api, async resp => {
|
|
13
|
-
const
|
|
13
|
+
const payload = await resp.json();
|
|
14
|
+
// 兼容旧版数组与新版 { data: [...] } 返回结构(#630)
|
|
15
|
+
const data = Array.isArray(payload) ? payload : (payload && Array.isArray(payload.data) ? payload.data : []);
|
|
14
16
|
data.forEach((item, i) => {
|
|
15
17
|
var cell = '<div class="timenode" index="' + i + '">';
|
|
16
18
|
cell += '<div class="header">';
|