hexo-theme-stellar 1.42.1 → 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 (117) hide show
  1. package/.claude/skills/stellar-theme-dev/SKILL.md +1 -0
  2. package/CHANGELOG.md +31 -0
  3. package/THIRD-PARTY-NOTICES.md +28 -0
  4. package/_config.yml +17 -7
  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/defines.ejs +2 -8
  26. package/layout/_partial/scripts/lazyload.ejs +0 -3
  27. package/layout/_partial/scripts.ejs +1 -0
  28. package/layout/_partial/sidebar/index_leftbar.ejs +12 -2
  29. package/layout/_partial/sidebar/logo.ejs +1 -1
  30. package/layout/_partial/sidebar/menu.ejs +21 -23
  31. package/layout/_partial/widgets/components/linklist.ejs +23 -21
  32. package/layout/_partial/widgets/linklist.ejs +7 -12
  33. package/layout/_partial/widgets/recent.ejs +52 -41
  34. package/layout/_partial/widgets/related.ejs +47 -36
  35. package/layout/_partial/widgets/tagtree.ejs +70 -54
  36. package/layout/_partial/widgets/toc.ejs +37 -62
  37. package/layout/_partial/widgets/tree.ejs +38 -51
  38. package/layout/_plugins/mermaid.ejs +3 -4
  39. package/layout/archive.ejs +11 -20
  40. package/layout/collection-preview.ejs +60 -0
  41. package/layout/index_topic.ejs +12 -6
  42. package/layout/index_wiki.ejs +49 -3
  43. package/layout/layout.ejs +4 -3
  44. package/package.json +1 -1
  45. package/scripts/events/index.js +0 -2
  46. package/scripts/generators/stellar-icons.js +1 -1
  47. package/scripts/lib/subtitle.js +5 -1
  48. package/scripts/tags/index.js +1 -0
  49. package/scripts/tags/lib/about.js +1 -1
  50. package/scripts/tags/lib/albums.js +1 -1
  51. package/scripts/tags/lib/banner.js +1 -1
  52. package/scripts/tags/lib/dropdown.js +64 -0
  53. package/scripts/tags/lib/friends.js +1 -1
  54. package/scripts/tags/lib/gallery.js +3 -3
  55. package/scripts/tags/lib/image.js +1 -1
  56. package/scripts/tags/lib/posters.js +1 -1
  57. package/scripts/tags/lib/sites.js +8 -4
  58. package/source/css/_common/base.styl +4 -4
  59. package/source/css/_common/blockquote.styl +2 -2
  60. package/source/css/_common/dropdown.styl +132 -0
  61. package/source/css/_common/html.styl +1 -1
  62. package/source/css/_common/loading.styl +5 -2
  63. package/source/css/_common/title.styl +8 -8
  64. package/source/css/_components/collection.styl +188 -0
  65. package/source/css/_components/layout.styl +1 -1
  66. package/source/css/_components/list.styl +44 -46
  67. package/source/css/_components/md.styl +3 -3
  68. package/source/css/_components/page-transition.styl +22 -0
  69. package/source/css/_components/pages/archives.styl +19 -18
  70. package/source/css/_components/pages/article-indent.styl +2 -2
  71. package/source/css/_components/pages/article-story.styl +9 -5
  72. package/source/css/_components/pages/collection-preview.styl +68 -0
  73. package/source/css/_components/pages/notebook.styl +11 -9
  74. package/source/css/_components/partial/article-footer.styl +7 -4
  75. package/source/css/_components/partial/cover.styl +280 -1
  76. package/source/css/_components/partial/related.styl +10 -6
  77. package/source/css/_components/sidebar/footer.styl +65 -12
  78. package/source/css/_components/sidebar/nav-area.styl +6 -1
  79. package/source/css/_components/sidebar/search.styl +6 -2
  80. package/source/css/_components/tag-plugins/chat.styl +1 -1
  81. package/source/css/_components/tag-plugins/folders.styl +4 -4
  82. package/source/css/_components/tag-plugins/folding.styl +3 -3
  83. package/source/css/_components/tag-plugins/hashtag.styl +2 -3
  84. package/source/css/_components/tag-plugins/mdrender.styl +1 -1
  85. package/source/css/_components/tag-plugins/note.styl +4 -4
  86. package/source/css/_components/tag-plugins/poetry.styl +5 -5
  87. package/source/css/_components/tag-plugins/quot.styl +1 -2
  88. package/source/css/_components/tag-plugins/quote.styl +1 -1
  89. package/source/css/_components/tag-plugins/tabs.styl +2 -2
  90. package/source/css/_components/tag-plugins/timeline.styl +4 -4
  91. package/source/css/_components/widgets/markdown.styl +1 -14
  92. package/source/css/_components/widgets/timeline.styl +2 -2
  93. package/source/css/_components/widgets/toc.styl +22 -20
  94. package/source/css/_components/widgets/widgets.styl +13 -1
  95. package/source/css/_components/wiki-card.styl +209 -0
  96. package/source/css/_custom.styl +22 -20
  97. package/source/css/_plugins/lazyload.styl +8 -5
  98. package/source/css/comments/utterances.styl +2 -2
  99. package/source/css/plugins/mermaid.styl +31 -12
  100. package/source/js/icons.js +1 -1
  101. package/source/js/main.js +62 -6
  102. package/source/js/plugins/adaptive-text.js +46 -2
  103. package/source/js/plugins/dropdown.js +435 -0
  104. package/source/js/plugins/galaxy.js +423 -0
  105. package/source/js/search/algolia-search.js +1 -1
  106. package/source/js/search/local-search.js +1 -1
  107. package/source/js/services/ghinfo.js +31 -3
  108. package/source/js/services/siteinfo.js +26 -0
  109. package/source/js/services/sites.js +6 -2
  110. package/source/js/services.js +5 -2
  111. package/source/js/tagtree.js +7 -7
  112. package/source/js/utils.js +1 -1
  113. package/layout/_partial/widgets/components/link.ejs +0 -21
  114. package/source/css/_components/sidebar/menu.styl +0 -77
  115. package/source/css/_components/widgets/components.styl +0 -68
  116. package/source/css/_components/widgets/list.styl +0 -46
  117. package/source/css/_components/widgets/related.styl +0 -40
