hexo-theme-stellar 1.42.0 → 1.43.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 (122) hide show
  1. package/.claude/skills/stellar-theme-dev/SKILL.md +2 -0
  2. package/CHANGELOG.md +42 -0
  3. package/THIRD-PARTY-NOTICES.md +28 -0
  4. package/_config.yml +20 -10
  5. package/_data/icons.yml +10 -9
  6. package/languages/en.yml +11 -0
  7. package/languages/zh-CN.yml +11 -0
  8. package/languages/zh-TW.yml +11 -0
  9. package/layout/_partial/comments/artalk/layout.ejs +1 -1
  10. package/layout/_partial/comments/beaudar/layout.ejs +1 -1
  11. package/layout/_partial/comments/giscus/layout.ejs +1 -1
  12. package/layout/_partial/comments/twikoo/layout.ejs +1 -1
  13. package/layout/_partial/comments/utterances/layout.ejs +1 -1
  14. package/layout/_partial/comments/waline/layout.ejs +1 -1
  15. package/layout/_partial/components/collection-item.ejs +44 -0
  16. package/layout/_partial/components/collection.ejs +26 -0
  17. package/layout/_partial/components/widget-frame.ejs +35 -0
  18. package/layout/_partial/cover/wiki_cover.ejs +82 -26
  19. package/layout/_partial/dropdown.ejs +62 -0
  20. package/layout/_partial/head.ejs +9 -0
  21. package/layout/_partial/main/navbar/breadcrumb/blog.ejs +1 -1
  22. package/layout/_partial/main/post_list/archive_item.ejs +12 -0
  23. package/layout/_partial/main/post_list/topic_card.ejs +21 -9
  24. package/layout/_partial/main/post_list/wiki_card.ejs +49 -10
  25. package/layout/_partial/scripts/bootstrap.ejs +3 -1
  26. package/layout/_partial/scripts/defines.ejs +2 -8
  27. package/layout/_partial/scripts/lazyload.ejs +0 -3
  28. package/layout/_partial/scripts.ejs +1 -0
  29. package/layout/_partial/sidebar/index_leftbar.ejs +12 -2
  30. package/layout/_partial/sidebar/logo.ejs +1 -1
  31. package/layout/_partial/sidebar/menu.ejs +21 -23
  32. package/layout/_partial/widgets/components/linklist.ejs +23 -21
  33. package/layout/_partial/widgets/linklist.ejs +7 -12
  34. package/layout/_partial/widgets/recent.ejs +52 -41
  35. package/layout/_partial/widgets/related.ejs +47 -36
  36. package/layout/_partial/widgets/tagtree.ejs +70 -54
  37. package/layout/_partial/widgets/toc.ejs +37 -62
  38. package/layout/_partial/widgets/tree.ejs +38 -51
  39. package/layout/_plugins/index.ejs +25 -0
  40. package/layout/_plugins/mermaid.ejs +3 -4
  41. package/layout/archive.ejs +11 -20
  42. package/layout/collection-preview.ejs +60 -0
  43. package/layout/index_topic.ejs +12 -6
  44. package/layout/index_wiki.ejs +49 -3
  45. package/layout/layout.ejs +4 -3
  46. package/package.json +1 -1
  47. package/scripts/events/index.js +0 -2
  48. package/scripts/filters/lib/img_lazyload.js +184 -40
  49. package/scripts/generators/stellar-icons.js +1 -1
  50. package/scripts/lib/subtitle.js +5 -1
  51. package/scripts/tags/index.js +1 -0
  52. package/scripts/tags/lib/about.js +1 -1
  53. package/scripts/tags/lib/albums.js +1 -1
  54. package/scripts/tags/lib/banner.js +1 -1
  55. package/scripts/tags/lib/dropdown.js +64 -0
  56. package/scripts/tags/lib/friends.js +1 -1
  57. package/scripts/tags/lib/gallery.js +3 -3
  58. package/scripts/tags/lib/image.js +1 -1
  59. package/scripts/tags/lib/posters.js +1 -1
  60. package/scripts/tags/lib/sites.js +8 -4
  61. package/source/css/_common/base.styl +4 -4
  62. package/source/css/_common/blockquote.styl +2 -2
  63. package/source/css/_common/dropdown.styl +132 -0
  64. package/source/css/_common/html.styl +1 -1
  65. package/source/css/_common/loading.styl +5 -2
  66. package/source/css/_common/title.styl +8 -8
  67. package/source/css/_components/collection.styl +188 -0
  68. package/source/css/_components/layout.styl +1 -1
  69. package/source/css/_components/list.styl +44 -46
  70. package/source/css/_components/md.styl +3 -3
  71. package/source/css/_components/page-transition.styl +22 -0
  72. package/source/css/_components/pages/archives.styl +19 -18
  73. package/source/css/_components/pages/article-indent.styl +2 -2
  74. package/source/css/_components/pages/article-story.styl +9 -5
  75. package/source/css/_components/pages/collection-preview.styl +68 -0
  76. package/source/css/_components/pages/notebook.styl +11 -9
  77. package/source/css/_components/partial/article-footer.styl +7 -4
  78. package/source/css/_components/partial/cover.styl +280 -1
  79. package/source/css/_components/partial/related.styl +10 -6
  80. package/source/css/_components/sidebar/footer.styl +65 -12
  81. package/source/css/_components/sidebar/nav-area.styl +6 -1
  82. package/source/css/_components/sidebar/search.styl +6 -2
  83. package/source/css/_components/tag-plugins/chat.styl +1 -1
  84. package/source/css/_components/tag-plugins/folders.styl +4 -4
  85. package/source/css/_components/tag-plugins/folding.styl +3 -3
  86. package/source/css/_components/tag-plugins/hashtag.styl +2 -3
  87. package/source/css/_components/tag-plugins/mdrender.styl +1 -1
  88. package/source/css/_components/tag-plugins/note.styl +4 -4
  89. package/source/css/_components/tag-plugins/poetry.styl +5 -5
  90. package/source/css/_components/tag-plugins/quot.styl +1 -2
  91. package/source/css/_components/tag-plugins/quote.styl +1 -1
  92. package/source/css/_components/tag-plugins/tabs.styl +2 -2
  93. package/source/css/_components/tag-plugins/timeline.styl +4 -4
  94. package/source/css/_components/widgets/ghrepo.styl +4 -0
  95. package/source/css/_components/widgets/markdown.styl +1 -14
  96. package/source/css/_components/widgets/timeline.styl +2 -2
  97. package/source/css/_components/widgets/toc.styl +22 -20
  98. package/source/css/_components/widgets/widgets.styl +13 -1
  99. package/source/css/_components/wiki-card.styl +209 -0
  100. package/source/css/_custom.styl +22 -20
  101. package/source/css/_plugins/lazyload.styl +8 -5
  102. package/source/css/comments/utterances.styl +2 -2
  103. package/source/css/comments/waline.styl +24 -0
  104. package/source/css/plugins/mermaid.styl +31 -12
  105. package/source/js/icons.js +1 -1
  106. package/source/js/main.js +62 -6
  107. package/source/js/plugins/adaptive-text.js +46 -2
  108. package/source/js/plugins/dropdown.js +435 -0
  109. package/source/js/plugins/galaxy.js +423 -0
  110. package/source/js/search/algolia-search.js +1 -1
  111. package/source/js/search/local-search.js +1 -1
  112. package/source/js/services/ghinfo.js +31 -3
  113. package/source/js/services/siteinfo.js +26 -0
  114. package/source/js/services/sites.js +6 -2
  115. package/source/js/services.js +5 -2
  116. package/source/js/tagtree.js +7 -7
  117. package/source/js/utils.js +1 -1
  118. package/layout/_partial/widgets/components/link.ejs +0 -21
  119. package/source/css/_components/sidebar/menu.styl +0 -77
  120. package/source/css/_components/widgets/components.styl +0 -68
  121. package/source/css/_components/widgets/list.styl +0 -46
  122. package/source/css/_components/widgets/related.styl +0 -40
