hexo-theme-stellar 1.44.0 → 2.0.0-rc.2

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 (377) hide show
  1. package/CHANGELOG.md +63 -5
  2. package/CONTRIBUTING.md +29 -120
  3. package/README.md +66 -73
  4. package/README_EN.md +66 -73
  5. package/_config.yml +890 -743
  6. package/_data/icons.yml +43 -171
  7. package/_data/widgets.yml +1 -1
  8. package/assets/icon-v2-x512.webp +0 -0
  9. package/languages/en.yml +59 -2
  10. package/languages/zh-CN.yml +59 -2
  11. package/languages/zh-TW.yml +59 -2
  12. package/layout/404.ejs +7 -4
  13. package/layout/_partial/comments/artalk/layout.ejs +3 -2
  14. package/layout/_partial/comments/beaudar/layout.ejs +2 -2
  15. package/layout/_partial/comments/giscus/layout.ejs +2 -5
  16. package/layout/_partial/comments/layout.ejs +11 -40
  17. package/layout/_partial/comments/twikoo/layout.ejs +3 -2
  18. package/layout/_partial/comments/utterances/layout.ejs +2 -2
  19. package/layout/_partial/comments/waline/layout.ejs +3 -2
  20. package/layout/_partial/components/collection-item.ejs +30 -13
  21. package/layout/_partial/components/collection.ejs +2 -2
  22. package/layout/_partial/cover/index.ejs +5 -2
  23. package/layout/_partial/cover/wiki_cover.ejs +39 -28
  24. package/layout/_partial/dropdown.ejs +26 -14
  25. package/layout/_partial/head.ejs +96 -69
  26. package/layout/_partial/main/article/contributors.ejs +12 -9
  27. package/layout/_partial/main/article/post_footer.ejs +47 -0
  28. package/layout/_partial/main/article/post_read_next.ejs +22 -0
  29. package/layout/_partial/main/article/post_related.ejs +14 -0
  30. package/layout/_partial/main/article/post_tags.ejs +12 -0
  31. package/layout/_partial/main/article/share.ejs +55 -0
  32. package/layout/_partial/main/footer.ejs +11 -10
  33. package/layout/_partial/main/{navbar/nav_tabs_blog.ejs → listing_nav/blog.ejs} +21 -18
  34. package/layout/_partial/main/listing_nav/wiki.ejs +56 -0
  35. package/layout/_partial/main/navbar/article_banner.ejs +39 -35
  36. package/layout/_partial/main/navbar/breadcrumb/blog.ejs +15 -28
  37. package/layout/_partial/main/navbar/breadcrumb/note.ejs +12 -5
  38. package/layout/_partial/main/navbar/breadcrumb/wiki.ejs +15 -11
  39. package/layout/_partial/main/navbar/dateinfo.ejs +28 -12
  40. package/layout/_partial/main/navbar/ghinfo.ejs +5 -3
  41. package/layout/_partial/main/notebook/note_card.ejs +12 -17
  42. package/layout/_partial/main/notebook/note_tags.ejs +3 -4
  43. package/layout/_partial/main/notebook/notebook_card.ejs +11 -4
  44. package/layout/_partial/main/pin_slider.ejs +48 -43
  45. package/layout/_partial/main/post_list/archive_item.ejs +8 -4
  46. package/layout/_partial/main/post_list/latest_post_card.ejs +10 -4
  47. package/layout/_partial/main/post_list/post_card.ejs +39 -90
  48. package/layout/_partial/main/post_list/topic_card.ejs +7 -5
  49. package/layout/_partial/main/post_list/wiki_card.ejs +16 -13
  50. package/layout/_partial/menubtn.ejs +3 -3
  51. package/layout/_partial/primitives/item.ejs +8 -0
  52. package/layout/_partial/primitives/navigation.ejs +17 -0
  53. package/layout/_partial/primitives/region.ejs +16 -0
  54. package/layout/_partial/primitives/section.ejs +8 -0
  55. package/layout/_partial/primitives/shell.ejs +35 -0
  56. package/layout/_partial/regions/widgets.ejs +84 -0
  57. package/layout/_partial/scripts/defines.ejs +46 -76
  58. package/layout/_partial/scripts/runtime.ejs +55 -0
  59. package/layout/_partial/scripts.ejs +18 -42
  60. package/layout/_partial/search/dialog.ejs +33 -0
  61. package/layout/_partial/search/shortcut.ejs +1 -0
  62. package/layout/_partial/sidebar/brand.ejs +127 -0
  63. package/layout/_partial/sidebar/menu.ejs +59 -14
  64. package/layout/_partial/sidebar/search.ejs +58 -41
  65. package/layout/_partial/widgets/actions.ejs +25 -0
  66. package/layout/_partial/widgets/author.ejs +9 -5
  67. package/layout/_partial/widgets/components/linklist.ejs +1 -2
  68. package/layout/_partial/widgets/ghissues.ejs +7 -10
  69. package/layout/_partial/widgets/ghrepo.ejs +23 -16
  70. package/layout/_partial/widgets/ghuser.ejs +3 -3
  71. package/layout/_partial/widgets/recent.ejs +25 -21
  72. package/layout/_partial/widgets/related.ejs +25 -34
  73. package/layout/_partial/widgets/settings.ejs +17 -0
  74. package/layout/_partial/widgets/tagtree.ejs +18 -10
  75. package/layout/_partial/widgets/toc.ejs +17 -9
  76. package/layout/_partial/widgets/tree.ejs +12 -13
  77. package/layout/archive.ejs +20 -13
  78. package/layout/categories.ejs +4 -3
  79. package/layout/collection-preview.ejs +8 -9
  80. package/layout/index.ejs +32 -43
  81. package/layout/index_topic.ejs +6 -13
  82. package/layout/index_wiki.ejs +17 -34
  83. package/layout/layout.ejs +195 -65
  84. package/layout/notebooks.ejs +3 -3
  85. package/layout/notes.ejs +4 -4
  86. package/layout/page.ejs +95 -50
  87. package/layout/settings.ejs +109 -0
  88. package/layout/tags.ejs +4 -3
  89. package/{THIRD-PARTY-NOTICES.md → legal/THIRD-PARTY-NOTICES.md} +8 -4
  90. package/package.json +28 -6
  91. package/scripts/commands/stellar.js +55 -0
  92. package/scripts/events/index.js +15 -9
  93. package/scripts/events/lib/authors.js +9 -7
  94. package/scripts/events/lib/config-hot-reload.js +65 -0
  95. package/scripts/events/lib/config-schema.js +20 -0
  96. package/scripts/events/lib/config.js +22 -63
  97. package/scripts/events/lib/content-config.js +5 -0
  98. package/scripts/events/lib/doc_tree.js +99 -9
  99. package/scripts/events/lib/links.js +6 -3
  100. package/scripts/events/lib/notebooks.js +200 -8
  101. package/scripts/events/lib/utils.js +4 -4
  102. package/scripts/events/lib/version-check.js +57 -3
  103. package/scripts/filters/index.js +2 -27
  104. package/scripts/filters/lib/img_lazyload.js +4 -126
  105. package/scripts/filters/lib/img_onerror.js +14 -17
  106. package/scripts/filters/lib/page-view-model.js +152 -0
  107. package/scripts/generators/404.js +13 -8
  108. package/scripts/generators/author.js +16 -11
  109. package/scripts/generators/notebooks.js +68 -39
  110. package/scripts/generators/search.js +62 -67
  111. package/scripts/generators/settings.js +43 -0
  112. package/scripts/generators/stellar-icons.js +13 -5
  113. package/scripts/generators/topic.js +24 -13
  114. package/scripts/generators/wiki.js +49 -21
  115. package/scripts/helpers/ai_label.js +6 -12
  116. package/scripts/helpers/brand.js +17 -0
  117. package/scripts/helpers/browser_runtime_manifest.js +12 -0
  118. package/scripts/helpers/caption.js +8 -0
  119. package/scripts/helpers/category_color.js +13 -13
  120. package/scripts/helpers/collection.js +18 -0
  121. package/scripts/helpers/comments_model.js +14 -0
  122. package/scripts/helpers/extension_assets.js +8 -0
  123. package/scripts/helpers/json_ld.js +16 -7
  124. package/scripts/helpers/mdrender.js +3 -9
  125. package/scripts/helpers/menu.js +9 -0
  126. package/scripts/helpers/post_view_model.js +23 -0
  127. package/scripts/helpers/regions.js +37 -0
  128. package/scripts/helpers/scrollreveal.js +7 -7
  129. package/scripts/helpers/search_domain.js +34 -0
  130. package/scripts/helpers/service_provider.js +9 -0
  131. package/scripts/helpers/settings.js +16 -0
  132. package/scripts/helpers/share_action.js +8 -0
  133. package/scripts/helpers/stellar_config.js +12 -0
  134. package/scripts/helpers/stellar_data.js +8 -0
  135. package/scripts/helpers/stellar_info.js +21 -16
  136. package/scripts/helpers/stellar_inject.js +9 -0
  137. package/scripts/helpers/ui_classes.js +12 -0
  138. package/scripts/lib/ai_label.js +14 -11
  139. package/scripts/lib/brand.js +41 -0
  140. package/scripts/lib/browser-runtime.js +115 -0
  141. package/scripts/lib/caption.js +21 -0
  142. package/scripts/lib/collection-pipeline/adapters/notebook.js +62 -0
  143. package/scripts/lib/collection-pipeline/adapters/post.js +20 -0
  144. package/scripts/lib/collection-pipeline/adapters/topic.js +82 -0
  145. package/scripts/lib/collection-pipeline/adapters/wiki.js +20 -0
  146. package/scripts/lib/collection-pipeline/index.js +169 -0
  147. package/scripts/lib/collection-pipeline/registry.js +27 -0
  148. package/scripts/lib/collection-pipeline/shared.js +164 -0
  149. package/scripts/lib/comments.js +60 -0
  150. package/scripts/lib/config-inject.js +20 -0
  151. package/scripts/lib/config-schema.js +1095 -0
  152. package/scripts/lib/content-config.js +157 -0
  153. package/scripts/lib/content-defaults.js +56 -0
  154. package/scripts/lib/content-membership.js +233 -0
  155. package/scripts/lib/contribution-contract.js +165 -0
  156. package/scripts/lib/contribution-registry.js +429 -0
  157. package/scripts/lib/doc_tree.js +46 -44
  158. package/scripts/lib/doctor.js +296 -0
  159. package/scripts/lib/front-matter.js +29 -0
  160. package/scripts/lib/hero-effect-registry.js +262 -0
  161. package/scripts/lib/html-images.js +128 -0
  162. package/scripts/lib/internal-constants.js +152 -0
  163. package/scripts/lib/layout-config.js +62 -0
  164. package/scripts/lib/mdrender_html.js +36 -23
  165. package/scripts/lib/menu.js +36 -0
  166. package/scripts/lib/model-schema.js +90 -0
  167. package/scripts/lib/models/index.js +1542 -0
  168. package/scripts/lib/new-note.js +172 -0
  169. package/scripts/lib/page-view-model-registry.js +103 -0
  170. package/scripts/lib/regions.js +132 -0
  171. package/scripts/lib/runtime-data.js +23 -0
  172. package/scripts/lib/safe-path.js +44 -0
  173. package/scripts/lib/search-domain.js +59 -0
  174. package/scripts/lib/seo.js +10 -10
  175. package/scripts/lib/service-provider.js +12 -0
  176. package/scripts/lib/settings.js +16 -0
  177. package/scripts/lib/share-services.js +96 -0
  178. package/scripts/lib/source-config.js +27 -0
  179. package/scripts/lib/theme-metadata.js +22 -0
  180. package/scripts/lib/ui-capabilities.js +32 -0
  181. package/scripts/lib/widget-registry.js +150 -0
  182. package/scripts/lib/wiki_readme.js +15 -11
  183. package/scripts/schema/config-rules.js +290 -0
  184. package/scripts/schema/config-schema.js +163 -0
  185. package/scripts/schema/content-config-rules.js +264 -0
  186. package/scripts/schema/content-config-schema.js +321 -0
  187. package/scripts/schema/model-schema.js +971 -0
  188. package/scripts/schema/schema-utils.js +48 -0
  189. package/scripts/tags/index.js +2 -3
  190. package/scripts/tags/lib/albums.js +6 -6
  191. package/scripts/tags/lib/banner.js +10 -4
  192. package/scripts/tags/lib/box.js +1 -1
  193. package/scripts/tags/lib/button.js +1 -1
  194. package/scripts/tags/lib/chat.js +9 -31
  195. package/scripts/tags/lib/checkbox.js +6 -1
  196. package/scripts/tags/lib/copy.js +4 -1
  197. package/scripts/tags/lib/dropdown.js +6 -4
  198. package/scripts/tags/lib/emoji.js +5 -10
  199. package/scripts/tags/lib/friends.js +4 -4
  200. package/scripts/tags/lib/gallery.js +7 -7
  201. package/scripts/tags/lib/ghcard.js +5 -3
  202. package/scripts/tags/lib/gist.js +16 -0
  203. package/scripts/tags/lib/grid.js +4 -2
  204. package/scripts/tags/lib/hashtag.js +1 -1
  205. package/scripts/tags/lib/icon.js +1 -1
  206. package/scripts/tags/lib/image.js +1 -1
  207. package/scripts/tags/lib/link.js +6 -3
  208. package/scripts/tags/lib/mark.js +8 -3
  209. package/scripts/tags/lib/md.js +1 -1
  210. package/scripts/tags/lib/note.js +1 -1
  211. package/scripts/tags/lib/okr.js +18 -1
  212. package/scripts/tags/lib/posters.js +6 -6
  213. package/scripts/tags/lib/quot.js +15 -5
  214. package/scripts/tags/lib/rating.js +7 -4
  215. package/scripts/tags/lib/sites.js +8 -6
  216. package/scripts/tags/lib/tip.js +6 -6
  217. package/scripts/tags/lib/toc.js +2 -2
  218. package/scripts/tags/lib/vote.js +8 -5
  219. package/source/css/_appearances/_mixins.styl +258 -0
  220. package/source/css/_appearances/card.styl +30 -0
  221. package/source/css/_appearances/flat.styl +44 -0
  222. package/source/css/_appearances/glass.styl +138 -0
  223. package/source/css/_appearances/minimal.styl +325 -0
  224. package/source/css/_common/base.styl +5 -9
  225. package/source/css/_common/blockquote.styl +0 -1
  226. package/source/css/_common/button.styl +1 -1
  227. package/source/css/_common/device.styl +24 -42
  228. package/source/css/_common/dropdown.styl +20 -14
  229. package/source/css/_common/highlight.styl +5 -12
  230. package/source/css/_common/icon.styl +38 -0
  231. package/source/css/_common/input.styl +33 -0
  232. package/source/css/_common/loading.styl +2 -2
  233. package/source/css/_common/svg.styl +0 -8
  234. package/source/css/_components/collection.styl +21 -64
  235. package/source/css/_components/layout.styl +524 -84
  236. package/source/css/_components/list.styl +36 -22
  237. package/source/css/_components/main.styl +16 -19
  238. package/source/css/_components/md.styl +8 -13
  239. package/source/css/_components/pages/article-indent.styl +1 -1
  240. package/source/css/_components/pages/article-story.styl +7 -6
  241. package/source/css/_components/pages/article-tech.styl +5 -5
  242. package/source/css/_components/pages/collection-preview.styl +1 -5
  243. package/source/css/_components/pages/settings.styl +184 -0
  244. package/source/css/_components/partial/article-banner.styl +2 -2
  245. package/source/css/_components/partial/article-footer.styl +0 -32
  246. package/source/css/_components/partial/article-share.styl +48 -0
  247. package/source/css/_components/partial/article-tags.styl +1 -1
  248. package/source/css/_components/partial/cover.styl +13 -13
  249. package/source/css/_components/partial/listing-nav.styl +68 -0
  250. package/source/css/_components/partial/paginator.styl +1 -1
  251. package/source/css/_components/pin-slider.styl +3 -3
  252. package/source/css/_components/sidebar/brand.styl +135 -0
  253. package/source/css/_components/sidebar/footer.styl +14 -31
  254. package/source/css/_components/sidebar/nav-area.styl +4 -0
  255. package/source/css/_components/sidebar/search.styl +176 -26
  256. package/source/css/_components/sidebar/sidebar.styl +76 -110
  257. package/source/css/_components/tag-plugins/banner.styl +16 -28
  258. package/source/css/_components/tag-plugins/checkbox.styl +7 -22
  259. package/source/css/_components/tag-plugins/copy.styl +3 -5
  260. package/source/css/_components/tag-plugins/gallery.styl +2 -2
  261. package/source/css/_components/tag-plugins/grid.styl +3 -0
  262. package/source/css/_components/tag-plugins/image.styl +2 -1
  263. package/source/css/_components/tag-plugins/link.styl +0 -1
  264. package/source/css/_components/tag-plugins/mark.styl +19 -4
  265. package/source/css/_components/tag-plugins/mdrender.styl +2 -2
  266. package/source/css/_components/tag-plugins/navbar.styl +23 -17
  267. package/source/css/_components/tag-plugins/note.styl +2 -2
  268. package/source/css/_components/tag-plugins/okr.styl +1 -2
  269. package/source/css/_components/tag-plugins/quot.styl +5 -0
  270. package/source/css/_components/tag-plugins/rating.styl +3 -2
  271. package/source/css/_components/tag-plugins/timeline.styl +1 -1
  272. package/source/css/_components/tag-plugins/tip.styl +1 -1
  273. package/source/css/_components/tag-plugins/vote.styl +3 -2
  274. package/source/css/_components/widgets/ghrepo.styl +2 -1
  275. package/source/css/_components/widgets/ghuser.styl +4 -6
  276. package/source/css/_components/widgets/settings.styl +49 -0
  277. package/source/css/_components/widgets/timeline.styl +2 -2
  278. package/source/css/_components/widgets/toc.styl +12 -33
  279. package/source/css/_components/widgets/widgets.styl +14 -19
  280. package/source/css/_components/wiki-card.styl +2 -2
  281. package/source/css/_custom.styl +26 -32
  282. package/source/css/_defines/func.styl +47 -37
  283. package/source/css/_defines/theme_base.styl +8 -10
  284. package/source/css/_plugins/card-hover.styl +1 -1
  285. package/source/css/_plugins/copycode.styl +16 -19
  286. package/source/css/_plugins/index.styl +3 -8
  287. package/source/css/_plugins/lazyload.styl +1 -1
  288. package/source/css/main.styl +12 -0
  289. package/source/js/main.js +302 -59
  290. package/source/js/plugins/adaptive-text.js +33 -16
  291. package/source/js/plugins/card-hover.js +2 -24
  292. package/source/js/plugins/copycode.js +10 -16
  293. package/source/js/plugins/voice.js +51 -48
  294. package/source/js/profile.js +316 -0
  295. package/source/js/runtime/asset-loader.js +61 -0
  296. package/source/js/runtime/extension-registry.js +149 -0
  297. package/source/js/runtime/extensions/card-hover.js +5 -0
  298. package/source/js/runtime/extensions/color-scheme-switch.js +101 -0
  299. package/source/js/runtime/extensions/comments.js +200 -0
  300. package/source/js/runtime/extensions/deferred-icons.js +53 -0
  301. package/source/js/{plugins → runtime/extensions}/dropdown.js +69 -30
  302. package/source/js/runtime/extensions/feature.js +200 -0
  303. package/source/js/runtime/extensions/hero-effect.js +179 -0
  304. package/source/js/runtime/extensions/reveal.js +80 -0
  305. package/source/js/runtime/extensions/search.js +23 -0
  306. package/source/js/{services.js → runtime/extensions/services.js} +67 -54
  307. package/source/js/runtime/extensions/settings.js +235 -0
  308. package/source/js/runtime/hero-effects/ferrofluid.js +385 -0
  309. package/source/js/{plugins → runtime/hero-effects}/galaxy.js +53 -177
  310. package/source/js/runtime/hero-effects/light-rays.js +293 -0
  311. package/source/js/runtime/index.js +75 -0
  312. package/source/js/runtime/legacy-request-adapter.js +47 -0
  313. package/source/js/runtime/request-cache.js +219 -0
  314. package/source/js/search/algolia-search.js +76 -51
  315. package/source/js/search/highlight.js +1 -1
  316. package/source/js/search/local-search.js +96 -57
  317. package/source/js/search/shortcut.js +31 -20
  318. package/source/js/services/friends_and_posts.js +6 -1
  319. package/source/js/services/rating.js +14 -6
  320. package/source/js/services/siteinfo.js +3 -7
  321. package/source/js/services/timeline.js +1 -1
  322. package/source/js/services/vote.js +7 -5
  323. package/source/js/services/weibo.js +1 -1
  324. package/source/js/utils.js +19 -444
  325. package/.claude/skills/stellar-theme-dev/SKILL.md +0 -51
  326. package/_data/chat_users.yml +0 -0
  327. package/layout/_partial/comments/artalk/script.ejs +0 -76
  328. package/layout/_partial/comments/beaudar/script.ejs +0 -33
  329. package/layout/_partial/comments/giscus/script.ejs +0 -31
  330. package/layout/_partial/comments/script.ejs +0 -3
  331. package/layout/_partial/comments/twikoo/script.ejs +0 -25
  332. package/layout/_partial/comments/utterances/script.ejs +0 -33
  333. package/layout/_partial/comments/waline/script.ejs +0 -43
  334. package/layout/_partial/main/article/article_footer.ejs +0 -164
  335. package/layout/_partial/main/article/article_tags.ejs +0 -16
  336. package/layout/_partial/main/article/read_next.ejs +0 -63
  337. package/layout/_partial/main/article/related_posts.ejs +0 -16
  338. package/layout/_partial/main/navbar/nav_tabs_wiki.ejs +0 -52
  339. package/layout/_partial/scripts/bootstrap.ejs +0 -37
  340. package/layout/_partial/scripts/lazyload.ejs +0 -89
  341. package/layout/_partial/sidebar/grad-def.ejs +0 -26
  342. package/layout/_partial/sidebar/index_leftbar.ejs +0 -191
  343. package/layout/_partial/sidebar/index_rightbar.ejs +0 -94
  344. package/layout/_partial/sidebar/logo.ejs +0 -99
  345. package/layout/_plugins/adaptive_text.ejs +0 -11
  346. package/layout/_plugins/card_hover.ejs +0 -32
  347. package/layout/_plugins/copycode.ejs +0 -17
  348. package/layout/_plugins/fancybox.ejs +0 -53
  349. package/layout/_plugins/heti.ejs +0 -21
  350. package/layout/_plugins/index.ejs +0 -50
  351. package/layout/_plugins/mathjax.ejs +0 -49
  352. package/layout/_plugins/mermaid.ejs +0 -33
  353. package/layout/_plugins/preload.ejs +0 -9
  354. package/layout/_plugins/scrollreveal.ejs +0 -102
  355. package/layout/_plugins/search/algolia_search.ejs +0 -12
  356. package/layout/_plugins/search/local_search.ejs +0 -9
  357. package/layout/_plugins/swiper.ejs +0 -27
  358. package/layout/_plugins/tianli_gpt.ejs +0 -14
  359. package/scripts/commands/new-note.js +0 -49
  360. package/scripts/events/lib/merge_posts.js +0 -48
  361. package/scripts/events/lib/topic_tree.js +0 -48
  362. package/scripts/helpers/related_posts.js +0 -51
  363. package/scripts/helpers/subtitle.js +0 -8
  364. package/scripts/lib/notebooks.js +0 -181
  365. package/scripts/lib/subtitle.js +0 -28
  366. package/scripts/tags/lib/about.js +0 -48
  367. package/source/css/_components/page-transition.styl +0 -22
  368. package/source/css/_components/partial/navbar.styl +0 -51
  369. package/source/css/_components/sidebar/logo.styl +0 -111
  370. package/source/css/_components/tag-plugins/about.styl +0 -56
  371. package/source/css/_components/widgets/tree.styl +0 -0
  372. package/source/css/_plugins/scrollreveal.styl +0 -6
  373. package/source/css/_plugins/tianli_gpt.styl +0 -213
  374. package/source/css/plugins/mermaid.styl +0 -137
  375. package/source/js/icons.js +0 -60
  376. package/source/js/services/fcircle.js +0 -35
  377. package/source/js/theme.js +0 -61