@@ -0,0 +1,62 @@
1
+ <%
2
+ const dropdownItem = typeof item !== 'undefined' && item ? item : {}
3
+ const items = Array.isArray(dropdownItem.items) ? dropdownItem.items.filter(child => child && child.title && child.url) : []
4
+ const classes = ['dropdown']
5
+ const dropdownRootClass = typeof rootClass === 'string' ? rootClass : ''
6
+ if (dropdownRootClass) {
7
+ classes.push(dropdownRootClass)
8
+ }
9
+ const triggerClasses = ['dropdown-trigger']
10
+ const dropdownTriggerClass = typeof triggerClass === 'string' ? triggerClass : ''
11
+ if (dropdownTriggerClass) {
12
+ triggerClasses.push(dropdownTriggerClass)
13
+ }
14
+ const dropdownDirection = typeof direction === 'string' ? direction : ''
15
+ const dropdownShowTitle = typeof showTitle === 'undefined' || showTitle !== false
16
+
17
+ function linkAttrs(child) {
18
+ const url = String(child.url)
19
+ var attrs = ' href="' + escape_html(url_for(url)) + '"'
20
+ if (url.includes('://')) {
21
+ attrs += ' target="_blank" rel="external nofollow noopener noreferrer"'
22
+ } else {
23
+ attrs += ' rel="noopener noreferrer"'
24
+ }
25
+ return attrs
26
+ }
27
+
28
+ function layoutDiv() {
29
+ if (!dropdownItem.icon || items.length === 0) {
30
+ return ''
31
+ }
32
+ const title = dropdownItem.title ? escape_html(dropdownItem.title) : ''
33
+ var el = '<details class="' + classes.join(' ') + '"'
34
+ if (dropdownDirection) {
35
+ el += ' direction="' + escape_html(dropdownDirection) + '"'
36
+ }
37
+ el += '>'
38
+ el += '<summary class="' + triggerClasses.join(' ') + '"'
39
+ if (title) {
40
+ el += ' title="' + title + '" aria-label="' + title + '"'
41
+ }
42
+ el += '>'
43
+ // dropdown 首次展开时需要立即显示图标,使用内联 SVG 避免 /js/icons.js 异步替换造成闪烁。
44
+ el += icon(dropdownItem.icon, 'no-lazy', true)
45
+ if (dropdownShowTitle && dropdownItem.title) {
46
+ el += '<span>' + title + '</span>'
47
+ }
48
+ el += '</summary>'
49
+ el += '<div class="dropdown-menu ui-collection" data-ui-surface="glass" data-layout="list" data-variant="nav" data-density="compact">'
50
+ for (let child of items) {
51
+ el += '<a class="dropdown-item ui-collection__item"' + linkAttrs(child) + '>'
52
+ if (child.icon) {
53
+ el += '<span class="ui-collection__leading">' + icon(child.icon, 'no-lazy', true) + '</span>'
54
+ }
55
+ el += '<span class="ui-collection__content"><span class="ui-collection__title">' + escape_html(child.title) + '</span></span>'
56
+ el += '</a>'
57
+ }
58
+ el += '</div></details>'
59
+ return el
60
+ }
61
+ %>
62
+ <%- layoutDiv() %>
@@ -237,6 +237,15 @@ function generate_canonical() {
237
237
  const raw = theme.icons && theme.icons[key];
238
238
  if (raw) vars.push(` ${name}: url("data:image/svg+xml,${encodeURIComponent(raw)}");`);
239
239
  }