@@ -1,61 +1,72 @@
1
1
  <%
2
2
  function layoutDiv() {
3
- var el = `<widget class="widget-wrapper${scrollreveal(' ')} recent post-list">`
4
- // header
5
- el += '<div class="widget-header dis-select">';
6
- el += '<span class="name">' + __("meta.recent_update") + '</span>';
3
+ var action = ''
7
4
  if (item.rss) {
8
- el += '<a class="cap-action" id="rss" title="Subscribe" href="' + item.rss + '">';
9
- el += icon('default:rss')
10
- el += '</a>';
5
+ action += '<a class="cap-action" id="rss" title="Subscribe" href="' + escape_html(item.rss) + '">'
6
+ action += icon('default:rss')
7
+ action += '</a>'
11
8
  }
12
- el += '</div>';
13
- // body
14
- var arr = [];
9
+ var arr = []
15
10
  if (page.menu_id == 'wiki') {
16
- arr = theme.wiki.all_pages.filter( p => {
11
+ arr = theme.wiki.all_pages.filter(function(p) {
17
12
  if (p.wiki) {
18
- let proj = theme.wiki.tree[p.wiki];
19
- return proj?.index != false;
13
+ var proj = theme.wiki.tree[p.wiki]
14
+ return proj?.index != false
20
15
  }
21
16
  return false
22
17
  })
23
- arr = arr.sort((p1, p2) => p1.updated > p2.updated ? -1 : 1)
18
+ arr = arr.sort(function(p1, p2) {
19
+ return p1.updated > p2.updated ? -1 : 1
20
+ })
24
21
  } else if (page.layout === 'notebooks') {
25
- arr = site.pages.filter(p => p.notebook).sort('-updated')
22
+ arr = site.pages.filter(function(p) {
23
+ return p.notebook
24
+ }).sort('-updated')
26
25
  } else if (page.notebook) {
27
- arr = site.pages.filter(p => p.notebook === page.notebook).sort('-updated')
26
+ arr = site.pages.filter(function(p) {
27
+ return p.notebook === page.notebook
28
+ }).sort('-updated')
28
29
  } else {
29
- arr = site.posts.filter( p => p.title && p.title.length > 0)
30
- arr = arr.sort("updated", -1)
30
+ arr = site.posts.filter(function(p) {
31
+ return p.title && p.title.length > 0
32
+ }).sort('updated', -1)
31
33
  }
32
- el += '<div class="widget-body fs14">'
33
- arr = arr.filter(p => p.title?.length > 0)
34
+ arr = arr.filter(function(p) {
35
+ return p.title?.length > 0
36
+ })
34
37
  arr.length = item.limit
35
- arr.forEach(post => {
36
- if (!post) { return }
37
- el += `<a class="item title" href="${pretty_url(post.link || post.path)}">`
38
- el += '<span class="title">'
38
+ var entries = []
39
+ arr.forEach(function(post) {
40
+ if (!post) {
41
+ return
42
+ }
43
+ var prefix = ''
39
44
  if (post.wiki) {
40
- const proj = theme.wiki.tree[post.wiki];
41
- let name = proj?.name || post?.wiki;
42
- if (name) {
43
- el += '<strong>' + name + '</strong>' + '<span class="dot"></span>';
44
- }
45
+ var proj = theme.wiki.tree[post.wiki]
46
+ prefix = proj?.name || post.wiki || ''
45
47
  } 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
- }
48
+ var notebook = theme.notebooks.tree[post.notebook]
49
+ prefix = notebook?.name || post.notebook || ''
51
50
  }
52
- el += post.title + '</span>';
53
- el += '</a>';
54
- el += '';
55
- });
56
- el += '</div>';
57
- el += '</widget>';
58
- return el;
51
+ entries.push({
52
+ href: pretty_url(post.link || post.path),
53
+ title: post.title,
54
+ prefix: prefix,
55
+ indicator: false
56
+ })
57
+ })
58
+ var body = partial('_partial/components/collection', {options: {
59
+ layout: 'list',
60
+ variant: 'nav',
61
+ density: 'compact',
62
+ items: entries
63
+ }})
64
+ return partial('_partial/components/widget-frame', {
65
+ className: 'recent-widget',
66
+ title: __('meta.recent_update'),
67
+ action: action,
68
+ body: body
69
+ })
59
70
  }