@@ -1,57 +1,65 @@
1
1
  <%
2
2
  function layoutWikiCover() {
3
3
  var el = '';
4
- const proj = theme.wiki.tree[page.wiki];
5
- if (proj == undefined || proj.homepage.path !== page.path) {
6
- return el;
4
+ var wikiViewModel = typeof viewModel !== 'undefined' && viewModel?.collection?.profile === 'wiki'
5
+ ? viewModel
6
+ : null
7
+ if (!wikiViewModel?.render?.cover) {
8
+ throw new Error('Stellar v2: Wiki Hero 缺少显式 render.cover')
7
9
  }
8
- if (proj.coverpage == undefined || proj.coverpage === false || proj.coverpage === '[]') {
10
+ var cover = wikiViewModel.render.cover
11
+ if (!cover.enabled) {
9
12
  return el;
10
13
  }
11
-
12
- var background = typeof proj.background === 'string' && proj.background.length > 0 ? proj.background : '';
13
- var animation = proj.animation && typeof proj.animation === 'object' ? proj.animation : {};
14
- var preview = proj.preview || {};
14
+ var backgroundConfig = cover.background || {};
15
+ var background = typeof backgroundConfig.image === 'string' && backgroundConfig.image.length > 0 ? backgroundConfig.image : '';
16
+ var effect = backgroundConfig.effect || {};
17
+ var preview = cover.preview || {};
15
18
  var commands = Array.isArray(preview.commands) ? preview.commands.filter(function (item) {
16
19
  return item && item.codes;
17
20
  }) : [];
18
21
  var hasPreview = (preview.type === 'image' && preview.src) || commands.length > 0;
19
- var title = proj.headline || proj.title || page.title;
20
- var desc = proj.description || page.description;
21
- var repo = proj.repo;
22
- var actions = Array.isArray(proj.actions) ? proj.actions.filter(function (item) {
22
+ var title = cover.title;
23
+ var desc = cover.description;
24
+ var repo = cover.repository;
25
+ var actions = Array.isArray(cover.actions) ? cover.actions.filter(function (item) {
23
26
  return item && item.title && item.url;
24
27
  }) : [];
25
- var isGalaxy = animation.type === 'galaxy';
28
+ var hasEffect = typeof effect.type === 'string' && effect.type.length > 0;
26
29
  var isImageBackground = background.length > 0;
27
- var galaxyParams = animation.params && typeof animation.params === 'object' && !Array.isArray(animation.params) ? animation.params : {};
28
- var galaxyParamsJson = escape_html(JSON.stringify(galaxyParams));
29
- var projectName = proj.name || proj.title || page.wiki;
30
+ var effectOptions = effect.options && typeof effect.options === 'object' && !Array.isArray(effect.options) ? effect.options : {};
31
+ var effectRuntime = effect.runtime && typeof effect.runtime === 'object' && !Array.isArray(effect.runtime) ? effect.runtime : {};
32
+ var effectPresentation = effect.presentation && typeof effect.presentation === 'object' && !Array.isArray(effect.presentation) ? effect.presentation : {};
33
+ var effectBackground = typeof effectPresentation.background === 'string' ? effectPresentation.background : '#000000';
34
+ var respectsReducedMotion = effectRuntime.respectReducedMotion !== false;
35
+ var effectOptionsJson = escape_html(JSON.stringify(effectOptions));
36
+ var effectRuntimeJson = escape_html(JSON.stringify(effectRuntime));
37
+ var projectName = cover.projectName;
30
38
  var adaptiveTextAttr = '';
31
39
  if (isImageBackground) {
32
40
  adaptiveTextAttr = ' data-text-adaptive="split" style="--cover-url:url(\'' + escape_html(background.replace(/'/g, '%27')) + '\')"';
33
- } else if (isGalaxy) {
34
- adaptiveTextAttr = ' data-text-adaptive="split" style="--adaptive-background:#000000"';
41
+ } else if (hasEffect) {
42
+ adaptiveTextAttr = ' data-text-adaptive="split" style="--adaptive-background:' + escape_html(effectBackground) + '"';
35
43
  }
36
44
 
37
- el += '<div class="l_cover wiki wiki-hero' + (hasPreview ? '' : ' without-preview') + '">';
38
- el += '<div class="wiki-cover-background' + (isGalaxy ? ' galaxy' : '') + (isImageBackground ? ' image' : '') + '" aria-hidden="true">';
45
+ el += '<div class="cover-content wiki wiki-hero' + (hasPreview ? '' : ' without-preview') + '">';
46
+ el += '<div class="wiki-cover-background' + (hasEffect ? ' has-effect' : '') + (respectsReducedMotion ? ' respect-reduced-motion' : '') + (isImageBackground ? ' image' : '') + '"' + (hasEffect ? ' style="--hero-effect-background:' + escape_html(effectBackground) + '"' : '') + ' aria-hidden="true">';
39
47
  if (isImageBackground) {
40
48
  el += '<img class="wiki-cover-background-image" src="' + escape_html(background) + '" alt="" />';
41
49
  el += '<img class="wiki-cover-background-blur" src="' + escape_html(background) + '" alt="" />';
42
50
  }
43
- if (isGalaxy) {
44
- el += '<canvas data-galaxy-params="' + galaxyParamsJson + '"></canvas>';
51
+ if (hasEffect) {
52
+ el += '<canvas data-hero-effect="' + escape_html(effect.type) + '" data-effect-options="' + effectOptionsJson + '" data-effect-runtime="' + effectRuntimeJson + '"></canvas>';
45
53
  }
46
54
  el += '<div class="wiki-cover-background-overlay"></div></div>';
47
55
  el += '<div class="wiki-cover-layout"' + adaptiveTextAttr + '>';
48
- el += '<nav class="wiki-navbar" aria-label="' + escape_html(config.title) + '">';
49
- el += '<a class="wiki-navbar-home" href="' + escape_html(pretty_url('/')) + '">' + escape_html(config.title) + '</a>';
56
+ el += '<nav class="wiki-navbar" aria-label="' + escape_html(cover.siteName) + '">';
57
+ el += '<a class="wiki-navbar-home" href="' + escape_html(pretty_url('/')) + '">' + escape_html(cover.siteName) + '</a>';
50
58
  el += '</nav>';
51
59
  el += '<article class="cover-wrap wiki-cover-content">';
52
60
  el += '<section class="wiki-cover-intro">';
53
61
  if (repo) {
54
- el += '<a class="wiki-cover-release ds-ghinfo is-loading" data-project-name="' + escape_html(projectName) + '" data-repo="' + escape_html(repo) + '" data-api="https://' + theme.api_host.ghapi + '/repos/' + escape_html(repo) + '/tags" index="0" target="_blank" rel="external nofollow noopener noreferrer">';
62
+ el += '<a class="wiki-cover-release ds-ghinfo is-loading" data-project-name="' + escape_html(projectName) + '" data-repo="' + escape_html(repo) + '" data-api="' + escape_html(cover.releaseApi) + '" index="0" target="_blank" rel="external nofollow noopener noreferrer">';
55
63
  el += '<span class="wiki-cover-release-value"><span type="text" id="latest-tag-name"></span></span></a>';
56
64
  }
57
65
  if (title) {
@@ -62,11 +70,14 @@ function layoutWikiCover() {
62
70
  }
63
71
  el += '<div class="wiki-cover-actions">';
64
72
  if (repo) {
65
- el += '<a class="button wiki-cover-source card-hover card-hover--spotlight" href="https://github.com/' + escape_html(repo) + '" target="_blank" rel="external nofollow noopener noreferrer">' + icon('default:github') + '<span>' + escape_html(__('btn.source')) + '</span></a>';
73
+ el += '<a class="' + ui_classes('button wiki-cover-source', 'spotlight') + '" href="' + escape_html(cover.sourceUrl) + '" target="_blank" rel="external nofollow noopener noreferrer">' + icon('default:github') + '<span>' + escape_html(__('btn.source')) + '</span></a>';
66
74
  }
67
- el += '<a class="button card-hover card-hover--spotlight" href="#start">' + icon('default:documents') + '<span>' + escape_html(__('btn.docs')) + '</span></a>';
75
+ el += '<a class="' + ui_classes('button', 'spotlight') + '" href="#start">' + icon('default:documents') + '<span>' + escape_html(__('btn.docs')) + '</span></a>';
68
76
  actions.forEach(function (item) {
69
- el += '<a class="button card-hover card-hover--spotlight" href="' + escape_html(item.url) + '" target="_blank" rel="external nofollow noopener noreferrer">';
77
+ var linkAttrs = item.url.includes('://')
78
+ ? ' target="_blank" rel="external nofollow noopener noreferrer"'
79
+ : ' rel="noopener noreferrer"';
80
+ el += '<a class="' + ui_classes('button', 'spotlight') + '" href="' + escape_html(item.url) + '"' + linkAttrs + '>';
70
81
  if (item.icon) {
71
82
  el += icon(item.icon);
72
83
  }
@@ -1,16 +1,18 @@
1
1
  <%
2
2
  const dropdownItem = typeof item !== 'undefined' && item ? item : {}
3
- const items = Array.isArray(dropdownItem.items) ? dropdownItem.items.filter(child => child && child.title && child.url) : []
3
+ const items = Array.isArray(dropdownItem.items) ? dropdownItem.items.filter(child => {
4
+ if (!child || !child.title) return false
5
+ if (child.type === 'link') return !!child.url
6
+ if (child.type === 'button') return !!child.onclick
7
+ return false
8
+ }) : []
4
9
  const classes = ['dropdown']
5
10
  const dropdownRootClass = typeof rootClass === 'string' ? rootClass : ''
6
11
  if (dropdownRootClass) {
7
12
  classes.push(dropdownRootClass)
8
13
  }
9
- const triggerClasses = ['dropdown-trigger']
10
14
  const dropdownTriggerClass = typeof triggerClass === 'string' ? triggerClass : ''
11
- if (dropdownTriggerClass) {
12
- triggerClasses.push(dropdownTriggerClass)
13
- }
15
+ const triggerClasses = ui_classes('dropdown-trigger', 'interactive', dropdownTriggerClass)
14
16
  const dropdownDirection = typeof direction === 'string' ? direction : ''
15
17
  const dropdownShowTitle = typeof showTitle === 'undefined' || showTitle !== false
16
18
 
@@ -25,6 +27,15 @@ function linkAttrs(child) {
25
27
  return attrs
26
28
  }
27
29
 
30
+ function itemContent(child) {
31
+ var body = ''
32
+ if (child.icon) {
33
+ body += '<span class="ui-icon">' + icon(child.icon, 'no-lazy', true) + '</span>'
34
+ }
35
+ body += '<span class="ui-collection__content"><span class="ui-collection__title">' + escape_html(child.title) + '</span></span>'
36
+ return body
37
+ }
38
+
28
39
  function layoutDiv() {
29
40
  if (!dropdownItem.icon || items.length === 0) {
30
41
  return ''
@@ -35,25 +46,26 @@ function layoutDiv() {
35
46
  el += ' direction="' + escape_html(dropdownDirection) + '"'
36
47
  }
37
48
  el += '>'
38
- el += '<summary class="' + triggerClasses.join(' ') + '"'
49
+ el += '<summary class="' + triggerClasses + '"'
39
50
  if (title) {
40
51
  el += ' title="' + title + '" aria-label="' + title + '"'
41
52
  }
42
53
  el += '>'
43
- // dropdown 首次展开时需要立即显示图标,使用内联 SVG 避免 /js/icons.js 异步替换造成闪烁。
44
- el += icon(dropdownItem.icon, 'no-lazy', true)
54
+ // dropdown 首次展开时需要立即显示图标,使用内联 SVG 避免异步替换造成闪烁。
55
+ el += '<span class="ui-icon">' + icon(dropdownItem.icon, 'no-lazy', true) + '</span>'
45
56
  if (dropdownShowTitle && dropdownItem.title) {
46
57
  el += '<span>' + title + '</span>'
47
58
  }
48
59
  el += '</summary>'
49
- el += '<div class="dropdown-menu ui-collection" data-ui-surface="glass" data-layout="list" data-variant="nav" data-density="compact">'
60
+ el += '<div class="dropdown-menu ui-collection" data-layout="list" data-variant="nav" data-density="compact">'
50
61
  for (let child of items) {
51
- el += '<a class="dropdown-item ui-collection__item card-hover card-hover--spotlight"' + linkAttrs(child) + '>'
52
- if (child.icon) {
53
- el += '<span class="ui-collection__leading">' + icon(child.icon, 'no-lazy', true) + '</span>'
62
+ if (child.type === 'button') {
63
+ el += '<button type="button" class="' + ui_classes('dropdown-item', 'collectionItem') + '" onclick="' + escape_html(child.onclick) + '">'
64
+ el += itemContent(child) + '</button>'
65
+ } else {
66
+ el += '<a class="' + ui_classes('dropdown-item', 'collectionItem') + '"' + linkAttrs(child) + '>'
67
+ el += itemContent(child) + '</a>'
54
68
  }
55
- el += '<span class="ui-collection__content"><span class="ui-collection__title">' + escape_html(child.title) + '</span></span>'
56
- el += '</a>'
57
69
  }
58
70
  el += '</div></details>'
59
71
  return el
@@ -1,28 +1,22 @@
1
1
  <%
2
- function strip_wiki_title(title, wiki) {
3
- if (!wiki) { return title; }
4
- var t = String(title || '');
5
- if (t.toLowerCase() === String(wiki).toLowerCase()) { return ''; }
6
- // wiki 名中的空格/连字符在标题中可能写为另一种形态(如 cloud shell / cloud-shell)
7
- var flex = String(wiki).replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/[\s-]+/g, '[\\s-]*');
8
- var m = t.match(new RegExp('^' + flex + '[\\s-]*[::\\-](.*)$', 'i'));
9
- if (m && m[1]) {
10
- return m[1].trim();
11
- }
12
- return t;
2
+ var pageViewModel = typeof viewModel !== 'undefined' && ['post', 'wiki', 'topic', 'notebook'].includes(viewModel?.collection?.profile)
3
+ ? viewModel
4
+ : null
5
+ var pageRender = pageViewModel?.render
6
+ var pageConfig = content_config(page)
7
+ var stellarSeo = {
8
+ canonical: stellar_config('canonical'),
9
+ openGraph: stellar_config('openGraph'),
10
+ structuredData: stellar_config('structuredData')
11
+ }
12
+ var stellarResources = {
13
+ preconnect: stellar_config('preconnect'),
14
+ fallbacks: stellar_config('fallbacks')
13
15
  }
14
16
 
15
17
  function generate_title() {
16
- if (page.wiki) {
17
- const proj = theme.wiki.tree[page.wiki]
18
- const wiki = proj?.name || page.wiki
19
- if (page.title) {
20
- var stripped = strip_wiki_title(page.title, wiki)
21
- if (stripped) {
22
- return wiki + __('symbol.colon') + stripped + ' - ' + config.title;
23
- }
24
- }
25
- return wiki + ' - ' + config.title;
18
+ if (pageRender) {
19
+ return pageRender.seo.title
26
20
  } else {
27
21
  if (page.title) {
28
22
  return page.title + ' - ' + config.title;
@@ -38,25 +32,27 @@ function generate_title() {
38
32
  return config.title;
39
33
  }
40
34
  function generate_description() {
41
- if (theme.open_graph && theme.open_graph.enable) {
35
+ if (pageRender) {
36
+ if (pageRender.seo.openGraph) return ''
37
+ return '<meta name="description" content="' + escape_meta_content(pageRender.seo.description) + '">'
38
+ }
39
+ if (stellarSeo.openGraph.enabled) {
42
40
  return '';
43
41
  }
44
42
  if (page.description) {
45
43
  return '<meta name="description" content="' + truncate(strip_html(page.description), {length: 150}) + '">';
46
44
  }
47
- if (page.wiki) {
48
- const proj = theme.wiki.tree[page.wiki]
49
- if (proj?.description) {
50
- return '<meta name="description" content="' + proj.description + '">'
51
- }
52
- }
53
45
  if (page.excerpt || page.content) {
54
46
  return '<meta name="description" content="' + truncate(strip_html(page.excerpt || page.content), {length: 150}) + '">';
55
47
  }
56
48
  return '<meta name="description" content="' + config.description + '">';
57
49
  }
58
50
  function generate_keywords() {
59
- if (page.keywords && page.keywords.length > 0) {
51
+ if (pageRender) {
52
+ return pageRender.seo.keywords.length > 0
53
+ ? '<meta name="keywords" content="' + escape_meta_content(pageRender.seo.keywords.join(',')) + '">'
54
+ : ''
55
+ } else if (page.keywords && page.keywords.length > 0) {
60
56
  return '<meta name="keywords" content="' + page.keywords + '">';
61
57
  } else if (page.tags && page.tags.length > 0) {
62
58
  return '<meta name="keywords" content="' + page.tags.map(tag => tag.name).join(',') + '">';
@@ -68,6 +64,11 @@ function generate_keywords() {
68
64
  }
69
65
 
70
66
  function generate_robots() {
67
+ if (pageRender) {
68
+ return pageRender.seo.robots
69
+ ? '<meta name="robots" content="' + escape_meta_content(pageRender.seo.robots) + '">'
70
+ : ''
71
+ }
71
72
  const IS_BACKUP = process.env.IS_BACKUP === 'true';
72
73
  if (IS_BACKUP) {
73
74
  return '<meta name="robots" content="noindex, nofollow">';
@@ -80,21 +81,18 @@ function generate_robots() {
80
81
  }
81
82
  }
82
83
  function og_args() {
84
+ if (pageRender) {
85
+ return Object.assign({}, pageRender.seo.openGraph?.args || {})
86
+ }
83
87
  var args = {};
84
- if (theme.open_graph.twitter_id) {
85
- args.twitter_id = theme.open_graph.twitter_id;
88
+ if (stellarSeo.openGraph.twitterId) {
89
+ args.twitter_id = stellarSeo.openGraph.twitterId;
86
90
  }
87
- if (page.layout == 'post' && page.cover) {
91
+ if (page.layout == 'post' && pageConfig.cover) {
88
92
  args.twitter_card = 'summary_large_image';
89
93
  }
90
- if (page.wiki && !page.description) {
91
- const proj = theme.wiki.tree[page.wiki];
92
- if (proj && proj.description) {
93
- args.description = proj.description;
94
- }
95
- }
96
- args.image = page.cover || page.banner || first_content_image(page.content) || config.avatar || (config.email ? gravatar(config.email) : null);
97
- return Object.assign(args, page.open_graph);
94
+ args.image = pageConfig.cover || pageConfig.banner?.image || first_content_image(page.content) || config.avatar || (config.email ? gravatar(config.email) : null);
95
+ return Object.assign(args, pageConfig.seo?.openGraph || {});
98
96
  }
99
97
 
100
98
  function escape_meta_content(content) {
@@ -106,29 +104,47 @@ function escape_meta_content(content) {
106
104
  }
107
105
 
108
106
  function generate_og_title() {
109
- return escape_meta_content(strip_html(page.title || config.title));
107
+ return escape_meta_content(strip_html(pageRender ? pageRender.seo.openGraph.title : page.title || config.title));
110
108
  }
111
109
 
112
110
  function generate_og_site_name() {
113
- return escape_meta_content(strip_html(config.title));
111
+ return escape_meta_content(strip_html(pageRender ? pageRender.seo.openGraph.siteName : config.title));
112
+ }
113
+
114
+ function generate_twitter_title() {
115
+ return escape_meta_content(strip_html(pageRender ? pageRender.seo.openGraph.twitterTitle : page.title || config.title));
114
116
  }
115
117
 
116
118
  function render_open_graph() {
117
- if (!(theme.open_graph && theme.open_graph.enable)) {
119
+ if (pageRender ? !pageRender.seo.openGraph : !stellarSeo.openGraph.enabled) {
118
120
  return '';
119
121
  }
120
122
  let meta = open_graph(og_args());
123
+ if (pageRender) {
124
+ meta = meta.replace(/<meta property="article:tag" content="[^"]*">\n?/g, '');
125
+ var articleMeta = ''
126
+ if (pageRender.seo.openGraph.publishedTime) {
127
+ articleMeta += `<meta property="article:published_time" content="${escape_meta_content(pageRender.seo.openGraph.publishedTime)}">\n`
128
+ }
129
+ if (pageRender.seo.openGraph.modifiedTime) {
130
+ articleMeta += `<meta property="article:modified_time" content="${escape_meta_content(pageRender.seo.openGraph.modifiedTime)}">\n`
131
+ }
132
+ for (var tag of pageRender.seo.openGraph.tags) {
133
+ articleMeta += `<meta property="article:tag" content="${escape_meta_content(tag)}">\n`
134
+ }
135
+ meta = meta.replace(/<meta name="twitter:card"/, `${articleMeta}<meta name="twitter:card"`)
136
+ }
121
137
  meta = meta.replace(/<meta property="og:title" content="[^"]*">/, `<meta property="og:title" content="${generate_og_title()}">`);
122
138
  if (meta.match(/<meta property="og:site_name" content="[^"]*">/)) {
123
139
  meta = meta.replace(/<meta property="og:site_name" content="[^"]*">/, `<meta property="og:site_name" content="${generate_og_site_name()}">`);
124
140
  } else {
125
141
  meta += `<meta property="og:site_name" content="${generate_og_site_name()}">`;
126
142
  }
127
- if (page.title) {
143
+ if (pageRender || page.title) {
128
144
  if (meta.match(/<meta name="twitter:title" content="[^"]*">/)) {
129
- meta = meta.replace(/<meta name="twitter:title" content="[^"]*">/, `<meta name="twitter:title" content="${generate_og_title()}">`);
145
+ meta = meta.replace(/<meta name="twitter:title" content="[^"]*">/, `<meta name="twitter:title" content="${generate_twitter_title()}">`);
130
146
  } else {
131
- meta += `<meta name="twitter:title" content="${generate_og_title()}">`;
147
+ meta += `<meta name="twitter:title" content="${generate_twitter_title()}">`;
132
148
  }
133
149
  }
134
150
  return meta;
@@ -136,34 +152,29 @@ function render_open_graph() {
136
152
 
137
153
  function preconnect() {
138
154
  var el = '';
139
- for (let href of (theme.preconnect || [])) {
155
+ for (var href of stellarResources.preconnect) {
140
156
  el += `<link rel="preconnect" href="${href}" crossorigin>`;
141
157
  }
142
158
  return el;
143
159
  }
144
160
 
145
- function custom_inject() {
146
- var el = '';
147
- for (let item of (config.inject?.head || [])) {
148
- el += item;
149
- }
150
- for (let item of (theme.inject?.head || [])) {
151
- el += item;
152
- }
153
- for (let item of (page.inject?.head || [])) {
154
- el += item;
155
- }
156
- return el;
161
+ function custom_inject(kind) {
162
+ return stellar_inject(kind, pageRender ? pageRender.document[`${kind}Inject`] : pageConfig.inject?.[kind])
157
163
  }
158
164
 
159
165
  function generate_canonical() {
160
- const originalHost = theme.canonical.originalHost || '';
161
- if (originalHost.length > 0) {
166
+ if (pageRender) {
167
+ return pageRender.seo.canonical
168
+ ? `<link rel="canonical" href="${escape_meta_content(pageRender.seo.canonical)}">`
169
+ : ''
170
+ }
171
+ var host = stellarSeo.canonical.host;
172
+ if (typeof host === 'string' && host.length > 0) {
162
173
  var path = pretty_url(page.path) || '/';
163
174
  if (path.startsWith('/404') || path.startsWith('404')) {
164
175
  return '';
165
176
  }
166
- return `<link rel="canonical" href="https://${originalHost}${path}">`;
177
+ return `<link rel="canonical" href="https://${host}${path}">`;
167
178
  }
168
179
  return '';
169
180
  }
@@ -171,6 +182,7 @@ function generate_canonical() {
171
182
  %>
172
183
 
173
184
  <head>
185
+ <%- custom_inject('headBegin') %>
174
186
  <meta charset="utf-8">
175
187
  <meta name="hexo-theme" content="<%- stellar_info('tree') %>" theme-name="<%- stellar_info('name') %>" theme-version="<%- stellar_info('version') %>">
176
188
  <%- generate_canonical() %>
@@ -187,6 +199,18 @@ function generate_canonical() {
187
199
  <meta name="theme-color" media="(prefers-color-scheme: dark)" content="#000">
188
200
  <meta name="theme-color" content="#f9fafb">
189
201
  <title><%- generate_title() %></title>
202
+ <script>
203
+ (function () {
204
+ var key = 'stellar:v2:leftbar-state';
205
+ var fallback = <%- JSON.stringify(stellar_config('leftbar.defaultState')) %>;
206
+ try {
207
+ var value = localStorage.getItem(key);
208
+ document.documentElement.dataset.leftbarState = value === 'collapsed' || value === 'expanded' ? value : fallback;
209
+ } catch (error) {
210
+ document.documentElement.dataset.leftbarState = fallback;
211
+ }
212
+ })();
213
+ </script>
190
214
 
191
215
  <%- render_open_graph() %>
192
216
  <%- generate_description() %>
@@ -208,8 +232,9 @@ function generate_canonical() {
208
232
  <% }) %>
209
233
  <% } %>
210
234
 
211
- <link rel="preload" href="<%- url_for(`${theme.stellar.main_css}?v=${stellar_info('version')}`) %>" as="style">
212
- <link rel="stylesheet" href="<%- url_for(`${theme.stellar.main_css}?v=${stellar_info('version')}`) %>">
235
+ <link rel="preload" href="<%- url_for(`${stellar_info('main_css')}?v=${stellar_info('version')}`) %>" as="style">
236
+ <link rel="stylesheet" href="<%- url_for(`${stellar_info('main_css')}?v=${stellar_info('version')}`) %>">
237
+ <style>:root{--text-loading:<%- JSON.stringify(__('message.loading')) %>;--text-loading-error:<%- JSON.stringify(__('message.loading_failed')) %>}</style>
213
238
 
214
239
 
215
240
  <% if (config.favicon) { %>
@@ -217,11 +242,13 @@ function generate_canonical() {
217
242
  <% } %>
218
243
 
219
244
  <% if (config.highlight && config.highlight.enable == true && config.highlight.hljs == true) { %>
220
- <%- css(theme.style.codeblock.highlightjs_theme) %>
245
+ <% if (stellar_config('appearance.codeBlock.highlightStylesheet')) { %>
246
+ <%- css(stellar_config('appearance.codeBlock.highlightStylesheet')) %>
247
+ <% } %>
221
248
  <% } %>
222
249
 
223
- <%- json_ld() %>
224
- <%- custom_inject() %>
250
+ <%- json_ld(pageViewModel || undefined) %>
251
+ <%- custom_inject('headEnd') %>
225
252
 
226
253
  <!-- icons.yml 驱动的 CSS 装饰图标 -->
227
254
  <%
@@ -234,11 +261,11 @@ function generate_canonical() {
234
261
  };
235
262
  let vars = [];
236
263
  for (const [name, key] of Object.entries(map)) {
237
- const raw = theme.icons && theme.icons[key];
264
+ var raw = stellar_data('icons') && stellar_data('icons')[key];
238
265
  if (raw) vars.push(` ${name}: url("data:image/svg+xml,${encodeURIComponent(raw)}");`);
239
266
  }
240
267
  // 加载占位:由 icons.yml 的 default:loading 提供(内联 SVG 编码为 data URI,URL 直用)
241
- const loadingRaw = theme.icons && theme.icons['default:loading'];
268
+ var loadingRaw = stellar_data('icons') && stellar_data('icons')['default:loading'];
242
269
  if (loadingRaw) {
243
270
  if (loadingRaw.trim().startsWith('<svg')) {
244
271
  vars.push(` --icon-loading: url("data:image/svg+xml,${encodeURIComponent(loadingRaw)}");`);
@@ -1,30 +1,33 @@
1
1
  <%
2
2
  function parseURL() {
3
3
  const source = page.source || ''
4
- for (let id of Object.keys(map)) {
5
- if (source.startsWith(id) && map[id]) {
6
- return source.replace(id, map[id])
7
- }
4
+ const matched = (repositories || [])
5
+ .filter(item => source.startsWith(item.sourcePrefix))
6
+ .sort((left, right) => right.sourcePrefix.length - left.sourcePrefix.length)[0]
7
+ if (!matched) return null
8
+ const file = source.slice(matched.sourcePrefix.length).replace(/^\/+/, '')
9
+ return {
10
+ edit: `https://github.com/${matched.repository}/blob/${matched.branch || 'main'}/${file}`,
11
+ commits: `${stellar_config('services.github.apiUrl').replace(/\/+$/, '')}/repos/${matched.repository}/commits?path=${encodeURIComponent(file)}`
8
12
  }
9
13
  }
10
14
  function layoutDiv() {
11
- const url = parseURL()
12
- if (url == null) {
15
+ const urls = parseURL()
16
+ if (urls == null) {
13
17
  return ''
14
18
  }
15
- const commits = url.replace('/github.com/', `/${theme.api_host.ghapi}/repos/`).replace('/blob/main/', '/commits?path=');
16
19
  var el = `
17
20
  <section id="contributors">
18
21
  <div class="header">
19
22
  <span>${__('meta.contributors')}</span>
20
23
  <span class="spacer"></span>
21
- <a class="edit" href="${url_for(url)}">
24
+ <a class="edit" href="${url_for(urls.edit)}">
22
25
  ${icon('default:edit')}
23
26
  <span>${__('btn.edit')}</span>
24
27
  </a>
25
28
  </div>
26
29
  <div class="body">
27
- <div class="ds-contributors users-wrap" api-type="commits" data-api="${commits}"><div class="grid-box"></div></div>
30
+ <div class="ds-contributors users-wrap" api-type="commits" data-api="${urls.commits}"><div class="grid-box"></div></div>
28
31
  </div>
29
32
  </section>
30
33
  `
@@ -0,0 +1,47 @@
1
+ <%
2
+ function referenceList(items) {
3
+ return items.map(function(ref) {
4
+ return `<li class="post-title">${markdown(ref)}</li>`
5
+ }).join('')
6
+ }
7
+
8
+ function contributorSection(contributor) {
9
+ if (!contributor) return ''
10
+ return `
11
+ <section id="contributors">
12
+ <div class="header">
13
+ <span>${__('meta.contributors')}</span>
14
+ <span class="spacer"></span>
15
+ <a class="edit" href="${url_for(contributor.editUrl)}">
16
+ ${icon('default:edit')}
17
+ <span>${__('btn.edit')}</span>
18
+ </a>
19
+ </div>
20
+ <div class="body">
21
+ <div class="ds-contributors users-wrap" api-type="commits" data-api="${escape_html(contributor.commitsUrl)}"><div class="grid-box"></div></div>
22
+ </div>
23
+ </section>`
24
+ }
25
+
26
+ function layoutDiv() {
27
+ var sections = ''
28
+ if (footer.references.length > 0) {
29
+ sections += `
30
+ <section id="references">
31
+ <div class="header"><span>${__('meta.references')}</span></div>
32
+ <div class="body"><ul>${referenceList(footer.references)}</ul></div>
33
+ </section>`
34
+ }
35
+ if (footer.license) {
36
+ sections += `
37
+ <section id="license">
38
+ <div class="header"><span>${__('meta.license')}</span></div>
39
+ <div class="body">${markdown(footer.license)}</div>
40
+ </section>`
41
+ }
42
+ sections += contributorSection(footer.contributor)
43
+ if (sections.length === 0) return ''
44
+ return `<div class="article-footer${scrollreveal(' ')}">${sections}</div>`
45
+ }
46
+ %>
47
+ <%- layoutDiv() %>
@@ -0,0 +1,22 @@
1
+ <%
2
+ function itemHtml(id, item, label) {
3
+ var el = `<div class="item" id="${id}">`
4
+ if (item) {
5
+ var title = item.title || (item.date ? date(item.date, config.date_format) : '')
6
+ el += `<div class="note">${label}</div>`
7
+ el += `<a href="${pretty_url(item.path)}">${escape_html(title)}</a>`
8
+ }
9
+ return el + '</div>'
10
+ }
11
+
12
+ function layoutDiv() {
13
+ if (!previous && !next) return ''
14
+ var prevLabel = typeof previousLabel !== 'undefined' ? previousLabel : __('meta.newer')
15
+ var nextLabelText = typeof nextLabel !== 'undefined' ? nextLabel : __('meta.older')
16
+ return `<div class="related-wrap${scrollreveal(' ')}" id="read-next"><section class="body">` +
17
+ itemHtml('prev', previous, prevLabel) +
18
+ itemHtml('next', next, nextLabelText) +
19
+ '</section></div>'
20
+ }
21
+ %>
22
+ <%- layoutDiv() %>
@@ -0,0 +1,14 @@
1
+ <%
2
+ function layoutDiv() {
3
+ if (!related.enabled || related.items.length === 0) return ''
4
+ var items = related.items.map(function(item) {
5
+ var excerpt = item.excerpt
6
+ ? `<span class="excerpt">${escape_html(truncate(strip_html(item.excerpt), {length: 120}))}</span>`
7
+ : ''
8
+ return `<a class="item" href="${escape_html(item.path)}" title="${escape_html(item.title)}"><span class="title">${escape_html(item.title)}</span>${excerpt}</a>`
9
+ }).join('')
10
+ var title = related.title || __('meta.related_posts')
11
+ return `<div class="related-wrap${scrollreveal(' ')}" id="related-posts"><section class="header"><div class="title cap theme">${escape_html(title)}</div></section><section class="body"><div class="related-posts">${items}</div></section></div>`
12
+ }
13
+ %>
14
+ <%- layoutDiv() %>
@@ -0,0 +1,12 @@
1
+ <%
2
+ function layoutDiv() {
3
+ if (!Array.isArray(tags) || tags.length === 0) return ''
4
+ var el = `<div class="article-tags${scrollreveal(' ')}">`
5
+ tags.forEach(function(tag) {
6
+ el += `<a class="tag" href="${pretty_url(tag.path)}">${icon('default:hashtag')}${escape_html(tag.name)}</a>`
7
+ })
8
+ el += '</div>'
9
+ return el
10
+ }
11
+ %>
12
+ <%- layoutDiv() %>