240
+ // 加载占位:由 icons.yml 的 default:loading 提供(内联 SVG 编码为 data URI,URL 直用)
241
+ const loadingRaw = theme.icons && theme.icons['default:loading'];
242
+ if (loadingRaw) {
243
+ if (loadingRaw.trim().startsWith('<svg')) {
244
+ vars.push(` --icon-loading: url("data:image/svg+xml,${encodeURIComponent(loadingRaw)}");`);
245
+ } else {
246
+ vars.push(` --icon-loading: url("${loadingRaw}");`);
247
+ }
248
+ }
240
249
  return vars.length > 0 ? `:root {\n${vars.join('\n')}\n}` : '';
241
250
  })();
242
251
  %>
@@ -31,4 +31,4 @@ function layoutDiv() {
31
31
  return el
32
32
  }
33
33
  %>
34
- <%- layoutDiv() %>
34
+ <%- layoutDiv() %>
@@ -0,0 +1,12 @@
1
+ <%
2
+ var item = post || {};
3
+ var href = pretty_url(item.link || item.path || '/');
4
+ var itemDate = item.date ? date(item.date, 'MM-DD') : '';
5
+ var itemTitle = item.title || (item.date ? date(item.date, config.date_format) : '');
6
+ %>
7
+ <div class="archive-list">
8
+ <a class="post fs14" href="<%- escape_html(href) %>">
9
+ <% if (itemDate) { %><time><%- escape_html(itemDate) %></time><% } %>
10
+ <span><%- escape_html(itemTitle) %></span>
11
+ </a>
12
+ </div>
@@ -17,17 +17,29 @@ function layoutDiv() {
17
17
  background: bg,
18
18
  post: latest
19
19
  });
