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
@@ -13,14 +13,22 @@ function layout_wiki_list(partial) {
13
13
  if (page.filter === false) {
14
14
  // wikiList
15
15
  el += `<div class="post-card-wrap${scrollreveal(' ')}">`
16
- el += `<a class="post-card wiki" href="${pretty_url(proj.homepage?.path || '/')}">`
16
+ var coverStyle = ''
17
+ if (proj.cover) {
18
+ coverStyle = ` style="--cover-url:url('${escape_html(proj.cover.replace(/'/g, '%27'))}')"`
19
+ }
20
+ el += `<a class="wiki-card${proj.cover ? '' : ' no-cover'}"${coverStyle} href="${pretty_url(proj.homepage?.path || '/')}">`
17
21
  el += partial(proj)
18
22
  el += `</a>`
19
23
  el += `</div>`
20
24
  } else if (proj.tags && proj.tags.includes(page.tagName) === true) {
21
25
  // filtered wikiList
22
26
  el += `<div class="post-card-wrap${scrollreveal(' ')} filter">`
23
- el += `<a class="post-card wiki" href="${pretty_url(proj.homepage?.path || '/')}">`
27
+ var coverStyle = ''
28
+ if (proj.cover) {
29
+ coverStyle = ` style="--cover-url:url('${escape_html(proj.cover.replace(/'/g, '%27'))}')"`
30
+ }
31
+ el += `<a class="wiki-card${proj.cover ? '' : ' no-cover'}"${coverStyle} href="${pretty_url(proj.homepage?.path || '/')}">`
24
32
  el += partial(proj)
25
33
  el += `</a>`
26
34
  el += `</div>`
@@ -41,4 +49,42 @@ function layoutDiv() {
41
49
  }
42
50
  %>
43
51
 
44
- <%- layoutDiv() %>
52
+ <%- layoutDiv() %>
53
+ <script>
54
+ // Wiki 覆盖层须同时等待封面原图和平均主题色完成,避免默认主题色短暂闪现。
55
+ function revealWikiCover(card) {
56
+ if (card._wikiImageReady && card._wikiOverlayReady && !card.classList.contains('no-cover')) {
57
+ card.classList.add('cover-loaded');
58
+ }
59
+ }
60
+ document.addEventListener('wiki-overlay-ready', function(event) {
61
+ var card = event.target.closest('.wiki-card');
62
+ if (!card) return;
63
+ card._wikiOverlayReady = true;
64
+ revealWikiCover(card);
65
+ });
66
+ document.querySelectorAll('.wiki-card-cover:not(.no-cover) > img').forEach(function(img) {
67
+ var cover = img.parentElement;
68
+ var card = img.closest('.wiki-card');
69
+ if (!cover || !card) return;
70
+ var loaded = function() {
71
+ card._wikiImageReady = true;
72
+ revealWikiCover(card);
73
+ };
74
+ var failed = function() {
75
+ card._wikiImageReady = false;
76
+ card.classList.remove('cover-loaded');
77
+ card.classList.add('no-cover');
78
+ cover.classList.add('no-cover', 'cover-error');
79
+ };
80
+ img.addEventListener('load', loaded, { once: true });
81
+ img.addEventListener('error', failed, { once: true });
82
+ if (img.complete) {
83
+ if (img.naturalWidth > 0) {
84
+ loaded();
85
+ } else {
86
+ failed();
87
+ }
88
+ }
89
+ });
90
+ </script>
package/layout/layout.ejs CHANGED
@@ -49,19 +49,20 @@ 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${theme.style.leftbar?.['ui-style'] === 'card' ? ' leftbar-card' : ''}">`
52
+ var leftbarSurface = theme.style.leftbar?.['ui-style'] === 'card' ? 'card' : 'glass'
53
+ html += `<aside class="l_left${leftbarSurface === 'card' ? ' leftbar-card' : ''}" data-ui-surface="${leftbarSurface}">`
53
54
  html += `<div class="sidebg"></div>`
54
55
  html += `<div class="leftbar-container${theme.style.leftbar?.blur ? ' leftbar-blur' : ''}">`
55
56
  html += partial('_partial/sidebar/index_leftbar')
56
57
  html += `</div>`
57
58
  html += `</aside>`
58
- html += `<div class="l_main" id="main">`
59
+ html += `<div class="l_main" id="main" data-ui-surface="content">`
59
60
  html += partial('_partial/sidebar/logo', {where: 'main'})
60
61
  html += body
61
62
  html += partial('_partial/main/footer')
62
63
  html += `<div class="main-mask" onclick="sidebar.dismiss()"></div>`
63
64
  html += `</div>`
64
- html += `<aside class="l_right">`
65
+ html += `<aside class="l_right" data-ui-surface="sidebar">`
65
66
  html += partial('_partial/sidebar/index_rightbar').trim()
66
67
  html += `</aside>`
67
68
  html += partial('_partial/menubtn')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hexo-theme-stellar",
3
- "version": "1.42.1",
3
+ "version": "1.43.0",
4
4
  "description": "Elegant and powerful theme for Hexo.",
5
5
  "main": "package.json",
6
6
  "scripts": {
@@ -41,7 +41,6 @@ hexo.on('ready', () => {
41
41
  checkVersion(hexo, { useCache: true });
42
42
  });
43
43
 
44
-
45
44
  // 防止重复注册
46
45
  let hasRun = false;
47
46
 
@@ -66,4 +65,3 @@ hexo.extend.filter.register('before_generate', async () => {
66
65
  fixMarkdownImages(hexo); // 不用 await,因为是同步的
67
66
  }
68
67
  });
69
-
@@ -5,7 +5,7 @@
5
5
  // 与 layout/_partial/scripts/defines.ejs 中旧的白名单保持一致(客户端通过 ctx.icons 读取)。
6
6
  hexo.extend.generator.register('stellar_icons', function () {
7
7
  const icons = (this.theme.config && this.theme.config.icons) || {};
8
- const keys = ['default:tocomment', 'default:loading-spinner', 'default:warning', 'weibo:repeat', 'weibo:like'];
8
+ const keys = ['default:tocomment', 'default:warning', 'weibo:repeat', 'weibo:like'];
9
9
  const out = {};
10
10
  // 去除 SVG 注释(避免 <!-- / </script> 解析风险),再转义 <
11
11
  for (const k of keys) {
@@ -3,12 +3,16 @@
3
3
  const { stripHTML } = require('hexo-util');
4
4
 
5
5
  // 文章一行小字取值(hero 卡片与置顶轮播共用,未来可复用于其它场景):
6
- // subtitle > description > excerpt/content 前 length 字(去 HTML、压缩空白、截断;省略号由 CSS 单行处理),空则返回空串。
6
+ // subtitle(含 " | " 时取非空左侧)> description > excerpt/content 前 length 字(去 HTML、压缩空白、截断;省略号由 CSS 单行处理),空则返回空串。
7
7
  function subtitle(post, length = 50) {
8
8
  if (post == null) {
9
9
  return '';
10
10
  }
11
11
  if (post.subtitle != null && String(post.subtitle).length > 0) {
12
+ const separator = String(post.subtitle).indexOf(' | ');
13
+ if (separator > 0) {
14
+ return String(post.subtitle).slice(0, separator);
15
+ }
12
16
  return post.subtitle;
13
17
  }
14
18
  if (post.description != null && String(post.description).length > 0) {
@@ -12,6 +12,7 @@ hexo.extend.tag.register('grid', require('./lib/grid')(hexo), true)
12
12
  hexo.extend.tag.register('swiper', require('./lib/swiper')(hexo), true)
13
13
  hexo.extend.tag.register('gallery', require('./lib/gallery')(hexo), {ends: true})
14
14
  hexo.extend.tag.register('banner', require('./lib/banner')(hexo), true)
15
+ hexo.extend.tag.register('dropdown', require('./lib/dropdown')(hexo), true)
15
16
 
16
17
  // data
17
18
  hexo.extend.tag.register('users', require('./lib/friends')(hexo))
@@ -18,7 +18,7 @@ module.exports = ctx => function(args, content) {
18
18
  el += '<div class="tag-plugin about">'
19
19
  if (args.back) {
20
20
  el += '<a class="nav-back cap" href="' + url_for(ctx.config.root) + '">'
21
- el += ctx.utils.icon('default:goback')
21
+ el += ctx.utils.icon('default:arrow-left')
22
22
  el += '</a>'
23
23
  }
24
24
  // avatar
@@ -32,7 +32,7 @@ module.exports = ctx => function(args) {
32
32
  el += `<div class="grid-cell album-card">`
33
33
  el += `<a class="card-link lazy-box" target="_blank" rel="external nofollow noopener noreferrer" href="${item.url}">`
34
34
  el += `<img class="lazy" data-src="${item.cover || item.icon || item.avatar || ctx.theme.config.default.cover}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${ctx.theme.config.default.cover}&quot;;"/>`
35
- el += `<div class="lazy-icon" style="background-image:url(${ctx.theme.config.default.loading || ctx.utils.iconData('default:loading-placeholder')});"></div>`
35
+ el += `<div class="lazy-icon"></div>`
36
36
  el += `<div class="image-meta">`
37
37
  if (item.title) {
38
38
  el += `<span class="image-caption">${item.title}</span>`
@@ -26,7 +26,7 @@ module.exports = ctx => function(args, content) {
26
26
  } else {
27
27
  el += `
28
28
  <button class="back cap" onclick="window.history.back()">
29
- ${ctx.utils.icon('default:goback')}
29
+ ${ctx.utils.icon('default:arrow-left')}
30
30
  </button>
31
31
  `
32
32
  }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * dropdown.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
3
+ *
4
+ * {% dropdown [direction:up/down] [align:left/right] title %}
5
+ * - icon:key [title](url)
6
+ * {% enddropdown %}
7
+ */
8
+
9
+ 'use strict'
10
+
11
+ function parseItem(line) {
12
+ const matches = line.match(/^\s*(?:[-*]\s+)?(?:icon:([^\s]+)\s+)?\[([^\]]+)\]\(([^)\s]+)\)(?:\s+icon:([^\s]+))?\s*$/)
13
+ if (!matches) {
14
+ return null
15
+ }
16
+ return {
17
+ icon: matches[1] || matches[4],
18
+ title: matches[2],
19
+ url: matches[3]
20
+ }
21
+ }
22
+
23
+ module.exports = ctx => function(args, content = '') {
24
+ const { escapeHTML, url_for } = require('hexo-util')
25
+ args = ctx.args.map(args, ['icon', 'direction', 'align', 'open'], ['title'])
26
+ if (!args.title) {
27
+ return ''
28
+ }
29
+
30
+ const items = content.split(/\r?\n/).map(parseItem).filter(Boolean)
31
+ if (items.length === 0) {
32
+ return ''
33
+ }
34
+
35
+ const direction = ['up', 'down'].includes(args.direction) ? args.direction : 'auto'
36
+ const align = ['left', 'right'].includes(args.align) ? args.align : 'auto'
37
+ const title = escapeHTML(args.title)
38
+ const toUrl = url_for.bind(ctx)
39
+ var el = `<details class="tag-plugin dropdown" direction="${direction}" align="${align}"`
40
+ if (args.open === 'true') {
41
+ el += ' open'
42
+ }
43
+ el += '>'
44
+ el += `<summary class="dropdown-trigger" title="${title}" aria-label="${title}">`
45
+ el += `<span>${title}</span>`
46
+ el += '<svg class="dropdown-arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M6 9l6 6 6-6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'
47
+ el += '</summary>'
48
+ el += '<div class="dropdown-menu ui-collection" data-ui-surface="glass" data-layout="list" data-variant="nav" data-density="compact">'
49
+ for (const item of items) {
50
+ const url = String(item.url)
51
+ el += `<a class="dropdown-item ui-collection__item" href="${escapeHTML(toUrl(url))}`
52
+ if (url.includes('://')) {
53
+ el += '" target="_blank" rel="external nofollow noopener noreferrer">'
54
+ } else {
55
+ el += '" rel="noopener noreferrer">'
56
+ }
57
+ if (item.icon) {
58
+ el += `<span class="ui-collection__leading">${ctx.utils.icon(item.icon, 'no-lazy', true)}</span>`
59
+ }
60
+ el += `<span class="ui-collection__content"><span class="ui-collection__title">${escapeHTML(item.title)}</span></span></a>`
61
+ }
62
+ el += '</div></details>'
63
+ return el
64
+ }
@@ -29,7 +29,7 @@ module.exports = ctx => function(args) {
29
29
  el += `<a class="card-link" target="_blank" rel="external nofollow noopener noreferrer" href="${item.url}">`
30
30
  el += `<div class="lazy-box icon">`
31
31
  el += `<img class="lazy" data-src="${item.icon || item.avatar || ctx.theme.config.default.avatar}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${ctx.theme.config.default.avatar}&quot;;"/>`
32
- el += `<div class="lazy-icon" style="background-image:url(&quot;${ctx.theme.config.default.loading || ctx.utils.iconData('default:loading-placeholder')}&quot;);"></div>`
32
+ el += `<div class="lazy-icon"></div>`
33
33
  el += `</div>`
34
34
  el += `<div class="name">`
35
35
  el += `<span>${item.title}</span>`
@@ -17,14 +17,14 @@
17
17
 
18
18
  var index = 0
19
19
 
20
- function img(src, alt, loading) {
20
+ function img(src, alt) {
21
21
  let img = ''
22
22
  img += `<img class="lazy" data-fancybox="gallery-${index}" data-src="${src}"`
23
23
  if (alt?.length > 0) {
24
24
  img += ` alt="${alt}"`
25
25
  }
26
26
  img += `/>`
27
- img += `<div class="lazy-icon" style="background-image:url(${loading});"></div>`
27
+ img += `<div class="lazy-icon"></div>`
28
28
  // cap
29
29
  img += `<div class="image-meta">`
30
30
  if (alt?.length > 0) {
@@ -55,7 +55,7 @@ module.exports = ctx => function(args, content) {
55
55
  if (matches?.length > 2) {
56
56
  let alt = matches[1]
57
57
  let src = matches[2]
58
- el += `<div class="${layoutType}-cell lazy-box">${img(src, alt, ctx.theme.config.default.loading || ctx.utils.iconData('default:loading-placeholder'))}</div>`
58
+ el += `<div class="${layoutType}-cell lazy-box">${img(src, alt)}</div>`
59
59
  }
60
60
  }
61
61
  el += `</div>`
@@ -50,7 +50,7 @@ module.exports = ctx => function(args) {
50
50
  img += `onerror="this.src=&quot;${ctx.theme.config.default.image_onerror || ctx.utils.iconData('image:onerror')}&quot;"`
51
51
  img += '/>'
52
52
  // loading
53
- img += `<div class="lazy-icon" style="background-image:url(${ctx.theme.config.default.loading || ctx.utils.iconData('default:loading-placeholder')});"></div>`
53
+ img += `<div class="lazy-icon"></div>`
54
54
  if (fancyboxHref) {
55
55
  a += ` href="${fancyboxHref}">${img}</a>`
56
56
  return a
@@ -32,7 +32,7 @@ module.exports = ctx => function(args) {
32
32
  el += `<div class="grid-cell poster-card">`
33
33
  el += `<a class="card-link lazy-box" target="_blank" rel="external nofollow noopener noreferrer" href="${item.url}">`
34
34
  el += `<img class="lazy" data-src="${item.cover || item.icon || item.avatar || ctx.theme.config.default.cover}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${ctx.theme.config.default.cover}&quot;;"/>`
35
- el += `<div class="lazy-icon" style="background-image:url(${ctx.theme.config.default.loading || ctx.utils.iconData('default:loading-placeholder')});"></div>`
35
+ el += `<div class="lazy-icon"></div>`
36
36
  el += `<div class="image-meta">`
37
37
  if (item.title) {
38
38
  el += `<span class="image-caption">${item.title}</span>`
@@ -10,6 +10,7 @@
10
10
  module.exports = ctx => function(args) {
11
11
  args = ctx.args.map(args, ['repo', 'api'], ['group'])
12
12
  const host = ctx.theme.config.api_host.ghraw
13
+ const siteinfoApi = ctx.theme.config.data_services.siteinfo?.api
13
14
  var api
14
15
  if (args.api) {
15
16
  api = args.api
@@ -29,16 +30,19 @@ module.exports = ctx => function(args) {
29
30
  el += '<div class="grid-box">'
30
31
  for (let item of (links[args.group] || [])) {
31
32
  if (item?.url && item?.title) {
33
+ const itemSiteinfoApi = !item.icon && !item.avatar && siteinfoApi
34
+ ? siteinfoApi.replace('{href}', item.url)
35
+ : ''
32
36
  el += `<div class="grid-cell site-card">`
33
- el += `<a class="card-link" target="_blank" rel="external nofollow noopener noreferrer" href="${item.url}">`
37
+ el += `<a class="card-link"${itemSiteinfoApi ? ` data-siteinfo-api="${itemSiteinfoApi}"` : ''} target="_blank" rel="external nofollow noopener noreferrer" href="${item.url}">`
34
38
  el += `<div class="lazy-box snapshot">`
35
39
  el += `<img class="lazy" data-src="${item.cover || item.snapshot || item.screenshot || ('https://image.thum.io/get/width/1280/crop/720/' + item.url)}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${ctx.theme.config.default.cover}&quot;;"/>`
36
- el += `<div class="lazy-icon" style="background-image:url(&quot;${ctx.theme.config.default.loading || ctx.utils.iconData('default:loading-placeholder')}&quot;);"></div>`
40
+ el += `<div class="lazy-icon"></div>`
37
41
  el += `</div>`
38
42
  el += `<div class="info">`
39
43
  el += `<div class="lazy-box icon">`
40
- el += `<img class="lazy" data-src="${item.icon || item.avatar || ctx.theme.config.default.link}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${item.icon || item.avatar || ctx.theme.config.default.link}&quot;;"/>`
41
- el += `<div class="lazy-icon" style="background-image:url(&quot;${ctx.theme.config.default.loading || ctx.utils.iconData('default:loading-placeholder')}&quot;);"></div>`
44
+ el += `<img class="lazy siteinfo-icon" data-src="${item.icon || item.avatar || ctx.theme.config.default.link}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${item.icon || item.avatar || ctx.theme.config.default.link}&quot;;"/>`
45
+ el += `<div class="lazy-icon"></div>`
42
46
  el += `</div>`
43
47
  el += `<span class="title">${item.title}</span>`
44
48
  el += `<span class="desc">${item.description || item.url}</span>`
@@ -24,11 +24,11 @@ img
24
24
  max-width: 100%
25
25
 
26
26
  li
27
- font-size: 'calc(%s - 1px)' % var(--fsp)
27
+ font-size: 'calc(%s - 1px)' % var(--fs-content)
28
28
 
29
29
 
30
30
  ul,ol
31
- padding-left: var(--fsp)
31
+ padding-left: var(--fs-content)
32
32
 
33
33
 
34
34
  // table
@@ -40,8 +40,8 @@ table:not([class])
40
40
  margin: 1rem 0
41
41
  max-width: 100%
42
42
  vertical-align: text-top
43
- --fsp: $fsp2
44
- font-size: var(--fsp)
43
+ --fs-content: $fs-content-2
44
+ font-size: var(--fs-content)
45
45
  scrollbar(0,0)
46
46
  th
47
47
  background: var(--block)
@@ -2,7 +2,7 @@ blockquote
2
2
  display: block
3
3
  margin-left: 0
4
4
  margin-right: 0
5
- padding: 0 var(--fsp)
5
+ padding: 0 var(--fs-content)
6
6
  $bd-left = 4px
7
7
  color: var(--text-p3)
8
8
  position: relative
@@ -18,4 +18,4 @@ blockquote
18
18
  opacity: 0.5
19
19
  background: var(--text-meta)
20
20
  &:hover:before
21
- background: var(--theme)
21
+ background: var(--theme)
@@ -0,0 +1,132 @@
1
+ .dropdown
2
+ position: relative
3
+ display: inline-block
4
+ z-index: 0
5
+ overflow: visible
6
+ line-height: 1
7
+ font-size: $fs-14
8
+ >.dropdown-trigger
9
+ display: inline-flex
10
+ align-items: center
11
+ gap: 0.5rem
12
+ box-sizing: border-box
13
+ min-height: 2rem
14
+ padding: 0.5rem 0.75rem
15
+ cursor: pointer
16
+ list-style: none
17
+ outline: none
18
+ border-radius: $border-bar
19
+ color: var(--text-p2)
20
+ background: var(--block)
21
+ text-decoration: none
22
+ white-space: nowrap
23
+ &::-webkit-details-marker
24
+ display: none
25
+ &::marker
26
+ display: none
27
+ &:hover, &:focus-visible
28
+ color: var(--text-p1)
29
+ background: var(--block-border)
30
+ >svg, >img, >.icon
31
+ flex: 0 0 1.25rem
32
+ width: 1.25rem
33
+ height: 1.25rem
34
+ >.dropdown-arrow
35
+ transition: transform 0.2s ease
36
+ >span
37
+ overflow: hidden
38
+ text-overflow: ellipsis
39
+ &[open]
40
+ z-index: 10
41
+ >.dropdown-trigger
42
+ color: var(--text-p1)
43
+ background: var(--block-border)
44
+ >.dropdown-arrow
45
+ transform: rotate(180deg)
46
+ .dropdown-menu
47
+ display: flex
48
+ flex-direction: column
49
+ gap: 0.125rem
50
+ box-sizing: border-box
51
+ max-width: calc(100vw - 2rem)
52
+ max-height: calc(100vh - 2rem)
53
+ padding: 0.25rem
54
+ overflow-x: hidden
55
+ overflow-y: auto
56
+ font-size: $fs-14
57
+ border: 1px solid var(--block-border)
58
+ border-radius: $border-card
59
+ corner-shape: $corner-shape
60
+ background: rgba(white, 0.3)
61
+ box-shadow: 0 0 2px rgba(black, .04), 0 2px 8px rgba(black, .04), 0 4px 16px rgba(black, .04)
62
+ backdrop-filter: saturate(300%) blur(16px)
63
+ -webkit-backdrop-filter: saturate(300%) blur(16px)
64
+ :root[data-theme="dark"] &
65
+ background: rgba(white, 0.2)
66
+ :root:not([data-theme]) &
67
+ @media (prefers-color-scheme: dark)
68
+ background: rgba(white, 0.2)
69
+ text-align: left
70
+ line-height: 1
71
+
72
+ .dropdown-menu.ui-collection[data-density='compact']
73
+ min-width: 150px
74
+ --ui-item-min-height: calc(1.5rem + 2 * var(--ui-item-padding-y))
75
+
76
+ .dropdown > .dropdown-menu
77
+ position: absolute
78
+ z-index: 10
79
+ top: calc(100% + 0.5rem)
80
+ left: 50%
81
+ transform: translateX(-50%)
82
+ visibility: hidden
83
+
84
+ .dropdown[direction=up] > .dropdown-menu
85
+ top: auto
86
+ bottom: calc(100% + 0.5rem)
87
+
88
+ .dropdown[align=left] > .dropdown-menu
89
+ left: 0
90
+ transform: none
91
+
92
+ .dropdown[align=right] > .dropdown-menu
93
+ right: 0
94
+ left: auto
95
+ transform: none
96
+
97
+ .dropdown-layer
98
+ position: fixed
99
+ z-index: 1000
100
+ top: 0
101
+ right: 0
102
+ bottom: 0
103
+ left: 0
104
+ pointer-events: none
105
+ > .dropdown-bridge
106
+ position: fixed
107
+ z-index: 0
108
+ top: 0
109
+ right: 0
110
+ bottom: 0
111
+ left: 0
112
+ pointer-events: auto
113
+ background: transparent
114
+ > .dropdown-menu
115
+ position: fixed
116
+ z-index: 1000
117
+ top: 0
118
+ left: 0
119
+ transform: none
120
+ pointer-events: auto
121
+ visibility: hidden
122
+ opacity: 0
123
+ contain: paint
124
+ will-change: opacity
125
+ transition: opacity 0.16s ease-out
126
+ &.dropdown-menu-visible
127
+ visibility: visible
128
+ opacity: 1
129
+
130
+ @media (prefers-reduced-motion: reduce)
131
+ .dropdown-layer > .dropdown-menu, .dropdown > .dropdown-trigger > .dropdown-arrow
132
+ transition: none
@@ -7,7 +7,7 @@ html
7
7
  scrollbar(8px,8px)
8
8
  overflow-y: scroll
9
9
  font-family: $ff-body
10
- font-size: $fs-root
10
+ font-size: var(--fs-root)
11
11
  -webkit-text-size-adjust: 100%
12
12
  -ms-text-size-adjust: 100%
13
13
  scroll-padding-top: 0
@@ -22,8 +22,11 @@ svg.loading
22
22
  border-radius: $border-card
23
23
  position relative
24
24
  padding: 2rem
25
- svg
25
+ svg,
26
+ .lazy-icon
26
27
  margin: 4px
28
+ .lazy-icon
29
+ display: inline-block
27
30
  &:after
28
31
  content: hexo-config('style.loading.loading')
29
32
  color: var(--text-p1)
@@ -35,4 +38,4 @@ svg.loading
35
38
 
36
39
  .article-footer
37
40
  .loading-wrap
38
- background: none
41
+ background: none
@@ -11,9 +11,9 @@ h3,.h3
11
11
  h4,.h4
12
12
  font-size: var(--fsh4)
13
13
  h5
14
- font-size: 'calc(%s + 1px)' % var(--fsp)
14
+ font-size: 'calc(%s + 1px)' % var(--fs-content)
15
15
  h6
16
- font-size: 'calc(%s + 0px)' % var(--fsp)
16
+ font-size: 'calc(%s + 0px)' % var(--fs-content)
17
17
 
18
18
  // story 文章段落标题(h2):居中 + 两侧 accent 斜杠装饰
19
19
  // 专栏列表页专栏标题(.topic-title)与 story 文章 h2 共用
@@ -34,15 +34,15 @@ story-title()
34
34
 
35
35
  // 次级段落字号
36
36
  .fs15
37
- --fsp: $fs-15
38
- font-size: var(--fsp)
37
+ --fs-content: $fs-15
38
+ font-size: var(--fs-content)
39
39
  .fs14
40
- --fsp: $fs-14
41
- font-size: var(--fsp)
40
+ --fs-content: $fs-14
41
+ font-size: var(--fs-content)
42
42
 
43
43
  .footnote
44
- --fsp: $fs-13
45
- font-size: var(--fsp)
44
+ --fs-content: $fs-13
45
+ font-size: var(--fs-content)
46
46
 
47
47
  .widgets
48
48
  .post-title