hexo-theme-stellar 1.37.0 → 1.39.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 +50 -0
- package/CHANGELOG.md +75 -0
- package/CONTRIBUTING.md +155 -0
- package/README.md +56 -35
- package/_config.yml +16 -13
- package/_data/icons.yml +3 -0
- package/languages/en.yml +37 -0
- package/languages/zh-CN.yml +37 -0
- package/languages/zh-TW.yml +37 -0
- package/layout/_partial/comments/layout.ejs +2 -1
- package/layout/_partial/main/navbar/article_banner.ejs +11 -5
- package/layout/_partial/main/navbar/nav_tabs_blog.ejs +2 -1
- package/layout/_partial/main/navbar/nav_tabs_wiki.ejs +1 -0
- package/layout/_partial/main/notebook/paginator.ejs +3 -2
- package/layout/_partial/main/pin_slider.ejs +325 -0
- package/layout/_partial/main/post_list/paginator.ejs +4 -3
- package/layout/_partial/main/post_list/post_card.ejs +2 -4
- package/layout/_partial/scripts/defines.ejs +2 -1
- package/layout/_partial/scripts/lazyload.ejs +24 -0
- package/layout/_partial/scripts/utils.ejs +58 -15
- package/layout/_partial/sidebar/grad-def.ejs +26 -0
- package/layout/_partial/sidebar/index_leftbar.ejs +25 -10
- package/layout/_partial/sidebar/logo.ejs +22 -1
- package/layout/_partial/sidebar/menu.ejs +11 -5
- package/layout/_partial/widgets/components/linklist.ejs +2 -2
- package/layout/_partial/widgets/toc.ejs +12 -8
- package/layout/_plugins/scrollreveal.ejs +17 -2
- package/layout/index.ejs +36 -1
- package/layout/layout.ejs +1 -1
- package/layout/notebooks.ejs +3 -1
- package/layout/notes.ejs +3 -1
- package/package.json +5 -2
- package/scripts/tags/index.js +1 -0
- package/scripts/tags/lib/mbti.js +169 -0
- package/source/css/_common/canonical.styl +9 -10
- package/source/css/_common/device.styl +12 -12
- package/source/css/_common/highlight.styl +0 -2
- package/source/css/_common/html.styl +2 -1
- package/source/css/_common/svg.styl +9 -1
- package/source/css/_components/layout.styl +11 -8
- package/source/css/_components/list.styl +55 -21
- package/source/css/_components/main.styl +2 -2
- package/source/css/_components/md.styl +0 -1
- package/source/css/_components/pages/article-story.styl +1 -3
- package/source/css/_components/partial/article-banner.styl +17 -11
- package/source/css/_components/partial/article-footer.styl +0 -1
- package/source/css/_components/partial/navbar.styl +10 -27
- package/source/css/_components/partial/paginator.styl +9 -11
- package/source/css/_components/pin-slider.styl +240 -0
- package/source/css/_components/sidebar/footer.styl +1 -1
- package/source/css/_components/sidebar/logo.styl +27 -1
- package/source/css/_components/sidebar/menu.styl +53 -30
- package/source/css/_components/sidebar/nav-area.styl +1 -1
- package/source/css/_components/sidebar/search.styl +50 -11
- package/source/css/_components/sidebar/sidebar.styl +32 -5
- package/source/css/_components/tag-plugins/banner.styl +2 -1
- package/source/css/_components/tag-plugins/chat.styl +3 -1
- package/source/css/_components/tag-plugins/folding.styl +1 -2
- package/source/css/_components/tag-plugins/friends.styl +1 -0
- package/source/css/_components/tag-plugins/friends_posts.styl +2 -1
- package/source/css/_components/tag-plugins/gallery.styl +1 -2
- package/source/css/_components/tag-plugins/grid.styl +1 -2
- package/source/css/_components/tag-plugins/image.styl +0 -2
- package/source/css/_components/tag-plugins/link.styl +0 -1
- package/source/css/_components/tag-plugins/mbti.styl +80 -0
- package/source/css/_components/tag-plugins/note.styl +1 -2
- package/source/css/_components/tag-plugins/okr.styl +1 -2
- package/source/css/_components/tag-plugins/sites.styl +0 -1
- package/source/css/_components/tag-plugins/timeline.styl +8 -6
- package/source/css/_components/widgets/components.styl +19 -7
- package/source/css/_components/widgets/ghuser.styl +3 -2
- package/source/css/_components/widgets/list.styl +20 -8
- package/source/css/_components/widgets/markdown.styl +5 -2
- package/source/css/_components/widgets/related.styl +6 -4
- package/source/css/_components/widgets/timeline.styl +1 -1
- package/source/css/_components/widgets/toc.styl +9 -6
- package/source/css/_components/widgets/widgets.styl +11 -10
- package/source/css/_custom.styl +5 -2
- package/source/css/_defines/const.styl +2 -2
- package/source/css/_defines/func.styl +49 -3
- package/source/css/_defines/theme_base.styl +22 -1
- package/source/css/_plugins/comments/artalk.styl +2 -2
- package/source/css/_plugins/comments/twikoo.styl +0 -2
- package/source/css/_plugins/tianli_gpt.styl +3 -0
- package/source/js/main.js +39 -17
- package/source/js/services/artalk_latest_comment.js +43 -1
- package/source/js/services/memos.js +12 -3
|
@@ -78,16 +78,11 @@ function layoutTitle(main, url, sub) {
|
|
|
78
78
|
function layoutWidgets() {
|
|
79
79
|
var el = '';
|
|
80
80
|
el += '<div class="widgets">';
|
|
81
|
+
// 左栏共用的 SVG 渐变定义(供激活书签等彩色图标使用,见 grad-def.ejs)
|
|
82
|
+
el += partial('grad-def', {id: 'leftbar-active-icon-grad'})
|
|
83
|
+
// 导航区放进组件滚动区,一起滚动
|
|
84
|
+
el += layoutNavArea()
|
|
81
85
|
if (page.leftbar) {
|
|
82
|
-
var search = {}
|
|
83
|
-
if (page.search) {
|
|
84
|
-
search = page.search
|
|
85
|
-
} else if (wiki?.search) {
|
|
86
|
-
search = wiki.search
|
|
87
|
-
} else if (topic?.search) {
|
|
88
|
-
search = topic.search
|
|
89
|
-
}
|
|
90
|
-
el += partial('search', {item: search})
|
|
91
86
|
page.leftbar.forEach((w, i) => {
|
|
92
87
|
let name = ''
|
|
93
88
|
let widget = {}
|
|
@@ -110,6 +105,20 @@ function layoutWidgets() {
|
|
|
110
105
|
el += '</div>';
|
|
111
106
|
return el;
|
|
112
107
|
}
|
|
108
|
+
function layoutSearch() {
|
|
109
|
+
if (!page.leftbar) {
|
|
110
|
+
return ''
|
|
111
|
+
}
|
|
112
|
+
var search = {}
|
|
113
|
+
if (page.search) {
|
|
114
|
+
search = page.search
|
|
115
|
+
} else if (wiki?.search) {
|
|
116
|
+
search = wiki.search
|
|
117
|
+
} else if (topic?.search) {
|
|
118
|
+
search = topic.search
|
|
119
|
+
}
|
|
120
|
+
return partial('search', {item: search})
|
|
121
|
+
}
|
|
113
122
|
function layoutFooterDiv() {
|
|
114
123
|
const { social } = theme.footer
|
|
115
124
|
if (social == null) {
|
|
@@ -154,6 +163,12 @@ function layoutNavArea() {
|
|
|
154
163
|
if (page.menu == false) {
|
|
155
164
|
return el
|
|
156
165
|
}
|
|
166
|
+
// wiki 内容页默认隐藏导航区,page.menu 或 wiki.menu 为 true 时显示
|
|
167
|
+
if (page.wiki?.length > 0) {
|
|
168
|
+
if (page.menu !== true && wiki?.menu !== true) {
|
|
169
|
+
return el
|
|
170
|
+
}
|
|
171
|
+
}
|
|
157
172
|
el += `<div class="nav-area">`
|
|
158
173
|
el += partial('menu', {where: 'sidebar'})
|
|
159
174
|
el += `</div>`
|
|
@@ -161,6 +176,6 @@ function layoutNavArea() {
|
|
|
161
176
|
}
|
|
162
177
|
%>
|
|
163
178
|
<%- layoutLogo() %>
|
|
164
|
-
<%-
|
|
179
|
+
<%- layoutSearch() %>
|
|
165
180
|
<%- layoutWidgets() %>
|
|
166
181
|
<%- layoutFooterDiv() %>
|
|
@@ -37,6 +37,26 @@ function layoutTitle(main, url, sub) {
|
|
|
37
37
|
return el
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
function layoutBack() {
|
|
41
|
+
if (where != 'sidebar' || !page.wiki) {
|
|
42
|
+
return ''
|
|
43
|
+
}
|
|
44
|
+
// 开关:page 的 wiki_home 优先,其次 wiki 定义的 wiki_home,默认显示
|
|
45
|
+
const wiki = theme.wiki.tree[page.wiki]
|
|
46
|
+
const enabled = page.wiki_home ?? wiki?.wiki_home ?? true
|
|
47
|
+
if (enabled === false) {
|
|
48
|
+
return ''
|
|
49
|
+
}
|
|
50
|
+
var el = ''
|
|
51
|
+
el += `<div class="wiki-home-wrap">`
|
|
52
|
+
el += `<a class="wiki-home cap" href="${pretty_url(theme.site_tree.index_wiki.base_dir)}">`
|
|
53
|
+
el += icon('default:goback')
|
|
54
|
+
el += `<span>${__('btn.all_wiki')}</span>`
|
|
55
|
+
el += `</a>`
|
|
56
|
+
el += `</div>`
|
|
57
|
+
return el
|
|
58
|
+
}
|
|
59
|
+
|
|
40
60
|
function layoutDiv() {
|
|
41
61
|
if (where == 'main') {
|
|
42
62
|
if (page.header == false) {
|
|
@@ -59,7 +79,7 @@ function layoutDiv() {
|
|
|
59
79
|
} else if (md_text(logo.avatar)) {
|
|
60
80
|
el += `<a class="avatar" href="${url_for(md_link(logo.avatar) || '/')}">`
|
|
61
81
|
if (theme.style.animated_avatar.animate) {
|
|
62
|
-
el += `<div class="bg"
|
|
82
|
+
el += `<div class="bg"></div>`
|
|
63
83
|
}
|
|
64
84
|
el += `<img no-lazy class="avatar" src="${md_text(logo.avatar)}">`
|
|
65
85
|
el += `</a>`
|
|
@@ -75,4 +95,5 @@ function layoutDiv() {
|
|
|
75
95
|
}
|
|
76
96
|
%>
|
|
77
97
|
|
|
98
|
+
<%- layoutBack() %>
|
|
78
99
|
<%- layoutDiv() %>
|
|
@@ -6,16 +6,22 @@ function layoutDiv() {
|
|
|
6
6
|
if (item == undefined || item.id == null || item.url == null) {
|
|
7
7
|
continue
|
|
8
8
|
}
|
|
9
|
-
|
|
9
|
+
var active = item.id == page.menu_id
|
|
10
|
+
el += `<a class="nav-item${active ? ' active' : ''}${item.theme?.length > 0 ? ' themed' : ''}"`
|
|
11
|
+
var style = `--item-grad:url(#menu-grad-${item.id})`
|
|
10
12
|
if (item.theme?.length > 0) {
|
|
11
|
-
|
|
13
|
+
style += `;--item-theme:${item.theme}`
|
|
12
14
|
}
|
|
13
|
-
el +=
|
|
15
|
+
el += ` style="${style}"`
|
|
16
|
+
el += ` title="${item.title}" href="${pretty_url(item.url)}">`
|
|
14
17
|
if (item.icon?.length > 0) {
|
|
15
18
|
el += icon(item.icon, 'no-lazy')
|
|
16
|
-
} else {
|
|
17
|
-
el += `<span>${__(item.title)}</span>`
|
|
18
19
|
}
|
|
20
|
+
el += `<span>${__(item.title)}</span>`
|
|
21
|
+
if (active) {
|
|
22
|
+
el += `<span class="active-dot"></span>`
|
|
23
|
+
}
|
|
24
|
+
el += partial('grad-def', {id: 'menu-grad-' + item.id})
|
|
19
25
|
el += `</a>`
|
|
20
26
|
}
|
|
21
27
|
el += `</nav>`
|
|
@@ -16,7 +16,7 @@ function layoutDiv(linklist) {
|
|
|
16
16
|
if (columns == 1 && links.length > 1) {
|
|
17
17
|
align = 'left'
|
|
18
18
|
}
|
|
19
|
-
el += `<div class="linklist ${align}" ${style}>`
|
|
19
|
+
el += `<div class="linklist ${align}${columns > 1 ? ' multi' : ''}" ${style}>`
|
|
20
20
|
for (let link of links) {
|
|
21
21
|
el += partial('link', {item: link, columns: columns})
|
|
22
22
|
}
|
|
@@ -24,4 +24,4 @@ function layoutDiv(linklist) {
|
|
|
24
24
|
return el
|
|
25
25
|
}
|
|
26
26
|
%>
|
|
27
|
-
<%- layoutDiv(item) %>
|
|
27
|
+
<%- layoutDiv(item) %>
|
|
@@ -37,9 +37,15 @@ function layoutToc(fallback) {
|
|
|
37
37
|
|
|
38
38
|
function layoutDiv(fallback) {
|
|
39
39
|
const tocBody = layoutTocBody()
|
|
40
|
+
const hasToc = tocBody.trim().length > 0
|
|
41
|
+
// 页面是否实际渲染评论区,由 _partial/comments/layout.ejs 标记
|
|
42
|
+
const hasComments = page.cmt_rendered === true
|
|
43
|
+
if (!hasToc && !hasComments) {
|
|
44
|
+
return ''
|
|
45
|
+
}
|
|
40
46
|
var el = ''
|
|
41
47
|
el += `<widget class="widget-wrapper${scrollreveal(' ')} toc" id="data-toc" collapse="${item.collapse}">`
|
|
42
|
-
if (
|
|
48
|
+
if (hasToc) {
|
|
43
49
|
el += layoutTocHeader()
|
|
44
50
|
el += `<div class="widget-body">`
|
|
45
51
|
el += tocBody
|
|
@@ -50,13 +56,11 @@ function layoutDiv(fallback) {
|
|
|
50
56
|
el += icon('default:upup')
|
|
51
57
|
el += `<span>${__('btn.top')}</span>`
|
|
52
58
|
el += `</a>`
|
|
53
|
-
if (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
el += `</a>`
|
|
59
|
-
}
|
|
59
|
+
if (hasComments) {
|
|
60
|
+
el += `<a class="buttom" onclick="util.scrollComment()">`
|
|
61
|
+
el += icon('default:tocomment')
|
|
62
|
+
el += `<span>${__('btn.comments')}</span>`
|
|
63
|
+
el += `</a>`
|
|
60
64
|
}
|
|
61
65
|
el += `</div>`
|
|
62
66
|
el += `</widget>`
|
|
@@ -18,11 +18,26 @@
|
|
|
18
18
|
return null;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
+
// 最近的滚动容器(overflow-y: auto/scroll)
|
|
22
|
+
// 吸顶容器内可能存在内部滚动容器(如侧栏 .widgets),
|
|
23
|
+
// 若直接用吸顶容器做 reveal 容器,折叠区下方的元素永远不会进入视口,导致一直不显示。
|
|
24
|
+
const findScrollContainer = (el) => {
|
|
25
|
+
let node = el.parentElement;
|
|
26
|
+
while (node && node.nodeType === 1) {
|
|
27
|
+
const oy = window.getComputedStyle(node).overflowY;
|
|
28
|
+
if (oy === 'auto' || oy === 'scroll') return node;
|
|
29
|
+
node = node.parentElement;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
};
|
|
33
|
+
|
|
21
34
|
const pinnedGroups = new Map(); // 容器 -> 元素列表
|
|
22
35
|
const targets = [];
|
|
23
36
|
els.forEach((el) => {
|
|
24
|
-
const
|
|
25
|
-
if (
|
|
37
|
+
const pinned = findPinnedContainer(el);
|
|
38
|
+
if (pinned) {
|
|
39
|
+
// 优先使用最近的滚动容器,其次才用吸顶容器
|
|
40
|
+
const container = findScrollContainer(el) || pinned;
|
|
26
41
|
const group = pinnedGroups.get(container) || [];
|
|
27
42
|
group.push(el);
|
|
28
43
|
pinnedGroups.set(container, group);
|
package/layout/index.ejs
CHANGED
|
@@ -5,6 +5,32 @@ if (page.menu_id == undefined) {
|
|
|
5
5
|
if (page.title && page.wiki) {
|
|
6
6
|
page.robots = 'noindex,follow';
|
|
7
7
|
}
|
|
8
|
+
// 首页评论区:仅首页第一页,默认不显示,可通过 site_tree.home.comments 开启/覆盖
|
|
9
|
+
var homeCmt = theme.site_tree?.home?.comments
|
|
10
|
+
if (homeCmt && homeCmt !== false && is_home_first_page()) {
|
|
11
|
+
page.comments = true;
|
|
12
|
+
if (typeof homeCmt === 'object') {
|
|
13
|
+
if (homeCmt.service) {
|
|
14
|
+
page.comments_service = homeCmt.service;
|
|
15
|
+
}
|
|
16
|
+
if (homeCmt.comment_title != null) {
|
|
17
|
+
page.comment_title = homeCmt.comment_title;
|
|
18
|
+
}
|
|
19
|
+
if (homeCmt.comment_id != null) {
|
|
20
|
+
page.comment_id = homeCmt.comment_id;
|
|
21
|
+
}
|
|
22
|
+
var homeCommentServices = ['beaudar', 'utterances', 'giscus', 'twikoo', 'waline', 'artalk'];
|
|
23
|
+
for (var i = 0; i < homeCommentServices.length; i++) {
|
|
24
|
+
var key = homeCommentServices[i];
|
|
25
|
+
if (homeCmt[key]) {
|
|
26
|
+
page[key] = homeCmt[key];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 首页第一页渲染置顶轮播时,列表不再重复展示置顶文章
|
|
33
|
+
var pinSliderActive = is_home_first_page()
|
|
8
34
|
|
|
9
35
|
function layout_post_card(layout, post, content) {
|
|
10
36
|
var el = '';
|
|
@@ -25,6 +51,12 @@ function layout_post_list(partial) {
|
|
|
25
51
|
el += `<div class="post-list post">`;
|
|
26
52
|
page.posts.each(function(post){
|
|
27
53
|
if (post.indexing != false) {
|
|
54
|
+
if (pinSliderActive) {
|
|
55
|
+
var pinVal = post.pin != null ? post.pin : post.sticky
|
|
56
|
+
if (pinVal !== false && pinVal != null) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
}
|
|
28
60
|
el += layout_post_card('post', post, partial(post));
|
|
29
61
|
}
|
|
30
62
|
})
|
|
@@ -38,4 +70,7 @@ function layout_post_list(partial) {
|
|
|
38
70
|
<%- layout_post_list(function(post){
|
|
39
71
|
return partial('_partial/main/post_list/post_card', {post: post})
|
|
40
72
|
}) %>
|
|
41
|
-
<%- partial('_partial/main/post_list/paginator') %>
|
|
73
|
+
<%- partial('_partial/main/post_list/paginator') %>
|
|
74
|
+
<% if (homeCmt && homeCmt !== false && is_home_first_page()) { %>
|
|
75
|
+
<%- partial('_partial/comments/layout') %>
|
|
76
|
+
<% } %>
|
package/layout/layout.ejs
CHANGED
|
@@ -49,7 +49,7 @@ if (theme.style.prefers_theme === 'auto') {
|
|
|
49
49
|
html += site_background
|
|
50
50
|
html += `<div id="l_cover">${partial('_partial/cover/index').trim()}</div>`
|
|
51
51
|
html += `<div class="l_body ${page_type}" id="start" layout="${page.layout}" type="${article_type}" ${indent ? 'text-indent' : ''}>`
|
|
52
|
-
html += `<aside class="l_left">`
|
|
52
|
+
html += `<aside class="l_left${theme.style.leftbar?.['ui-style'] === 'card' ? ' leftbar-card' : ''}">`
|
|
53
53
|
html += `<div class="sidebg"></div>`
|
|
54
54
|
html += `<div class="leftbar-container${theme.style.leftbar?.blur ? ' leftbar-blur' : ''}">`
|
|
55
55
|
html += partial('_partial/sidebar/index_leftbar')
|
package/layout/notebooks.ejs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
<%# 笔记本列表页主体部分 %>
|
|
2
2
|
<% for (const notebook of Object.values(theme.notebooks.tree)) { %>
|
|
3
3
|
<div class="post-list wiki">
|
|
4
|
-
<
|
|
4
|
+
<div class="post-card-wrap<%= scrollreveal(' ') %>">
|
|
5
|
+
<a class="post-card wiki" href="<%= pretty_url(notebook.base_dir) %>">
|
|
5
6
|
<%- include('_partial/main/notebook/notebook_card', { notebook: notebook }) %>
|
|
6
7
|
</a>
|
|
8
|
+
</div>
|
|
7
9
|
</div>
|
|
8
10
|
<% } %>
|
package/layout/notes.ejs
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
<%# 笔记列表页主体部分 %>
|
|
2
2
|
<div class="post-list post">
|
|
3
3
|
<% page.posts.each(post => { %>
|
|
4
|
+
<div class="post-card-wrap<%= scrollreveal(' ') %>">
|
|
4
5
|
<a
|
|
5
|
-
class="post-card
|
|
6
|
+
class="post-card post"
|
|
6
7
|
href="<%= pretty_url(post.link || post.path) %><%= page.activeTag ? `?tag=${page.activeTag}` : '' %>"
|
|
7
8
|
>
|
|
8
9
|
<%- include('_partial/main/notebook/note_card', { note: post }) %>
|
|
9
10
|
</a>
|
|
11
|
+
</div>
|
|
10
12
|
<% }) %>
|
|
11
13
|
</div>
|
|
12
14
|
<%- include('_partial/main/notebook/paginator') %>
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hexo-theme-stellar",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.39.0",
|
|
4
4
|
"description": "Elegant and powerful theme for Hexo.",
|
|
5
5
|
"main": "package.json",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"
|
|
7
|
+
"lint": "eslint .",
|
|
8
|
+
"test": "node --test",
|
|
9
|
+
"check": "npm run lint && npm test && python3 docs/knowledge/tools/verify.py",
|
|
8
10
|
"release": "node release.js",
|
|
9
11
|
"release:dry": "node release.js --dry-run"
|
|
10
12
|
},
|
|
@@ -31,5 +33,6 @@
|
|
|
31
33
|
},
|
|
32
34
|
"homepage": "https://xaoxuu.com/wiki/stellar/",
|
|
33
35
|
"devDependencies": {
|
|
36
|
+
"eslint": "^9.39.5"
|
|
34
37
|
}
|
|
35
38
|
}
|
package/scripts/tags/index.js
CHANGED
|
@@ -42,6 +42,7 @@ hexo.extend.tag.register('poetry', require('./lib/poetry')(hexo), true)
|
|
|
42
42
|
hexo.extend.tag.register('quot', require('./lib/quot')(hexo))
|
|
43
43
|
hexo.extend.tag.register('blockquote', require('./lib/blockquote')(hexo), true)
|
|
44
44
|
hexo.extend.tag.register('hashtag', require('./lib/hashtag')(hexo))
|
|
45
|
+
hexo.extend.tag.register('mbti', require('./lib/mbti')(hexo))
|
|
45
46
|
hexo.extend.tag.register('okr', require('./lib/okr')(hexo), {ends: true})
|
|
46
47
|
hexo.extend.tag.register('audio', require('./lib/audio')(hexo))
|
|
47
48
|
hexo.extend.tag.register('video', require('./lib/video')(hexo))
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mbti.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
|
|
3
|
+
*
|
|
4
|
+
* {% mbti INTJ-T 93-72-78-83-51 [url:https://...] [image:src] %}
|
|
5
|
+
*
|
|
6
|
+
* - type: 人格类型代码(如 INTJ-T / INTJ,大小写均可),决定标题与配色
|
|
7
|
+
* - scores: 五个百分比,用 - 分隔,顺序固定为 energy-mind-nature-tactics-identity
|
|
8
|
+
* - url: 档案链接(可选,url:https://...),支持完整 URL 或纯档案 ID
|
|
9
|
+
* - image: 标题上方横幅图片地址(可选)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
var TYPE_ROLES = {
|
|
15
|
+
INTJ: 'analyst',
|
|
16
|
+
INTP: 'analyst',
|
|
17
|
+
ENTJ: 'analyst',
|
|
18
|
+
ENTP: 'analyst',
|
|
19
|
+
INFJ: 'diplomat',
|
|
20
|
+
INFP: 'diplomat',
|
|
21
|
+
ENFJ: 'diplomat',
|
|
22
|
+
ENFP: 'diplomat',
|
|
23
|
+
ISTJ: 'sentinel',
|
|
24
|
+
ISFJ: 'sentinel',
|
|
25
|
+
ESTJ: 'sentinel',
|
|
26
|
+
ESFJ: 'sentinel',
|
|
27
|
+
ISTP: 'explorer',
|
|
28
|
+
ISFP: 'explorer',
|
|
29
|
+
ESTP: 'explorer',
|
|
30
|
+
ESFP: 'explorer'
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
var ROLE_COLORS = {
|
|
34
|
+
analyst: 'purple',
|
|
35
|
+
diplomat: 'green',
|
|
36
|
+
sentinel: 'blue',
|
|
37
|
+
explorer: 'yellow'
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
var TRAIT_KEYS = ['energy', 'mind', 'nature', 'tactics', 'identity'];
|
|
41
|
+
|
|
42
|
+
var TRAIT_COLORS = {
|
|
43
|
+
energy: '#2196f3',
|
|
44
|
+
mind: '#FFBD2B',
|
|
45
|
+
nature: '#3DC550',
|
|
46
|
+
tactics: '#9c27b0',
|
|
47
|
+
identity: '#F44336'
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// 根据类型代码推导每个维度的偏向侧(trait 侧):E/I、N/S、T/F、J/P、A/T
|
|
51
|
+
function getTraitSides(typeCode) {
|
|
52
|
+
var code = typeCode.replace('-', '');
|
|
53
|
+
var sides = {};
|
|
54
|
+
sides.energy = code.charAt(0) === 'E' ? 'left' : 'right';
|
|
55
|
+
sides.mind = code.charAt(1) === 'N' ? 'left' : 'right';
|
|
56
|
+
sides.nature = code.charAt(2) === 'T' ? 'left' : 'right';
|
|
57
|
+
sides.tactics = code.charAt(3) === 'J' ? 'left' : 'right';
|
|
58
|
+
if (code.charAt(4) === 'A') {
|
|
59
|
+
sides.identity = 'left';
|
|
60
|
+
} else if (code.charAt(4) === 'T') {
|
|
61
|
+
sides.identity = 'right';
|
|
62
|
+
} else {
|
|
63
|
+
sides.identity = 'left';
|
|
64
|
+
console.warn('[mbti] identity variant missing, default to Assertive (-A): ' + typeCode);
|
|
65
|
+
}
|
|
66
|
+
return sides;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function clamp(value) {
|
|
70
|
+
value = parseInt(value, 10);
|
|
71
|
+
if (isNaN(value)) {
|
|
72
|
+
value = 0;
|
|
73
|
+
}
|
|
74
|
+
return Math.max(0, Math.min(100, value));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function alphaHex(hex, alpha) {
|
|
78
|
+
var r = parseInt(hex.slice(1, 3), 16);
|
|
79
|
+
var g = parseInt(hex.slice(3, 5), 16);
|
|
80
|
+
var b = parseInt(hex.slice(5, 7), 16);
|
|
81
|
+
return 'rgba(' + r + ',' + g + ',' + b + ',' + alpha + ')';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = ctx => function(args) {
|
|
85
|
+
args = ctx.args.map(args, ['image', 'url'], ['type', 'scores']);
|
|
86
|
+
|
|
87
|
+
var typeCode = String(args.type || '').trim().toUpperCase();
|
|
88
|
+
if (!typeCode) {
|
|
89
|
+
console.error('[mbti] missing personality type');
|
|
90
|
+
return '';
|
|
91
|
+
}
|
|
92
|
+
var typeMatch = typeCode.match(/^([A-Z]{4})/);
|
|
93
|
+
var typeKey = typeMatch ? typeMatch[1] : null;
|
|
94
|
+
var role = typeKey ? TYPE_ROLES[typeKey] : null;
|
|
95
|
+
if (!role) {
|
|
96
|
+
console.warn('[mbti] unknown personality type: ' + typeCode);
|
|
97
|
+
}
|
|
98
|
+
var themeColor = role ? ROLE_COLORS[role] : '';
|
|
99
|
+
|
|
100
|
+
var __ = (this && typeof this.__ === 'function') ? this.__ : ctx.theme.i18n.__(ctx.config.language);
|
|
101
|
+
var typeName = typeKey ? __('tag_plugins.mbti.types.' + typeKey) : '';
|
|
102
|
+
if (!typeName || typeName.indexOf('tag_plugins.mbti.types.') === 0) {
|
|
103
|
+
typeName = typeCode;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
var scoreParts = String(args.scores || '').split('-');
|
|
107
|
+
var pcts = [];
|
|
108
|
+
for (var i = 0; i < TRAIT_KEYS.length; i++) {
|
|
109
|
+
pcts.push(clamp(scoreParts[i]));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
var url = args.url ? String(args.url).trim() : '';
|
|
113
|
+
if (url && !/^https?:\/\//i.test(url)) {
|
|
114
|
+
url = 'https://www.16personalities.com/ch/' + encodeURIComponent('档案') + '/' + url;
|
|
115
|
+
}
|
|
116
|
+
if (url) {
|
|
117
|
+
url = url.replace(/"/g, '"');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
var image = args.image ? String(args.image).trim() : '';
|
|
121
|
+
if (image) {
|
|
122
|
+
image = image.replace(/"/g, '"');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
var traitSides = getTraitSides(typeCode);
|
|
126
|
+
|
|
127
|
+
var el = '<div class="tag-plugin colorful note mbti"';
|
|
128
|
+
if (themeColor) {
|
|
129
|
+
el += ' color="' + themeColor + '"';
|
|
130
|
+
}
|
|
131
|
+
el += '>';
|
|
132
|
+
if (image) {
|
|
133
|
+
el += '<img class="mbti-image" src="' + image + '" alt="" />';
|
|
134
|
+
}
|
|
135
|
+
var title = __('tag_plugins.mbti.title', typeCode, typeName);
|
|
136
|
+
el += '<div class="mbti-title">';
|
|
137
|
+
if (url) {
|
|
138
|
+
el += '<a class="mbti-title-link" href="' + url + '" target="_blank" rel="external nofollow noopener noreferrer">' + title + '</a>';
|
|
139
|
+
} else {
|
|
140
|
+
el += '<span class="mbti-title-text">' + title + '</span>';
|
|
141
|
+
}
|
|
142
|
+
el += '</div>';
|
|
143
|
+
el += '<div class="mbti-body">';
|
|
144
|
+
el += '<div class="mbti-traits">';
|
|
145
|
+
for (var j = 0; j < TRAIT_KEYS.length; j++) {
|
|
146
|
+
var key = TRAIT_KEYS[j];
|
|
147
|
+
var left = __('tag_plugins.mbti.traits.' + key + '.left');
|
|
148
|
+
var right = __('tag_plugins.mbti.traits.' + key + '.right');
|
|
149
|
+
var pct = pcts[j];
|
|
150
|
+
// 官网规则:trait 在左端时 reverse=true,圆点位置 = reverse ? 100-pct : pct,高亮侧由圆点位置决定
|
|
151
|
+
var reverse = traitSides[key] === 'left';
|
|
152
|
+
var position = reverse ? 100 - pct : pct;
|
|
153
|
+
var activeSide = position <= 50 ? 'left' : 'right';
|
|
154
|
+
// 填充从主导侧(trait 侧)开始:trait 在右时 fill 右对齐
|
|
155
|
+
var reversed = traitSides[key] === 'right' ? ' reversed' : '';
|
|
156
|
+
el += '<div class="mbti-trait' + reversed + '" style="--theme:' + TRAIT_COLORS[key] + '; --theme-a30:' + alphaHex(TRAIT_COLORS[key], 0.3) + '">';
|
|
157
|
+
el += '<div class="mbti-labels">';
|
|
158
|
+
el += '<span class="mbti-label left' + (activeSide === 'left' ? ' active' : '') + '">' + left + (activeSide === 'left' ? ' <span class="pct">' + pct + '%</span>' : '') + '</span>';
|
|
159
|
+
el += '<span class="mbti-label right' + (activeSide === 'right' ? ' active' : '') + '">' + (activeSide === 'right' ? '<span class="pct">' + pct + '%</span> ' : '') + right + '</span>';
|
|
160
|
+
el += '</div>';
|
|
161
|
+
// fill 从主导侧端点延伸到圆点位置(宽度与圆点互补),保证填充末端与圆点对齐
|
|
162
|
+
el += '<div class="mbti-progress"><div class="fill" style="width:' + (100 - pct) + '%"></div><span class="mbti-dot" style="left:' + position + '%"></span></div>';
|
|
163
|
+
el += '</div>';
|
|
164
|
+
}
|
|
165
|
+
el += '</div>';
|
|
166
|
+
el += '</div>';
|
|
167
|
+
el += '</div>';
|
|
168
|
+
return el;
|
|
169
|
+
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
position: fixed
|
|
3
3
|
display: flex
|
|
4
4
|
text-align: center
|
|
5
|
+
border-radius: 8px
|
|
5
6
|
bottom: 4rem
|
|
6
7
|
left: 50%
|
|
7
8
|
transform: translateX(-50%)
|
|
@@ -12,17 +13,15 @@
|
|
|
12
13
|
box-shadow: 0 2px 8px rgba(black, .1), 0 4px 16px rgba(black, .1), 0 8px 32px rgba(black, .1)
|
|
13
14
|
.headline
|
|
14
15
|
font-size: $fsh2
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
background: var(--bg-a100)
|
|
16
|
+
margin-bottom: .5rem
|
|
17
|
+
a
|
|
18
18
|
display: block
|
|
19
19
|
color: inherit
|
|
20
|
-
padding:
|
|
21
|
-
font-size:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
margin-left: 1px
|
|
20
|
+
padding: .75rem
|
|
21
|
+
font-size: $fs-14
|
|
22
|
+
|
|
23
|
+
.canonical-tip.official
|
|
24
|
+
background-color: var(--bg-a100)
|
|
26
25
|
|
|
27
26
|
.canonical-tip.unofficial
|
|
28
27
|
background-color: hsl(5, 100%, 60%)
|
|
@@ -34,4 +33,4 @@
|
|
|
34
33
|
50%
|
|
35
34
|
background-color: hsl(5, 100%, 30%)
|
|
36
35
|
100%
|
|
37
|
-
background-color: hsl(5, 100%, 60%)
|
|
36
|
+
background-color: hsl(5, 100%, 60%)
|
|
@@ -20,28 +20,31 @@
|
|
|
20
20
|
display: flex
|
|
21
21
|
justify-content: center
|
|
22
22
|
flex-direction: column
|
|
23
|
-
|
|
23
|
+
gap: $bar-item-gap
|
|
24
|
+
padding: $bar-item-gap
|
|
24
25
|
@media screen and (min-width: $device-mobile-max)
|
|
25
26
|
margin-right: 3rem
|
|
26
27
|
overflow: hidden
|
|
27
28
|
trans1: all
|
|
28
|
-
&:before,&:after
|
|
29
|
-
border-radius: 64px
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
bar-glass()
|
|
32
31
|
|
|
33
32
|
|
|
34
33
|
// 侧边栏弹出按钮
|
|
35
34
|
.float-panel button
|
|
35
|
+
bar-item()
|
|
36
36
|
cursor: pointer
|
|
37
37
|
color: var(--text)
|
|
38
38
|
background: none
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
box-sizing: border-box
|
|
40
|
+
width: 36px
|
|
41
|
+
height: 36px
|
|
42
|
+
padding: 4px
|
|
41
43
|
line-height: 0
|
|
42
44
|
font-size: 28px
|
|
43
|
-
margin: 0
|
|
44
45
|
display: flex
|
|
46
|
+
justify-content: center
|
|
47
|
+
align-items: center
|
|
45
48
|
>*
|
|
46
49
|
path#sep
|
|
47
50
|
trans1 transform
|
|
@@ -49,10 +52,8 @@
|
|
|
49
52
|
height: 28px
|
|
50
53
|
|
|
51
54
|
// 侧边栏弹出后
|
|
52
|
-
.l_body[leftbar] .float-panel, .l_body[rightbar] .float-panel
|
|
53
|
-
|
|
54
|
-
&:before
|
|
55
|
-
background: var(--bg-a50)
|
|
55
|
+
.l_body[leftbar] .float-panel button.leftbar-toggle, .l_body[rightbar] .float-panel button.rightbar-toggle
|
|
56
|
+
bar-item-active()
|
|
56
57
|
|
|
57
58
|
.l_body[leftbar] .float-panel button.leftbar-toggle
|
|
58
59
|
color: var(--theme)
|
|
@@ -74,4 +75,3 @@
|
|
|
74
75
|
fill-opacity: 0.3
|
|
75
76
|
path#sep
|
|
76
77
|
transform: translateX(2px)
|
|
77
|
-
|
|
@@ -14,7 +14,6 @@ p>code:not([class]),li>code:not([class])
|
|
|
14
14
|
--theme: var(--text-p3)
|
|
15
15
|
margin: var(--gap-p) 0
|
|
16
16
|
border-radius: $border-card
|
|
17
|
-
corner-shape: superellipse(1.2)
|
|
18
17
|
overflow: hidden
|
|
19
18
|
background: var(--block)
|
|
20
19
|
font-family: $ff-codeblock
|
|
@@ -80,7 +79,6 @@ p>code:not([class]),li>code:not([class])
|
|
|
80
79
|
.gist-file
|
|
81
80
|
border: 1px solid var(--block-border)
|
|
82
81
|
border-radius: $border-card
|
|
83
|
-
corner-shape: superellipse(1.2)
|
|
84
82
|
overflow: hidden
|
|
85
83
|
.gist-data
|
|
86
84
|
--bgColor-default: var(--block)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
*
|
|
2
2
|
outline: none
|
|
3
|
+
corner-shape: $corner-shape
|
|
3
4
|
html
|
|
4
5
|
height: 100%
|
|
5
6
|
width: 100%
|
|
@@ -9,7 +10,7 @@ html
|
|
|
9
10
|
font-size: $fs-root
|
|
10
11
|
-webkit-text-size-adjust: 100%
|
|
11
12
|
-ms-text-size-adjust: 100%
|
|
12
|
-
scroll-padding-top:
|
|
13
|
+
scroll-padding-top: 0
|
|
13
14
|
body
|
|
14
15
|
background: var(--background)
|
|
15
16
|
margin: 0
|
|
@@ -6,4 +6,12 @@ svg.icon
|
|
|
6
6
|
overflow: hidden
|
|
7
7
|
|
|
8
8
|
svg.active-icon
|
|
9
|
-
color: var(--theme)
|
|
9
|
+
color: var(--theme)
|
|
10
|
+
|
|
11
|
+
// 隐藏的 SVG 渐变定义容器(配合 --item-grad 与 fill:var(--item-grad) 使用)
|
|
12
|
+
svg.grad-def
|
|
13
|
+
position: absolute
|
|
14
|
+
width: 0 !important
|
|
15
|
+
height: 0 !important
|
|
16
|
+
overflow: hidden
|
|
17
|
+
pointer-events: none
|