20
- // 其他文章(排除最新,最多 3 条,按发布时间倒序)
20
+ // 其他文章(排除最新,默认展示 3 条,剩余文章可展开)
21
21
  if (topic.pages && topic.pages.length > 1) {
22
- el += '<div class="post-panel">';
23
- el += '<div class="posts">';
24
- topic.pages.slice(1, 4).forEach(function(post) {
25
- el += '<a class="post-link" href="' + pretty_url(post.path) + '">';
26
- el += '<span class="title">' + escape_html(post.title) + '</span>';
27
- el += '<span class="date">' + escape_html(date(post.date, config.date_format)) + '</span>';
28
- el += '</a>';
22
+ var otherPosts = topic.pages.slice(1);
23
+ var visiblePosts = otherPosts.slice(0, 3);
24
+ var hiddenPosts = otherPosts.slice(3);
25
+ el += '<div class="topic-posts">';
26
+ visiblePosts.forEach(function(post) {
27
+ el += partial('_partial/main/post_list/archive_item', {post: post});
29
28
  });
30
- el += '</div>';
29
+ if (hiddenPosts.length > 0) {
30
+ el += '<details class="topic-posts-more">';
31
+ el += '<summary class="topic-posts-toggle">';
32
+ el += '<span class="when-closed">' + escape_html(__('btn.expand_posts', hiddenPosts.length)) + '</span>';
33
+ el += '<span class="when-open">' + escape_html(__('btn.collapse_posts')) + '</span>';
34
+ el += icon('default:arrow-right', '', true);
35
+ el += '</summary>';
36
+ el += '<div class="topic-posts-extra">';
37
+ hiddenPosts.forEach(function(post) {
38
+ el += partial('_partial/main/post_list/archive_item', {post: post});
39
+ });
40
+ el += '</div>';
41
+ el += '</details>';
42
+ }
31
43
  el += '</div>';
32
44
  }
33
45
  el += '</article>';
@@ -1,21 +1,60 @@
1
1
  <%
2
2
  function layoutDiv() {
3
3
  var el = '';
4
- el += '<article class="md-text">';
5
- el += `<div class="preview"><img src="${proj.icon ? proj.icon : theme.default.project}"/></div>`
6
- el += '<div class="excerpt">';
4
+ var cover = proj.cover;
5
+ var projectIcon = proj.icon;
6
+ var caption = subtitle(proj);
7
+ var headline = proj.headline || proj.title || proj.name || '';
8
+ el += '<article class="wiki-card-content">';
9
+ el += '<div class="wiki-card-cover' + (cover ? '' : ' no-cover') + '" position="bottom"';
10
+ if (cover) {
11
+ el += ' style="--cover-url:url(\'' + escape_html(cover.replace(/'/g, '%27')) + '\')">';
12
+ el += '<img src="' + escape_html(cover) + '" alt=""/>';
13
+ } else {
14
+ el += '>';
15
+ }
16
+ el += '<div class="wiki-card-info" position="bottom"';
17
+ if (cover) {
18
+ el += ' data-text-adaptive="split"';
19
+ }
20
+ el += '>';
21
+ el += '<div class="wiki-card-summary">';
7
22
  if (proj.tags && proj.tags.length > 0) {
8
- el += '<div class="caps">';
9
- for (let tag of proj.tags) {
10
- el += '<span class="cap breadcrumb"' + category_color(tag) + '>' + tag + '</span>';
11
- }
23
+ el += '<div class="wiki-tags wiki-meta">';
24
+ for (var i = 0; i < proj.tags.length; i++) {
25
+ var tag = proj.tags[i];
26
+ el += '<span>' + escape_html(tag) + '</span>';
27
+ }
28
+ el += '</div>';
29
+ }
30
+ el += '<h2 class="headline">' + escape_html(headline) + '</h2>';
31
+ if (proj.available || proj.repo) {
32
+ el += '<div class="wiki-meta">';
33
+ if (proj.available) {
34
+ el += '<span class="wiki-available">' + icon('default:platforms') + '<span class="label">' + escape_html(__('meta.available')) + '</span><span class="value">' + escape_html(proj.available) + '</span></span>';
35
+ }
36
+ if (proj.repo) {
37
+ el += '<span class="wiki-stars data-service ds-ghinfo" data-api="https://' + theme.api_host.ghapi + '/repos/' + escape_html(proj.repo) + '">';
38
+ el += icon('default:fire') + '<span type="text" id="stargazers_count"></span>';
39
+ el += '</span>';
40
+ }
12
41
  el += '</div>';
13
42
  }
14
- el += '<h2 class="post-title">' + (proj.title || proj.name) + '</h2>';
15
- if (proj.description) {
16
- el += '<p>' + proj.description + '</p>';
43
+ el += '</div>';
44
+ el += '<div class="wiki-project">';
45
+ el += '<div class="project-icon">';
46
+ if (projectIcon) {
47
+ el += '<img src="' + escape_html(projectIcon) + '" alt=""/>';
48
+ } else {
49
+ el += icon('default:documents');
17
50
  }
18
51
  el += '</div>';
52
+ el += '<div class="project-text"><div class="project-name">' + escape_html(proj.name || proj.title || '') + '</div>';
53
+ if (caption.length > 0) {
54
+ el += '<div class="project-subtitle">' + escape_html(caption) + '</div>';
55
+ }
56
+ el += '</div></div>';
57
+ el += '</div></div>';
19
58
  el += '</article>';
20
59
  return el;
21
60
  }
@@ -17,8 +17,6 @@
17
17
  }
18
18
  (function() {
19
19
  const scrollHandler = () => {
20
- const wikiCover = document.querySelector('#l_cover .l_cover.wiki');
21
- const startEl = document.getElementById('start');
22
20
  // 存在有效锚点时,等待布局稳定后滚动到目标
23
21
  // (浏览器原生锚点滚动发生在布局完成前,长文章的懒加载图片/异步组件会把目标顶偏)
24
22
  const hash = window.location.hash;
@@ -137,11 +135,8 @@
137
135
  return;
138
136
  }
139
137
  }
140
- if (wikiCover && startEl) {
141
- window.scrollTo(0, startEl.offsetTop);
142
- } else {
143
- window.scrollTo(0, 0);
144
- }
138
+ // 没有锚点时始终保持页面顶部;Wiki Hero 只在用户显式访问 #start 时定位正文。
139
+ window.scrollTo(0, 0);
145
140
  };
146
141
  if (document.readyState !== 'loading') scrollHandler();
147
142
  else document.addEventListener('DOMContentLoaded', scrollHandler);
@@ -174,7 +169,6 @@
174
169
  const def = {
175
170
  avatar: `<%- theme.default.avatar %>`,
176
171
  cover: `<%- theme.default.cover %>`,
177
- loading: `<%- theme.default.loading || (theme.icons && theme.icons['default:loading-placeholder']) %>`,
178
172
  data_cache: <%- JSON.stringify(theme.data_cache || {}) %>,
179
173
  };
180
174
  const deps = {
@@ -73,9 +73,6 @@
73
73
 
74
74
  const icon = document.createElement('div');
75
75
  icon.className = 'lazy-icon';
76
- if (def.loading) {
77
- icon.style.backgroundImage = `url("${def.loading}")`;
78
- }
79
76
 
80
77
  wrapper.appendChild(newImg);
81
78
  wrapper.appendChild(icon);
@@ -48,6 +48,7 @@ function tagtreeWidgetOnPage() {
48
48
  <script defer src="<%- url_for('/js/stellar-icons.js') %>?v=<%- stellar_info('version') %>"></script>
49
49
  <!-- 非首屏图标异步加载:占位符替换依赖构建期生成的 js/icons/{ns}.json -->
50
50
  <script defer src="<%- url_for('/js/icons.js') %>?v=<%- stellar_info('version') %>" data-version="<%- stellar_info('version') %>"></script>
51
+ <script defer src="<%- url_for('/js/plugins/dropdown.js') %>?v=<%- stellar_info('version') %>"></script>
51
52
  <script src="<%- url_for(`${theme.stellar.main_js}?v=${stellar_info('version')}`) %>" defer></script>
52
53
  <script defer src="<%- url_for('/js/theme.js') %>?v=<%- stellar_info('version') %>"></script>
53
54
 
@@ -125,10 +125,20 @@ function layoutFooterDiv() {
125
125
  return ''
126
126
  }
127
127
  var el = '<footer class="footer dis-select">';
128
- el += '<div class="social-wrap">';
128
+ el += '<div class="social-wrap" style="--item-grad:url(#footer-social-icon-grad)">';
129
+ el += partial('grad-def', {id: 'footer-social-icon-grad'})
129
130
  for (let id of Object.keys(social)) {
130
131
  let item = social[id];
131
- if (item.icon && (item.url || item.onclick)) {
132
+ if (id === 'spacer') {
133
+ el += '<span class="social-spacer" aria-hidden="true"></span>';
134
+ } else if (item?.type === 'dropdown') {
135
+ el += partial('../dropdown', {
136
+ item: item,
137
+ rootClass: 'social-dropdown',
138
+ triggerClass: 'social',
139
+ showTitle: false
140
+ })
141
+ } else if (item?.icon && (item.url || item.onclick)) {
132
142
  el += '<a class="social"';
133
143
  if (item.title) {
134
144
  el += ' title="' + escape_html(item.title) + '"';
@@ -50,7 +50,7 @@ function layoutBack() {
50
50
  var el = ''
51
51
  el += `<div class="wiki-home-wrap">`
52
52
  el += `<a class="wiki-home cap" href="${pretty_url(theme.site_tree.index_wiki.base_dir)}">`
53
- el += icon('default:goback', '', true)
53
+ el += icon('default:arrow-left', '', true)
54
54
  el += `<span>${__('btn.all_wiki')}</span>`
55
55
  el += `</a>`
56
56
  el += `</div>`
@@ -1,31 +1,29 @@
1
1
  <%
2
2
  function layoutDiv() {
3
- var el = ''
4
- el += `<nav class="menu dis-select${where == 'main' ? ' mobile-hidden' : ''}">`
5
- for (let item of (theme.menubar.items || [])) {
6
- if (item == undefined || item.id == null || item.url == null) {
3
+ var entries = []
4
+ for (var menuItem of (theme.menubar.items || [])) {
5
+ if (menuItem == null || menuItem.id == null || menuItem.url == null || menuItem.title == null) {
7
6
  continue
8
7
  }
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})`
12
- if (item.theme?.length > 0) {
13
- style += `;--item-theme:${item.theme}`
14
- }
15
- el += ` style="${style}"`
16
- el += ` title="${item.title}" href="${pretty_url(item.url)}">`
17
- if (item.icon?.length > 0) {
18
- el += icon(item.icon, 'no-lazy', true)
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})
25
- el += `</a>`
8
+ entries.push({
9
+ href: pretty_url(menuItem.url),
10
+ title: __(menuItem.title),
11
+ icon: menuItem.icon,
12
+ theme: menuItem.theme,
13
+ active: menuItem.id == page.menu_id,
14
+ className: menuItem.theme?.length > 0 ? 'is-themed' : '',
15
+ gradientId: 'menu-grad-' + menuItem.id
16
+ })
17
+ }
18
+ var body = partial('_partial/components/collection', {options: {
19
+ layout: 'list',
20
+ variant: 'nav',
21
+ items: entries
22
+ }})
23
+ if ((body || '').trim().length === 0) {
24
+ return ''
26
25
  }
27
- el += `</nav>`
28
- return el
26
+ return `<nav class="menu dis-select${where == 'main' ? ' mobile-hidden' : ''}">${body}</nav>`
29
27
  }
30
28
  %>
31
29
 
@@ -1,27 +1,29 @@
1
1
  <%
2
2
  function layoutDiv(linklist) {
3
- var links = linklist.items || []
4
- if (links.length == 0) {
5
- return ''
3
+ var links = Array.isArray(linklist.items) ? linklist.items : []
4
+ var view = linklist.view === 'grid' ? 'grid' : 'list'
5
+ var columns = view === 'grid' ? (linklist.columns || 2) : 1
6
+ var showTitle = linklist.show_title !== false
7
+ var entries = []
8
+ for (var link of links) {
9
+ if (!link || !link.url || !link.title) {
10
+ continue
11
+ }
12
+ entries.push({
13
+ href: link.url,
14
+ title: link.title,
15
+ icon: link.icon,
16
+ active: pretty_url(link.url) == pretty_url(page.path),
17
+ showTitle: showTitle,
18
+ gradientRef: 'leftbar-active-icon-grad'
19
+ })
6
20
  }
7
- var el = ''
8
- var style = ''
9
- var columns = linklist.columns || links.length
10
- if (links.length == 1) {
11
- style += `style="grid-template-columns:repeat(1,1fr);"`
12
- } else {
13
- style += `style="grid-template-columns:repeat(${columns},1fr);"`
14
- }
15
- var align = 'center'
16
- if (columns == 1 && links.length > 1) {
17
- align = 'left'
18
- }
19
- el += `<div class="linklist ${align}${columns > 1 ? ' multi' : ''}" ${style}>`
20
- for (let link of links) {
21
- el += partial('link', {item: link, columns: columns})
22
- }
23
- el += `</div>`
24
- return el
21
+ return partial('_partial/components/collection', {options: {
22
+ layout: view,
23
+ variant: showTitle ? 'nav' : 'icon',
24
+ columns: columns,
25
+ items: entries
26
+ }})
25
27
  }
26
28
  %>
27
29
  <%- layoutDiv(item) %>
@@ -1,17 +1,12 @@
1
1
  <%
2
2
  function layoutDiv() {
3
- var el = '';
4
- el += `<widget class="widget-wrapper linklist">`
5
- if (item.title?.length > 0) {
6
- el += '<div class="widget-header dis-select">';
7
- el += '<span class="name">' + item.title + '</span>';
8
- el += '</div>';
9
- }
10
- el += '<div class="widget-body fs14">';
11
- el += partial('components/linklist', {item: item})
12
- el += '</div>';
13
- el += '</widget>';
14
- return el;
3
+ var body = partial('components/linklist', {item: item})
4
+ return partial('_partial/components/widget-frame', {
5
+ className: 'link-list-widget',
6
+ title: item.title,
7
+ body: body,
8
+ reveal: false
9
+ })
15
10
  }
16
11
  %>
17
12
  <%- layoutDiv() %>
@@ -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() %>