60
71
  %>
61
72
  <%- layoutDiv() %>
@@ -3,63 +3,74 @@ function relatedPostsInTopic() {
3
3
  if (page.topic?.length == 0) {
4
4
  return ''
5
5
  }
6
- const topic = theme.topic.tree[page.topic]
6
+ var topic = theme.topic.tree[page.topic]
7
7
  if (topic == null) {
8
8
  return ''
9
9
  }
10
- var el = ''
11
- el += `<widget class="widget-wrapper related post-list">`
12
- el += `<div class="widget-header dis-select">`
13
- el += `<span class="name">${__('btn.topic') + __('symbol.colon') + (topic.name || topic.title)}</span>`
14
- el += `</div>`
15
- el += `<div class="widget-body">`
16
- for (let post of topic.pages) {
17
- const isActive = post.path == page.path
18
- el += `<a class="item${isActive ? ' active' : ''}" href="${pretty_url(post.path)}">`
19
- el += `<span class="title">${post.title}</span>`
20
- if (isActive) {
21
- el += '<span class="active-dot"></span>'
22
- }
23
- el += `</a>`
10
+ var entries = []
11
+ for (var post of topic.pages) {
12
+ entries.push({
13
+ href: pretty_url(post.path),
14
+ title: post.title,
15
+ active: post.path == page.path
16
+ })
24
17
  }
25
- el += `</div>`
26
- el += `</widget>`
27
- return el
18
+ var body = partial('_partial/components/collection', {options: {
19
+ layout: 'list',
20
+ variant: 'nav',
21
+ density: 'compact',
22
+ items: entries
23
+ }})
24
+ return partial('_partial/components/widget-frame', {
25
+ className: 'related-widget',
26
+ title: __('btn.topic') + __('symbol.colon') + (topic.name || topic.title),
27
+ body: body,
28
+ reveal: false
29
+ })
28
30
  }
31
+
29
32
  function relatedWiki() {
30
- let thisItemObject = theme.wiki.tree[page.wiki]
33
+ var thisItemObject = theme.wiki.tree[page.wiki]
31
34
  if (thisItemObject == null) {
32
35
  return ''
33
36
  }
34
- const relatedItems = thisItemObject.relatedItems || []
37
+ var relatedItems = thisItemObject.relatedItems || []
35
38
  var el = ''
36
- for (let relatedItem of relatedItems) {
37
- el += `<widget class="widget-wrapper${scrollreveal(' ')} post-card">`
38
- el += `<div class="widget-header dis-select">`
39
- el += `<span class="name">${__('meta.more') + __('symbol.colon') + relatedItem.name}</span>`
40
- el += `</div>`
41
- el += `<div class="widget-body">`
42
- for (let id of relatedItem.items) {
43
- // 同一个分组中的其它项目
44
- let item = theme.wiki.tree[id]
45
- el += `<a class="item wiki" href="${pretty_url(item.homepage?.path)}">`
46
- el += `<span class="title">${item.title}</span>`
47
- if (item.description && item.description.length > 0) {
48
- el += `<span class="excerpt">${item.description}</span>`
39
+ for (var relatedItem of relatedItems) {
40
+ var entries = []
41
+ for (var id of relatedItem.items) {
42
+ var relatedProject = theme.wiki.tree[id]
43
+ if (relatedProject) {
44
+ entries.push({
45
+ href: pretty_url(relatedProject.homepage?.path),
46
+ title: relatedProject.title,
47
+ description: relatedProject.description,
48
+ indicator: false
49
+ })
49
50
  }
50
- el += `</a>`
51
51
  }
52
- el += `</div>`
53
- el += `</widget>`
52
+ var body = partial('_partial/components/collection', {options: {
53
+ layout: 'list',
54
+ variant: 'summary',
55
+ density: 'compact',
56
+ items: entries
57
+ }})
58
+ el += partial('_partial/components/widget-frame', {
59
+ className: 'related-summary-widget',
60
+ title: __('meta.more') + __('symbol.colon') + relatedItem.name,
61
+ body: body
62
+ })
54
63
  }
55
64
  return el
56
65
  }
66
+
57
67
  function layoutDiv() {
58
68
  if (page.wiki?.length > 0) {
59
69
  return relatedWiki()
60
70
  } else if (page.topic?.length > 0) {
61
71
  return relatedPostsInTopic()
62
72
  }
73
+ return ''
63
74
  }
64
75
  %>
65
76
  <%- layoutDiv() %>
@@ -1,59 +1,75 @@
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 || {}
1
+ <%
2
+ var notebook = theme.notebooks.tree[page.notebook]
3
+ var tagTree = notebook?.tagTree
4
+
5
+ function isLeaf(tag) {
6
+ return tag.id === '' || tag.children.length === 0
7
+ }
8
+
9
+ function getTagcon(tag) {
10
+ var tagcons = theme.notebook.tagcons || {}
7
11
  return tagcons[tag.name] || tagcons[tag.id] || tagcons[tag.part] || tagcons[tag.part.toLowerCase()] || tagcons['']
8
- } %>
12
+ }
9
13
 
10
- <% function layoutTag(tag, level) { %>
11
- <% const active = page.activeTag === tag.id %>
12
- <a class="link<%= active ? ' active' : '' %>" href="<%= pretty_url(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
- <% } %>
14
+ function layoutTag(tag, level) {
15
+ var active = page.activeTag === tag.id
16
+ var tagcon = tag.id !== '' && item.show_tagcon ? getTagcon(tag) : ''
17
+ var trailing = ''
18
+ if (!isLeaf(tag)) {
19
+ trailing = '<span class="tag-switcher-wrapper"><span class="tag-switcher"></span></span>'
20
+ }
21
+ return partial('_partial/components/collection-item', {
22
+ item: {
23
+ href: pretty_url(tag.path),
24
+ title: tag.id === '' ? __('meta.all_notes') : tag.part,
25
+ icon: tagcon,
26
+ active: active,
27
+ indicator: false,
28
+ depth: level,
29
+ className: 'tag-tree__link',
30
+ after: trailing
31
+ }
32
+ })
33
+ }
27
34
 
28
- <% function layoutChildTags(tag, level) { %>
29
- <% for (const child of tag.children) { %>
30
- <%= tagAndSub(tagTree.get(child), level + 1) %>
31
- <% } %>
32
- <% } %>
35
+ function tagAndSub(tag, level) {
36
+ if (!tag) {
37
+ return ''
38
+ }
39
+ var active = page.activeTag === tag.id
40
+ var expanded = item.expand_all || (item.expand_active && active) || page.activeTag?.startsWith(tag.id + '/')
41
+ var classes = isLeaf(tag) ? ' is-leaf' : ' is-parent'
42
+ if (expanded) {
43
+ classes += ' is-expanded'
44
+ }
45
+ var el = '<div class="tag-subtree' + classes + '" data-tag="' + escape_html(tag.id) + '">'
46
+ el += layoutTag(tag, level)
47
+ if (!isLeaf(tag)) {
48
+ for (var child of tag.children) {
49
+ el += tagAndSub(tagTree.get(child), level + 1)
50
+ }
51
+ }
52
+ el += '</div>'
53
+ return el
54
+ }
33
55
 
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
- <% } %>
56
+ function layoutDiv() {
57
+ if (!tagTree) {
58
+ return ''
59
+ }
60
+ var body = '<div class="ui-collection tag-tree" data-layout="list" data-variant="nav" data-density="compact">'
61
+ body += tagAndSub(tagTree.get(''), 0)
62
+ for (var child of tagTree.get('').children) {
63
+ body += tagAndSub(tagTree.get(child), 0)
64
+ }
65
+ body += '</div>'
66
+ return partial('_partial/components/widget-frame', {
67
+ className: 'tag-tree-widget',
68
+ title: __('meta.tag_tree'),
69
+ body: body,
70
+ attrs: {'data-notebook': page.notebook}
71
+ })
72
+ }
73
+ %>
46
74
 
47
- <% if (tagTree) { %>
48
- <widget class="widget-wrapper<%= scrollreveal(' ') %> post-list" data-notebook="<%= page.notebook %>">
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
- <% } %>
75
+ <%- layoutDiv() %>
@@ -1,79 +1,54 @@
1
1
  <%
2
-
3
2
  function layoutTocBody() {
4
- if (toc(page.content).length > 0) {
5
- return toc(page.content, {
6
- list_number: item.list_number,
7
- min_depth: item.min_depth,
8
- max_depth: item.max_depth
9
- })
3
+ var tocBody = toc(page.content, {
4
+ list_number: item.list_number,
5
+ min_depth: item.min_depth,
6
+ max_depth: item.max_depth
7
+ })
8
+ if (tocBody.length === 0) {
9
+ return ''
10
10
  }
11
- return ''
11
+ return tocBody.replace('class="toc"', 'class="toc ui-collection-adapter"')
12
12
  }
13
13
 
14
- function layoutTocHeader(title) {
15
- var el = ''
16
- el += `<div class="widget-header dis-select">`
17
- el += `<span class="name">${title || __("meta.toc")}</span>`
18
- el += `<a class="cap-action" onclick="sidebar.toggleTOC()" >${icon('default:rightbar', '', true)}</a>`
19
- el += `</div>`
20
- return el
14
+ function layoutTocAction() {
15
+ return '<a class="cap-action" onclick="sidebar.toggleTOC()">' + icon('default:rightbar', '', true) + '</a>'
21
16
  }
22
17
 
23
- function layoutToc(fallback) {
24
- const tocBody = layoutTocBody()
25
- if (tocBody.length == 0) {
26
- return ''
18
+ function layoutFooter(hasComments) {
19
+ var footer = '<div class="widget-footer">'
20
+ footer += '<a class="top" onclick="util.scrollTop()">'
21
+ footer += icon('default:upup', '', true)
22
+ footer += '<span>' + __('btn.top') + '</span></a>'
23
+ if (hasComments) {
24
+ footer += '<a class="buttom" onclick="util.scrollComment()">'
25
+ footer += icon('default:tocomment', '', true)
26
+ footer += '<span>' + __('btn.comments') + '</span></a>'
27
27
  }
28
- var el = ''
29
- el += `<widget class="widget-wrapper${scrollreveal(' ')} toc" id="data-toc" collapse="${item.collapse}">`
30
- el += layoutTocHeader()
31
- el += `<div class="widget-body">`
32
- el += tocBody
33
- el += `</div>`
34
- el += `</widget>`
35
- return el
28
+ footer += '</div>'
29
+ return footer
36
30
  }
37
31
 
38
- function layoutDiv(fallback) {
39
- const tocBody = layoutTocBody()
40
- const hasToc = tocBody.trim().length > 0
41
- // 远程 md 内容由客户端渲染,构建期正文无标题:
42
- // 预留空 TOC 容器,渲染完成后由 mdrender.js 填充
43
- const hasRemoteMd = has_remote_md(page)
44
- // 页面是否实际渲染评论区,由 _partial/comments/layout.ejs 标记
45
- const hasComments = page.cmt_rendered === true
32
+ function layoutDiv() {
33
+ var tocBody = layoutTocBody()
34
+ var hasToc = tocBody.trim().length > 0
35
+ var hasRemoteMd = has_remote_md(page)
36
+ var hasComments = page.cmt_rendered === true
46
37
  if (!hasToc && !hasRemoteMd && !hasComments) {
47
38
  return ''
48
39
  }
49
- var el = ''
50
- el += `<widget class="widget-wrapper${scrollreveal(' ')} toc" id="data-toc" collapse="${item.collapse}">`
51
- if (hasToc) {
52
- el += layoutTocHeader()
53
- el += `<div class="widget-body">`
54
- el += tocBody
55
- el += `</div>`
56
- } else if (hasRemoteMd) {
57
- el += layoutTocHeader()
58
- el += `<div class="widget-body">`
59
- el += `</div>`
60
- }
61
- el += `<div class="widget-footer">`
62
- el += `<a class="top" onclick="util.scrollTop()">`
63
- el += icon('default:upup', '', true)
64
- el += `<span>${__('btn.top')}</span>`
65
- el += `</a>`
66
- if (hasComments) {
67
- el += `<a class="buttom" onclick="util.scrollComment()">`
68
- el += icon('default:tocomment', '', true)
69
- el += `<span>${__('btn.comments')}</span>`
70
- el += `</a>`
71
- }
72
- el += `</div>`
73
- el += `</widget>`
74
- return el
40
+ return partial('_partial/components/widget-frame', {
41
+ className: 'toc',
42
+ title: hasToc || hasRemoteMd ? __('meta.toc') : '',
43
+ action: hasToc || hasRemoteMd ? layoutTocAction() : '',
44
+ body: hasToc ? tocBody : (hasRemoteMd ? '<nav class="toc ui-collection-adapter"></nav>' : ''),
45
+ footer: layoutFooter(hasComments),
46
+ attrs: {
47
+ id: 'data-toc',
48
+ collapse: item.collapse === 'auto' ? 'auto' : String(item.collapse === true)
49
+ }
50
+ })
75
51
  }
76
-
77
52
  %>
78
53
 
79
54
  <%- layoutDiv() %>
@@ -1,71 +1,58 @@
1
1
  <%
2
- const proj = theme.wiki.tree[page.wiki]
2
+ var proj = theme.wiki.tree[page.wiki]
3
3
 
4
- function layoutTocHeader(title) {
5
- var el = ''
6
- el += `<div class="widget-header dis-select">`
7
- el += `<span class="name">${title || __("meta.toc")}</span>`
8
- el += `</div>`
9
- return el
4
+ function layoutSectionHeader(title) {
5
+ if (!title || title.length === 0) {
6
+ return ''
7
+ }
8
+ return '<div class="widget-header dis-select"><span class="name">' + escape_html(title) + '</span></div>'
10
9
  }
11
10
 
12
11
  function layoutDocTree(pages) {
13
- var el = ''
14
- for (let p of pages) {
15
- if (p.title == null || p.title.length == 0) {
12
+ var entries = []
13
+ for (var docPage of pages) {
14
+ if (docPage.title == null || docPage.title.length === 0 || proj.pages.length <= 1) {
16
15
  continue
17
16
  }
18
- let isActive = ''
19
- if (p.path === page.path) {
20
- isActive += ' active'
21
- }
22
- if (proj.pages.length > 1) {
23
- let href = pretty_url(p.path);
24
- if (p.is_homepage) {
25
- href += '#start'
26
- }
27
- el += `<a class="link${isActive}" href="${href}">`
28
- el += `<span class="toc-text">${p.title}</span>`
29
- if (isActive.length > 0) {
30
- el += '<span class="active-dot"></span>'
31
- }
32
- el += `</a>`
33
- }
17
+ entries.push({
18
+ href: pretty_url(docPage.path),
19
+ title: docPage.title,
20
+ active: docPage.path === page.path,
21
+ gradientRef: 'leftbar-active-icon-grad'
22
+ })
34
23
  }
35
- return el
24
+ return partial('_partial/components/collection', {options: {
25
+ layout: 'list',
26
+ variant: 'nav',
27
+ density: 'compact',
28
+ items: entries
29
+ }})
36
30
  }
37
31
 
38
-
39
- function layoutDiv(fallback) {
40
- if (proj == null) {
41
- return ''
42
- }
43
- if (proj.pages == null || proj.pages.length == 0) {
32
+ function layoutDiv() {
33
+ if (proj == null || proj.pages == null || proj.pages.length === 0 || proj.sections == null || proj.sections.length === 0) {
44
34
  return ''
45
35
  }
46
- if (proj.sections == null || proj.sections.length == 0) {
47
- return ''
48
- }
49
- var el = ''
50
- for (let sec of proj.sections) {
51
- if (sec.pages.length == 0) {
36
+ var body = ''
37
+ for (var section of proj.sections) {
38
+ if (section.pages.length === 0) {
52
39
  continue
53
40
  }
54
- if (sec.title?.length > 0) {
55
- el += layoutTocHeader(sec.title)
56
- }
57
- const docTree = layoutDocTree(sec.pages)
58
- if (docTree.trim().length > 0) {
59
- el += `<div class="widget-body fs14">${docTree}</div>`
41
+ var collection = (layoutDocTree(section.pages) || '').trim()
42
+ if (collection.length > 0) {
43
+ body += '<section class="widget-section">'
44
+ body += layoutSectionHeader(section.title)
45
+ body += collection
46
+ body += '</section>'
60
47
  }
61
48
  }
62
- if (el.trim().length > 0) {
63
- return `<widget class="widget-wrapper doc-tree post-list" data-wiki="${page.wiki}">${el}</widget>`
64
- } else {
65
- return ''
66
- }
49
+ return partial('_partial/components/widget-frame', {
50
+ className: 'doc-tree-widget',
51
+ body: body,
52
+ attrs: {'data-wiki': page.wiki},
53
+ reveal: false
54
+ })
67
55
  }
68
-
69
56
  %>
70
57
 
71
58
  <%- layoutDiv() %>
@@ -1,3 +1,28 @@
1
+ <script>
2
+ // 防御:bootstrap 被第三方优化器改写/移除导致 window.stellar.initPlugin 缺失时,
3
+ // 在此补一个等价注册点:utils 就绪时直接委托,未就绪时入队,utils 加载后自动补跑。
4
+ if (typeof window.stellar === 'undefined' || typeof window.stellar.initPlugin !== 'function') {
5
+ window.stellar = window.stellar || {};
6
+ window.stellar._pluginQueue = window.stellar._pluginQueue || [];
7
+ window.stellar.initPlugin = function (fn, name, options) {
8
+ if (typeof utils === 'object' && typeof utils.initPlugin === 'function') {
9
+ return utils.initPlugin(fn, name, options);
10
+ }
11
+ window.stellar._pluginQueue.push({ fn: fn, name: name, options: options || {} });
12
+ };
13
+ window.stellar._flushPlugins = function () {
14
+ var pending = window.stellar._pluginQueue.splice(0);
15
+ if (typeof utils !== 'object' || typeof utils.initPlugin !== 'function') return;
16
+ for (var i = 0; i < pending.length; i++) {
17
+ try {
18
+ utils.initPlugin(pending[i].fn, pending[i].name, pending[i].options);
19
+ } catch (e) {
20
+ console.error('[Stellar] 插件补注册失败:', e);
21
+ }
22
+ }
23
+ };
24
+ }
25
+ </script>
1
26
  <%
2
27
  function loadPlugins() {
3
28
  var el = ''
@@ -3,7 +3,9 @@
3
3
  stellar.initPlugin(() => {
4
4
  const els = document.querySelectorAll('.mermaid');
5
5
  if (els.length > 0) {
6
- utils.css(`<%- url_for('/css/plugins/mermaid.css') %>?v=<%- stellar_info('version') %>`);
6
+ if (<%- conf.style_optimization === true %>) {
7
+ utils.css(`<%- url_for('/css/plugins/mermaid.css') %>?v=<%- stellar_info('version') %>`);
8
+ }
7
9
  const mermaid_config = {
8
10
  startOnLoad: false,
9
11
  theme:
@@ -12,9 +14,6 @@
12
14
  ? "dark"
13
15
  : "<%- conf.theme %>",
14
16
  logLevel: 3,
15
- themeVariables: {
16
- darkMode: true
17
- },
18
17
  flowchart: {
19
18
  useMaxWidth: false,
20
19
  htmlLabels: true,