hexo-theme-stellar 1.28.1 → 1.29.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/_config.yml +55 -6
- package/_data/icons.yml +3 -1
- package/_data/widgets.yml +6 -0
- package/languages/en.yml +7 -0
- package/languages/zh-CN.yml +7 -0
- package/languages/zh-TW.yml +7 -0
- package/layout/_partial/comments/artalk/script.ejs +9 -13
- package/layout/_partial/comments/beaudar/script.ejs +20 -23
- package/layout/_partial/comments/giscus/script.ejs +11 -11
- package/layout/_partial/comments/twikoo/script.ejs +12 -17
- package/layout/_partial/comments/utterances/script.ejs +20 -23
- package/layout/_partial/comments/waline/script.ejs +9 -16
- package/layout/_partial/head.ejs +1 -1
- package/layout/_partial/main/article/article_footer.ejs +6 -2
- package/layout/_partial/main/article/related_posts.ejs +1 -1
- package/layout/_partial/main/navbar/article_banner.ejs +2 -0
- package/layout/_partial/main/navbar/breadcrumb/note.ejs +8 -0
- package/layout/_partial/main/navbar/dateinfo.ejs +11 -0
- package/layout/_partial/main/notebook/note_card.ejs +36 -0
- package/layout/_partial/main/notebook/note_tags.ejs +9 -0
- package/layout/_partial/main/notebook/notebook_card.ejs +10 -0
- package/layout/_partial/main/notebook/paginator.ejs +9 -0
- package/layout/_partial/scripts/tagtree.ejs +29 -0
- package/layout/_partial/scripts/theme.ejs +62 -0
- package/layout/_partial/scripts.ejs +4 -1
- package/layout/_partial/sidebar/index_leftbar.ejs +10 -0
- package/layout/_partial/sidebar/index_rightbar.ejs +10 -0
- package/layout/_partial/sidebar/search.ejs +4 -0
- package/layout/_partial/widgets/recent.ejs +10 -0
- package/layout/_partial/widgets/tagtree.ejs +59 -0
- package/layout/_partial/widgets/toc.ejs +7 -5
- package/layout/_plugins/tianli_gpt.ejs +1 -1
- package/layout/layout.ejs +5 -1
- package/layout/notebooks.ejs +8 -0
- package/layout/notes.ejs +12 -0
- package/layout/page.ejs +11 -1
- package/package.json +1 -1
- package/scripts/events/index.js +1 -0
- package/scripts/events/lib/notebooks.js +153 -0
- package/scripts/generators/notebooks.js +71 -0
- package/scripts/tags/index.js +5 -0
- package/scripts/tags/lib/quot.js +1 -2
- package/scripts/tags/lib/read/paper.js +90 -0
- package/scripts/tags/lib/read/reel.js +43 -0
- package/scripts/tags/lib/video.js +11 -6
- package/source/css/_common/button.styl +3 -3
- package/source/css/_common/device.styl +3 -3
- package/source/css/_components/md.styl +21 -19
- package/source/css/_components/pages/notebook.styl +65 -0
- package/source/css/_components/partial/article-banner.styl +4 -4
- package/source/css/_components/tag-plugins/common.styl +2 -2
- package/source/css/_components/tag-plugins/index.styl +1 -0
- package/source/css/_components/tag-plugins/media.styl +1 -1
- package/source/css/_components/tag-plugins/quot.styl +0 -2
- package/source/css/_components/tag-plugins/read/paper.styl +98 -0
- package/source/css/_components/tag-plugins/read/reel.styl +103 -0
- package/source/css/_defines/func.styl +4 -6
- package/source/css/_defines/theme.styl +36 -38
- package/source/css/_plugins/comments/waline.styl +3 -3
- package/source/css/_plugins/mermaid.styl +2 -2
- package/source/js/main.js +14 -0
- package/source/js/search/local-search.js +2 -2
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
(() => {
|
|
3
|
+
const tagSwitchers = document.querySelectorAll('.tag-subtree.parent-tag > a > .tag-switcher-wrapper')
|
|
4
|
+
for (const tagSwitcher of tagSwitchers) {
|
|
5
|
+
tagSwitcher.addEventListener('click', (e) => {
|
|
6
|
+
const parent = e.target.closest('.tag-subtree.parent-tag')
|
|
7
|
+
parent.classList.toggle('expanded')
|
|
8
|
+
e.preventDefault()
|
|
9
|
+
})
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Get active tag from query string, then activate it.
|
|
13
|
+
const urlParams = new URLSearchParams(window.location.search)
|
|
14
|
+
const activeTag = urlParams.get('tag')
|
|
15
|
+
if (activeTag) {
|
|
16
|
+
let tag = document.querySelector(`.tag-subtree[data-tag="${activeTag}"]`)
|
|
17
|
+
if (tag) {
|
|
18
|
+
tag.querySelector('a').classList.add('active')
|
|
19
|
+
<% if (!theme.widgets.tagtree.expand_active) { %>
|
|
20
|
+
tag = tag.parentElement.closest('.tag-subtree.parent-tag')
|
|
21
|
+
<% } %>
|
|
22
|
+
while (tag) {
|
|
23
|
+
tag.classList.add('expanded')
|
|
24
|
+
tag = tag.parentElement.closest('.tag-subtree.parent-tag')
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
})()
|
|
29
|
+
</script>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
const applyTheme = (theme) => {
|
|
3
|
+
if (theme === 'auto') {
|
|
4
|
+
document.documentElement.removeAttribute('data-theme')
|
|
5
|
+
} else {
|
|
6
|
+
document.documentElement.setAttribute('data-theme', theme)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
applyThemeToGiscus(theme)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const applyThemeToGiscus = (theme) => {
|
|
13
|
+
theme = theme === 'auto' ? 'preferred_color_scheme' : theme
|
|
14
|
+
|
|
15
|
+
const cmt = document.getElementById('giscus')
|
|
16
|
+
if (cmt) {
|
|
17
|
+
// This works before giscus load.
|
|
18
|
+
cmt.setAttribute('data-theme', theme)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const iframe = document.querySelector('#comments > section.giscus > iframe')
|
|
22
|
+
if (iframe) {
|
|
23
|
+
// This works after giscus loaded.
|
|
24
|
+
const src = iframe.src
|
|
25
|
+
const newSrc = src.replace(/theme=[\w]+/, `theme=${theme}`)
|
|
26
|
+
iframe.src = newSrc
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const switchTheme = () => {
|
|
31
|
+
// light -> dark -> auto -> light -> ...
|
|
32
|
+
const currentTheme = document.documentElement.getAttribute('data-theme')
|
|
33
|
+
let newTheme;
|
|
34
|
+
switch (currentTheme) {
|
|
35
|
+
case 'light':
|
|
36
|
+
newTheme = 'dark'
|
|
37
|
+
break
|
|
38
|
+
case 'dark':
|
|
39
|
+
newTheme = 'auto'
|
|
40
|
+
break
|
|
41
|
+
default:
|
|
42
|
+
newTheme = 'light'
|
|
43
|
+
}
|
|
44
|
+
applyTheme(newTheme)
|
|
45
|
+
window.localStorage.setItem('Stellar.theme', newTheme)
|
|
46
|
+
|
|
47
|
+
const messages = {
|
|
48
|
+
light: `<%- __('message.theme_switched.light') %>`,
|
|
49
|
+
dark: `<%- __('message.theme_switched.dark') %>`,
|
|
50
|
+
auto: `<%- __('message.theme_switched.auto') %>`,
|
|
51
|
+
}
|
|
52
|
+
hud?.toast?.(messages[newTheme])
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
(() => {
|
|
56
|
+
// Apply user's preferred theme, if any.
|
|
57
|
+
const theme = window.localStorage.getItem('Stellar.theme')
|
|
58
|
+
if (theme !== null) {
|
|
59
|
+
applyTheme(theme)
|
|
60
|
+
}
|
|
61
|
+
})()
|
|
62
|
+
</script>
|
|
@@ -18,9 +18,12 @@ function custom_inject() {
|
|
|
18
18
|
<%- partial('scripts/defines') %>
|
|
19
19
|
<%- partial('scripts/utils') %>
|
|
20
20
|
<%- partial('scripts/sidebar') %>
|
|
21
|
+
<%- partial('scripts/tagtree') %>
|
|
21
22
|
|
|
22
23
|
<!-- required -->
|
|
23
|
-
<script src="<%- `${theme.stellar.main_js}?v=${stellar_info('version')}` %>"
|
|
24
|
+
<script src="<%- `${theme.stellar.main_js}?v=${stellar_info('version')}` %>" defer></script>
|
|
25
|
+
|
|
26
|
+
<%- partial('scripts/theme') %>
|
|
24
27
|
|
|
25
28
|
<!-- optional -->
|
|
26
29
|
<%- partial('comments/script') %>
|
|
@@ -2,12 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
const wiki = theme.wiki.tree[page.wiki]
|
|
4
4
|
const topic = theme.topic.tree[page.topic]
|
|
5
|
+
const notebook = theme.notebooks.tree[page.notebook]
|
|
5
6
|
|
|
6
7
|
if (page.leftbar == null) {
|
|
7
8
|
const { site_tree } = theme
|
|
8
9
|
var sidebar
|
|
9
10
|
if (is_home()) {
|
|
10
11
|
sidebar = site_tree.home.leftbar
|
|
12
|
+
} else if (page.layout === 'notebooks') {
|
|
13
|
+
// 笔记本列表页
|
|
14
|
+
sidebar = site_tree.notebooks.leftbar
|
|
15
|
+
} else if (page.layout === 'notes') {
|
|
16
|
+
// 笔记列表页
|
|
17
|
+
sidebar = notebook ? notebook.leftbar : site_tree.notes.leftbar
|
|
18
|
+
} else if (notebook) {
|
|
19
|
+
// 笔记本文章内页
|
|
20
|
+
sidebar = page.leftbar ?? notebook.note_leftbar
|
|
11
21
|
} else if (is_category() || is_tag() || is_archive() || ['categories', 'tags', 'archives'].includes(page.layout)) {
|
|
12
22
|
sidebar = site_tree.index_blog.leftbar
|
|
13
23
|
} else if (page.layout === 'index_topic') {
|
|
@@ -2,12 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
const wiki = theme.wiki.tree[page.wiki]
|
|
4
4
|
const topic = theme.topic.tree[page.topic]
|
|
5
|
+
const notebook = theme.notebooks.tree[page.notebook]
|
|
5
6
|
|
|
6
7
|
if (page.rightbar == null) {
|
|
7
8
|
const { site_tree } = theme
|
|
8
9
|
var sidebar
|
|
9
10
|
if (is_home()) {
|
|
10
11
|
sidebar = site_tree.home.rightbar
|
|
12
|
+
} else if (page.layout === 'notebooks') {
|
|
13
|
+
// 笔记本列表页
|
|
14
|
+
sidebar = site_tree.notebooks.rightbar
|
|
15
|
+
} else if (page.layout === 'notes') {
|
|
16
|
+
// 笔记列表页
|
|
17
|
+
sidebar = notebook ? notebook.rightbar : site_tree.notes.rightbar
|
|
18
|
+
} else if (notebook) {
|
|
19
|
+
// 笔记本文章内页
|
|
20
|
+
sidebar = page.rightbar ?? notebook.note_rightbar
|
|
11
21
|
} else if (is_category() || is_tag() || is_archive() || ['categories', 'tags', 'archives'].includes(page.layout)) {
|
|
12
22
|
sidebar = site_tree.index_blog.rightbar
|
|
13
23
|
} else if (page.layout === 'index_topic') {
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
<%
|
|
2
|
+
const notebook = theme.notebooks.tree[page.notebook]
|
|
2
3
|
if (item.filter == null) {
|
|
3
4
|
item.filter = 'auto'
|
|
4
5
|
}
|
|
5
6
|
if (item.placeholder == null && item.filter == 'auto') {
|
|
6
7
|
if (theme.wiki.tree[page.wiki]?.name) {
|
|
7
8
|
item.placeholder = __('search.search_in', theme.wiki.tree[page.wiki]?.name)
|
|
9
|
+
} else if (notebook) {
|
|
10
|
+
item.placeholder = __('search.search_in', notebook.name || __('btn.notebook'))
|
|
11
|
+
item.filter = notebook.base_dir
|
|
8
12
|
}
|
|
9
13
|
}
|
|
10
14
|
function layoutDiv() {
|
|
@@ -21,6 +21,10 @@ function layoutDiv() {
|
|
|
21
21
|
return false
|
|
22
22
|
})
|
|
23
23
|
arr = arr.sort((p1, p2) => p1.updated > p2.updated ? -1 : 1)
|
|
24
|
+
} else if (page.layout === 'notebooks') {
|
|
25
|
+
arr = site.pages.filter(p => p.notebook).sort('-updated')
|
|
26
|
+
} else if (page.notebook) {
|
|
27
|
+
arr = site.pages.filter(p => p.notebook === page.notebook).sort('-updated')
|
|
24
28
|
} else {
|
|
25
29
|
arr = site.posts.filter( p => p.title && p.title.length > 0)
|
|
26
30
|
arr = arr.sort("updated", -1)
|
|
@@ -38,6 +42,12 @@ function layoutDiv() {
|
|
|
38
42
|
if (name) {
|
|
39
43
|
el += '<strong>' + name + '</strong>' + '<span class="dot"></span>';
|
|
40
44
|
}
|
|
45
|
+
} else if (page.layout === 'notebooks') {
|
|
46
|
+
const notebook = theme.notebooks.tree[post.notebook]
|
|
47
|
+
const name = notebook?.name || post.notebook
|
|
48
|
+
if (name) {
|
|
49
|
+
el += '<strong>' + name + '</strong>' + '<span class="dot"></span>';
|
|
50
|
+
}
|
|
41
51
|
}
|
|
42
52
|
el += post.title + '</span>';
|
|
43
53
|
el += '</a>';
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<%# 笔记本的标签树 %>
|
|
2
|
+
<% const notebook = theme.notebooks.tree[page.notebook] %>
|
|
3
|
+
<% const tagTree = notebook?.tagTree %>
|
|
4
|
+
<% const isLeaf = tag => tag.id === '' || tag.children.length === 0 %>
|
|
5
|
+
<% const getTagcon = tag => {
|
|
6
|
+
const tagcons = theme.notebook.tagcons || {}
|
|
7
|
+
return tagcons[tag.name] || tagcons[tag.id] || tagcons[tag.part] || tagcons[tag.part.toLowerCase()] || tagcons['']
|
|
8
|
+
} %>
|
|
9
|
+
|
|
10
|
+
<% function layoutTag(tag, level) { %>
|
|
11
|
+
<% const active = page.activeTag === tag.id %>
|
|
12
|
+
<a class="link<%= active ? ' active' : '' %>" href="<%= url_for(tag.path) %>">
|
|
13
|
+
<span class="toc-text"<% if (level > 0) { %> style="padding-left: <%= level * 0.875 %>rem;"<% } %>>
|
|
14
|
+
<% if (tag.id === '') { %>
|
|
15
|
+
<%= __('meta.all_notes') %>
|
|
16
|
+
<% } else { %>
|
|
17
|
+
<% const tagcon = item.show_tagcon && getTagcon(tag) %>
|
|
18
|
+
<% if (tagcon) { %><span class="tagcon"><%- icon(tagcon) %></span><% } %>
|
|
19
|
+
<%= tag.part %>
|
|
20
|
+
<% } %>
|
|
21
|
+
</span>
|
|
22
|
+
<span class="toc-text tag-switcher-wrapper">
|
|
23
|
+
<span class="tag-switcher" />
|
|
24
|
+
</span>
|
|
25
|
+
</a>
|
|
26
|
+
<% } %>
|
|
27
|
+
|
|
28
|
+
<% function layoutChildTags(tag, level) { %>
|
|
29
|
+
<% for (const child of tag.children) { %>
|
|
30
|
+
<%= tagAndSub(tagTree.get(child), level + 1) %>
|
|
31
|
+
<% } %>
|
|
32
|
+
<% } %>
|
|
33
|
+
|
|
34
|
+
<% function tagAndSub(tag, level) { %>
|
|
35
|
+
<% if (!tag) return '' %>
|
|
36
|
+
<% const active = page.activeTag === tag.id %>
|
|
37
|
+
<% const expanded = item.expand_all || (item.expand_active && active) || page.activeTag?.startsWith(`${tag.id}/`) %>
|
|
38
|
+
<% const classes = [isLeaf(tag) ? ' leaf-tag' : ' parent-tag', expanded ? ' expanded' : ''] %>
|
|
39
|
+
<div class="tag-subtree<%= classes.join('') %>" data-tag="<%= tag.id %>">
|
|
40
|
+
<%= layoutTag(tag, level) %>
|
|
41
|
+
<% if (!isLeaf(tag)) { %>
|
|
42
|
+
<%= layoutChildTags(tag, level) %>
|
|
43
|
+
<% } %>
|
|
44
|
+
</div>
|
|
45
|
+
<% } %>
|
|
46
|
+
|
|
47
|
+
<% if (tagTree) { %>
|
|
48
|
+
<widget class="widget-wrapper<%= scrollreveal(' ') %> post-list">
|
|
49
|
+
<div class="widget-header dis-select">
|
|
50
|
+
<span class="name"><%= __('meta.tag_tree') %></span>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="widget-body fs14 tag-tree">
|
|
53
|
+
<%= tagAndSub(tagTree.get(''), 0) %>
|
|
54
|
+
<% for (const child of tagTree.get('').children) { %>
|
|
55
|
+
<%= tagAndSub(tagTree.get(child), 0) %>
|
|
56
|
+
<% } %>
|
|
57
|
+
</div>
|
|
58
|
+
</widget>
|
|
59
|
+
<% } %>
|
|
@@ -55,11 +55,13 @@ function layoutDiv(fallback) {
|
|
|
55
55
|
el += icon('default:upup')
|
|
56
56
|
el += `<span>${__('btn.top')}</span>`
|
|
57
57
|
el += `</a>`
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
if (theme.comments.service && theme.comments.service.length > 0) {
|
|
59
|
+
if (page.comments == undefined || page.comments != false) {
|
|
60
|
+
el += `<a class="buttom" onclick="util.scrollComment()">`
|
|
61
|
+
el += icon('default:tocomment')
|
|
62
|
+
el += `<span>${__('btn.comments')}</span>`
|
|
63
|
+
el += `</a>`
|
|
64
|
+
}
|
|
63
65
|
}
|
|
64
66
|
el += `</div>`
|
|
65
67
|
el += `</widget>`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<%
|
|
2
2
|
const { field } = theme.plugins.tianli_gpt;
|
|
3
|
-
const matchesField = field === 'all' || field === layout || (field === 'wiki' && page.wiki) || (field === 'topic' && page.topic);
|
|
3
|
+
const matchesField = field === 'all' || field === page.layout || (field === 'wiki' && page.wiki) || (field === 'topic' && page.topic);
|
|
4
4
|
if (matchesField) { %>
|
|
5
5
|
<script defer src="<%- theme.plugins.tianli_gpt?.js %>"></script>
|
|
6
6
|
<script defer>
|
package/layout/layout.ejs
CHANGED
|
@@ -37,7 +37,11 @@ if (theme.style.site && theme.style.site['background-image']) {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
var html = `<!DOCTYPE html>`
|
|
40
|
-
|
|
40
|
+
if (theme.style.prefers_theme === 'auto') {
|
|
41
|
+
html += `<html lang="${page.lang}">`
|
|
42
|
+
} else {
|
|
43
|
+
html += `<html lang="${page.lang}" data-theme="${theme.style.prefers_theme}">`
|
|
44
|
+
}
|
|
41
45
|
html += partial('_partial/head')
|
|
42
46
|
html += `<body>`
|
|
43
47
|
html += site_background
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<%# 笔记本列表页主体部分 %>
|
|
2
|
+
<% for (const notebook of Object.values(theme.notebooks.tree)) { %>
|
|
3
|
+
<div class="post-list wiki">
|
|
4
|
+
<a class="post-card wiki<%= scrollreveal(' ') %>" href="<%= url_for(notebook.base_dir) %>">
|
|
5
|
+
<%- include('_partial/main/notebook/notebook_card', { notebook: notebook }) %>
|
|
6
|
+
</a>
|
|
7
|
+
</div>
|
|
8
|
+
<% } %>
|
package/layout/notes.ejs
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<%# 笔记列表页主体部分 %>
|
|
2
|
+
<div class="post-list post">
|
|
3
|
+
<% page.posts.each(post => { %>
|
|
4
|
+
<a
|
|
5
|
+
class="post-card<%= scrollreveal(' ') %> post"
|
|
6
|
+
href="<%= url_for(post.link || post.path) %><%= page.activeTag ? `?tag=${page.activeTag}` : '' %>"
|
|
7
|
+
>
|
|
8
|
+
<%- include('_partial/main/notebook/note_card', { note: post }) %>
|
|
9
|
+
</a>
|
|
10
|
+
<% }) %>
|
|
11
|
+
</div>
|
|
12
|
+
<%- include('_partial/main/notebook/paginator') %>
|
package/layout/page.ejs
CHANGED
|
@@ -3,6 +3,13 @@ const { layout } = page
|
|
|
3
3
|
// 是否使用 Heti 布局插件
|
|
4
4
|
const isUsingHeti = theme.plugins.heti?.enable
|
|
5
5
|
|
|
6
|
+
const notebook = theme.notebooks.tree[page.notebook]
|
|
7
|
+
if (notebook) {
|
|
8
|
+
page.menu_id ??= notebook.menu_id
|
|
9
|
+
page.license ??= notebook.license
|
|
10
|
+
page.share ??= notebook.share
|
|
11
|
+
}
|
|
12
|
+
|
|
6
13
|
// 默认的 menu_id
|
|
7
14
|
if (page.menu_id == null) {
|
|
8
15
|
if (page.wiki?.length > 0) {
|
|
@@ -40,7 +47,10 @@ function layoutDiv() {
|
|
|
40
47
|
if (page.content && page.content.length > 0) {
|
|
41
48
|
el += page.content
|
|
42
49
|
}
|
|
43
|
-
if (
|
|
50
|
+
if (notebook) {
|
|
51
|
+
el += partial('_partial/main/notebook/note_tags', { notebook: notebook })
|
|
52
|
+
}
|
|
53
|
+
if (layout === 'post' || page.wiki || notebook) {
|
|
44
54
|
el += partial('_partial/main/article/article_footer')
|
|
45
55
|
}
|
|
46
56
|
el += `</article>`
|
package/package.json
CHANGED
package/scripts/events/index.js
CHANGED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* notebooks.js v1
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
'use strict'
|
|
6
|
+
|
|
7
|
+
class NotePage {
|
|
8
|
+
constructor(page) {
|
|
9
|
+
this.id = page._id
|
|
10
|
+
this.notebook = page.notebook
|
|
11
|
+
this.title = page.title
|
|
12
|
+
this.tags = page.tags
|
|
13
|
+
this.path = page.path
|
|
14
|
+
this.path_key = page.path.replace('.html', '')
|
|
15
|
+
this.layout = page.layout
|
|
16
|
+
this.date = page.date
|
|
17
|
+
this.updated = page.updated || page.date
|
|
18
|
+
|
|
19
|
+
const pin = page.pin ?? page.sticky ?? 0
|
|
20
|
+
if (pin === true) {
|
|
21
|
+
this.pin = 1
|
|
22
|
+
} else if (pin === false) {
|
|
23
|
+
this.pin = 0
|
|
24
|
+
} else {
|
|
25
|
+
this.pin = pin
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function splitTag(tag) {
|
|
31
|
+
return tag.split('/').filter(t => t.length > 0)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function prepareNotebook(id, info, ctx) {
|
|
35
|
+
const notebook = info
|
|
36
|
+
notebook.id = id
|
|
37
|
+
|
|
38
|
+
if (notebook.base_dir) {
|
|
39
|
+
if (notebook.base_dir.startsWith('/')) {
|
|
40
|
+
notebook.base_dir = notebook.base_dir.substring(1)
|
|
41
|
+
}
|
|
42
|
+
if (notebook.base_dir.endsWith('/')) {
|
|
43
|
+
notebook.base_dir = notebook.base_dir.substring(0, notebook.base_dir.length - 1)
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
const notebooksBaseDir = ctx.theme.config.site_tree.notebooks.base_dir
|
|
47
|
+
notebook.base_dir = notebooksBaseDir ? `${notebooksBaseDir}/${id}` : id
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
notebook.sort ||= 0
|
|
51
|
+
notebook.auto_excerpt ||= ctx.theme.config.notebook.auto_excerpt || 0
|
|
52
|
+
notebook.per_page ??= ctx.theme.config.notebook.per_page ?? ctx.config.per_page ?? 10
|
|
53
|
+
notebook.order_by ||= ctx.theme.config.notebook.order_by || '-updated'
|
|
54
|
+
notebook.menu_id ??= ctx.theme.config.site_tree.notes.menu_id
|
|
55
|
+
notebook.license ??= ctx.theme.config.notebook.license
|
|
56
|
+
notebook.share ??= ctx.theme.config.notebook.share
|
|
57
|
+
|
|
58
|
+
notebook.leftbar ??= ctx.theme.config.site_tree.notes.leftbar
|
|
59
|
+
notebook.rightbar ??= ctx.theme.config.site_tree.notes.rightbar
|
|
60
|
+
notebook.note_leftbar ??= ctx.theme.config.site_tree.note.leftbar
|
|
61
|
+
notebook.note_rightbar ??= ctx.theme.config.site_tree.note.rightbar
|
|
62
|
+
|
|
63
|
+
const tagMap = new Map() // tagId: tagInfo
|
|
64
|
+
notebook.tagTree = tagMap
|
|
65
|
+
|
|
66
|
+
const rootTag = {
|
|
67
|
+
id: '',
|
|
68
|
+
name: '',
|
|
69
|
+
part: '',
|
|
70
|
+
path: notebook.base_dir,
|
|
71
|
+
parent: null, // parent tag id
|
|
72
|
+
childSet: new Set(), // child tag ids
|
|
73
|
+
noteSet: new Set(), // note ids
|
|
74
|
+
}
|
|
75
|
+
tagMap.set(rootTag.id, rootTag)
|
|
76
|
+
|
|
77
|
+
// Iterate through all notes in the notebook, build the tag tree.
|
|
78
|
+
const allPages = ctx.locals.get('pages')
|
|
79
|
+
const pages = allPages.filter(p => p.notebook === notebook.id)
|
|
80
|
+
for (const page of pages.data) {
|
|
81
|
+
rootTag.noteSet.add(page._id)
|
|
82
|
+
|
|
83
|
+
if (!page.tags) {
|
|
84
|
+
continue
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (const hierarchyTag of page.tags) {
|
|
88
|
+
const parts = splitTag(hierarchyTag)
|
|
89
|
+
let parent = rootTag
|
|
90
|
+
for (const part of parts) {
|
|
91
|
+
const tagName = parent.name ? `${parent.name}/${part}` : part
|
|
92
|
+
const tagId = tagName.toLowerCase()
|
|
93
|
+
let tag = tagMap.get(tagId)
|
|
94
|
+
if (tag == null) {
|
|
95
|
+
tag = {
|
|
96
|
+
id: tagId,
|
|
97
|
+
name: tagName,
|
|
98
|
+
part: part,
|
|
99
|
+
path: `${notebook.base_dir}/tags/${tagId}`,
|
|
100
|
+
parent: parent.id,
|
|
101
|
+
childSet: new Set(),
|
|
102
|
+
noteSet: new Set(),
|
|
103
|
+
}
|
|
104
|
+
tagMap.set(tagId, tag)
|
|
105
|
+
parent.childSet.add(tagId)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
tag.noteSet.add(page._id)
|
|
109
|
+
parent = tag
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
notebook.noteMap = pages.map(p => new NotePage(p)).reduce((map, note) => {
|
|
115
|
+
map.set(note.id, note)
|
|
116
|
+
return map
|
|
117
|
+
}, new Map())
|
|
118
|
+
|
|
119
|
+
// Sort child tags for each tag.
|
|
120
|
+
for (const [_, tag] of tagMap) {
|
|
121
|
+
tag.children = Array.from(tag.childSet)
|
|
122
|
+
tag.children.sort()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return notebook
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function getNotebooksObject(ctx) {
|
|
129
|
+
const notebooks = {
|
|
130
|
+
tree: {},
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const data = ctx.locals.get('data')
|
|
134
|
+
const list = []
|
|
135
|
+
for (const [key, info] of Object.entries(data)) {
|
|
136
|
+
if (!key.startsWith('notebooks/') || key.endsWith('.DS_Store')) {
|
|
137
|
+
continue
|
|
138
|
+
}
|
|
139
|
+
const id = key.substring(10)
|
|
140
|
+
list.push(prepareNotebook(id, info, ctx))
|
|
141
|
+
}
|
|
142
|
+
list.sort((a, b) => a.sort - b.sort)
|
|
143
|
+
for (const info of list) {
|
|
144
|
+
notebooks.tree[info.id] = info
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return notebooks
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
module.exports = ctx => {
|
|
151
|
+
const notebooks = getNotebooksObject(ctx)
|
|
152
|
+
ctx.theme.config.notebooks = notebooks
|
|
153
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* notebooks v1
|
|
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
|
+
|
|
31
|
+
hexo.extend.generator.register('notebooks', function (locals) {
|
|
32
|
+
const { site_tree, notebooks } = hexo.theme.config
|
|
33
|
+
if (notebooks.tree.length === 0) {
|
|
34
|
+
return []
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const routes = []
|
|
38
|
+
|
|
39
|
+
// The index page of all notebooks.
|
|
40
|
+
routes.push({
|
|
41
|
+
path: site_tree.notebooks.base_dir + '/index.html',
|
|
42
|
+
layout: ['notebooks'],
|
|
43
|
+
data: {
|
|
44
|
+
layout: 'notebooks',
|
|
45
|
+
menu_id: site_tree.notebooks.menu_id,
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
for (const notebook of Object.values(notebooks.tree)) {
|
|
50
|
+
const pages = locals.pages.filter(p => notebook.noteMap.has(p._id)).sort(notebook.order_by)
|
|
51
|
+
pages.data.sort((a, b) => notebook.noteMap.get(b._id).pin - notebook.noteMap.get(a._id).pin)
|
|
52
|
+
|
|
53
|
+
// Note list pages (for every tag) of current notebook.
|
|
54
|
+
for (const [_, tag] of notebook.tagTree) {
|
|
55
|
+
const notes = pages.filter(p => tag.noteSet.has(p._id))
|
|
56
|
+
const slices = paginationWithEmpty(tag.path, notes, {
|
|
57
|
+
perPage: notebook.per_page,
|
|
58
|
+
layout: ['notes'],
|
|
59
|
+
data: {
|
|
60
|
+
layout: 'notes',
|
|
61
|
+
menu_id: notebook.menu_id,
|
|
62
|
+
notebook: notebook.id,
|
|
63
|
+
activeTag: tag.id,
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
routes.push(...slices)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return routes
|
|
71
|
+
})
|
package/scripts/tags/index.js
CHANGED
|
@@ -43,4 +43,9 @@ hexo.extend.tag.register('hashtag', require('./lib/hashtag')(hexo))
|
|
|
43
43
|
hexo.extend.tag.register('okr', require('./lib/okr')(hexo), {ends: true})
|
|
44
44
|
hexo.extend.tag.register('audio', require('./lib/audio')(hexo))
|
|
45
45
|
hexo.extend.tag.register('video', require('./lib/video')(hexo))
|
|
46
|
+
|
|
47
|
+
// read 阅读类
|
|
48
|
+
hexo.extend.tag.register('reel', require('./lib/read/reel')(hexo), true)
|
|
49
|
+
hexo.extend.tag.register('paper', require('./lib/read/paper')(hexo), true)
|
|
50
|
+
|
|
46
51
|
// others
|
package/scripts/tags/lib/quot.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
'use strict'
|
|
11
11
|
|
|
12
|
-
module.exports = ctx => function(args) {
|
|
12
|
+
module.exports = ctx => function (args) {
|
|
13
13
|
var el = ''
|
|
14
14
|
args = ctx.args.map(args, ['el', 'icon', 'prefix', 'suffix'], ['text'])
|
|
15
15
|
if (!args.el) {
|
|
@@ -43,7 +43,6 @@ module.exports = ctx => function(args) {
|
|
|
43
43
|
if (args.el.includes('h')) {
|
|
44
44
|
el += '<div' + ' class="tag-plugin quot">'
|
|
45
45
|
el += '<' + args.el + ' class="content" id="' + args.text + '"' + type + '>'
|
|
46
|
-
el += '<a href="#' + args.text + '" class="headerlink" title="' + args.text + '"></a>'
|
|
47
46
|
el += content()
|
|
48
47
|
el += '</' + args.el + '>'
|
|
49
48
|
el += '</div>'
|