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
@@ -0,0 +1,84 @@
1
+ <%
2
+ var regionId = typeof region !== 'undefined' ? region : 'leftbar'
3
+ var regionState = typeof state === 'object' && state ? state : {}
4
+ var regionWidgets = Array.isArray(widgets) ? widgets : []
5
+ var activeViewModel = typeof viewModel !== 'undefined' ? viewModel : null
6
+
7
+ function renderWidget(widget, options) {
8
+ options = options || {}
9
+ var item = widget.item || {}
10
+ var body = ''
11
+ if (widget.system) {
12
+ if (widget.layout === 'menu') {
13
+ body = partial('../sidebar/menu', {
14
+ where: regionId,
15
+ menu: regionState.menu,
16
+ activeMenu: activeViewModel?.item.navigation.menu,
17
+ viewModel: activeViewModel
18
+ })
19
+ } else if (widget.layout === 'settings') {
20
+ body = partial('../widgets/settings', {item: item, viewModel: activeViewModel, placement: regionId})
21
+ } else if (widget.layout === 'spacer') {
22
+ body = '<span class="topbar-spacer" aria-hidden="true"></span>'
23
+ }
24
+ } else if (regionId === 'topbar' && widget.layout === 'toc') {
25
+ var tocContent = activeViewModel?.collection?.profile === 'wiki'
26
+ ? activeViewModel.item.content
27
+ : page.content
28
+ var tocBody = toc(tocContent || '', {
29
+ list_number: item.list_number,
30
+ min_depth: item.min_depth,
31
+ max_depth: item.max_depth
32
+ })
33
+ if (tocBody.trim().length > 0) {
34
+ tocBody = tocBody.replace('class="toc"', 'class="toc ui-collection-adapter"')
35
+ tocBody = tocBody.replace(/class="toc-link/g, 'class="' + ui_classes('toc-link', 'interactive'))
36
+ body = `<details class="topbar-toc"><summary class="${ui_classes('region-compact-action')}" aria-label="${escape_html(__('btn.toc'))}"><span class="ui-icon">${icon('default:rightbar', '', true)}</span></summary><div class="topbar-toc-popover">${tocBody}</div></details>`
37
+ }
38
+ } else {
39
+ body = partial('../widgets/' + widget.layout, {item: item, viewModel: activeViewModel})
40
+ }
41
+ if (!body || body.trim().length === 0) return ''
42
+ var rail = widget.presentations?.includes('leftbarRail') ? ' data-leftbar-rail' : ''
43
+ return `<div class="widget-instance widget-instance--${widget.layout}" data-widget-id="${escape_html(widget.instanceId)}" data-presentation="${widget.presentation}"${rail}>${body}</div>`
44
+ }
45
+ function stack(items, zone, options) {
46
+ if (!items.length) return ''
47
+ return `<div class="widgets widget-stack widget-stack--${regionId} widget-stack--${zone}">${items.map(function(widget) { return renderWidget(widget, options) }).join('')}</div>`
48
+ }
49
+ %>
50
+ <% if (regionId === 'topbar') {
51
+ var topbarBrandBody = regionState.brand === false ? '' : partial('../sidebar/brand', {
52
+ placement: 'topbar',
53
+ brandModel: regionState.brand,
54
+ viewModel: activeViewModel
55
+ })
56
+ %>
57
+ <% if (topbarBrandBody.trim().length > 0) { %><div class="widget-instance widget-instance--brand" data-widget-id="topbar:fixed:brand" data-presentation="topbar"><%- topbarBrandBody %></div><% } %>
58
+ <%- stack(regionWidgets, 'topbar') %>
59
+ <% } else if (regionId === 'leftbar') {
60
+ var leftbarBrandBody = regionState.brand === false ? '' : partial('../sidebar/brand', {
61
+ placement: 'leftbar',
62
+ brandModel: regionState.brand,
63
+ viewModel: activeViewModel
64
+ })
65
+ var menuItems = Array.isArray(regionState.menu) ? regionState.menu : []
66
+ var actionItems = Array.isArray(regionState.footer?.actions) ? regionState.footer.actions : []
67
+ %>
68
+ <header class="site-region__header site-region__zone site-region__zone--top">
69
+ <% if (leftbarBrandBody.trim().length > 0) { %><div class="widget-instance widget-instance--brand" data-widget-id="leftbar:fixed:brand" data-presentation="leftbar" data-leftbar-rail><%- leftbarBrandBody %></div><% } %>
70
+ </header>
71
+ <div class="site-region__body site-region__zone site-region__zone--body">
72
+ <% if (menuItems.length > 0) { %><div class="widget-instance widget-instance--menu" data-widget-id="leftbar:fixed:menu" data-presentation="leftbar" data-leftbar-rail><%- partial('../sidebar/menu', {where: 'leftbar', menu: menuItems, activeMenu: activeViewModel?.item.navigation.menu, viewModel: activeViewModel}) %></div><% } %>
73
+ <%- stack(regionWidgets, 'body') %>
74
+ </div>
75
+ <footer class="site-region__footer">
76
+ <% if (actionItems.length > 0) { %><div class="site-region__footer-actions"><div class="widget-instance widget-instance--actions" data-widget-id="leftbar:fixed:actions" data-presentation="leftbar"><%- partial('../widgets/actions', {actions: actionItems, viewModel: activeViewModel}) %></div></div><% } %>
77
+ <div class="site-region__system ui-collection" data-layout="inline" data-variant="nav" data-density="compact">
78
+ <div class="site-region__settings"><div class="widget-instance widget-instance--settings" data-widget-id="leftbar:fixed:settings" data-presentation="leftbar" data-leftbar-rail><%- partial('../widgets/settings', {viewModel: activeViewModel, placement: 'leftbar'}) %></div></div>
79
+ <button type="button" class="<%- ui_classes('leftbar-state-toggle', 'collectionItem', 'is-icon-only') %>" aria-label="Collapse leftbar" aria-expanded="true" data-shell-action="toggle-leftbar"><span class="ui-icon"><%- icon('default:leftbar-toggle', '', true) %></span></button>
80
+ </div>
81
+ </footer>
82
+ <% } else { %>
83
+ <%- stack(regionWidgets, regionId) %>
84
+ <% } %>
@@ -1,11 +1,24 @@
1
1
  <%
2
- const canonical = theme.canonical || {}
3
- const encoded = Buffer.from(canonical.originalHost || '').toString('base64');
4
- const canonicalWithEncoded = Object.assign({}, canonical, {
2
+ var canonical = stellar_config('canonical')
3
+ var encoded = Buffer.from(canonical.host || '').toString('base64');
4
+ var canonicalWithEncoded = Object.assign({}, canonical, {
5
5
  encoded: encoded
6
6
  });
7
- const permalink = page.permalink || canonical.originalHost;
8
- const checklink = theme.data_services.video.js
7
+ var permalink = page.permalink || canonical.host;
8
+ var extensions = {
9
+ search: stellar_config('search'),
10
+ comments: stellar_config('comments'),
11
+ tags: stellar_config('tags'),
12
+ features: stellar_config('features'),
13
+ services: stellar_config('services')
14
+ }
15
+ var assets = extension_assets()
16
+ var checklink = assets.services.video.js
17
+ var clientServices = JSON.parse(JSON.stringify(assets.services))
18
+ var siteInfoService = service_provider(extensions.services.siteInfo)
19
+ clientServices.siteinfo.api = siteInfoService ? siteInfoService.endpoint : null
20
+ var localSearch = extensions.search.local
21
+ var searchProvider = extensions.search.provider === 'local' ? 'local_search' : extensions.search.provider
9
22
  %>
10
23
 
11
24
  <script type="text/javascript">
@@ -45,55 +58,12 @@
45
58
  });
46
59
  observer.observe({ entryTypes: ['resource'] });
47
60
  } catch (e) {}
48
- // 统计进行中的 fetch/XHR 请求,异步组件(timeline 等)未完成前不判定布局稳定
49
- let pendingRequests = 0;
50
- const trackRequest = (fn) => {
51
- pendingRequests++;
52
- try {
53
- const result = fn();
54
- if (result && typeof result.finally === 'function') {
55
- result.finally(function () {
56
- pendingRequests = Math.max(0, pendingRequests - 1);
57
- });
58
- } else {
59
- pendingRequests = Math.max(0, pendingRequests - 1);
60
- }
61
- return result;
62
- } catch (e) {
63
- pendingRequests = Math.max(0, pendingRequests - 1);
64
- throw e;
65
- }
66
- };
67
- if (typeof window.fetch === 'function') {
68
- const origFetch = window.fetch.bind(window);
69
- window.fetch = function () {
70
- const args = arguments;
71
- return trackRequest(function () {
72
- return origFetch.apply(window, args);
73
- });
74
- };
75
- }
76
- if (window.XMLHttpRequest) {
77
- const origOpen = window.XMLHttpRequest.prototype.open;
78
- const origSend = window.XMLHttpRequest.prototype.send;
79
- window.XMLHttpRequest.prototype.open = function () {
80
- this.__anchorTracked = true;
81
- return origOpen.apply(this, arguments);
82
- };
83
- window.XMLHttpRequest.prototype.send = function () {
84
- if (this.__anchorTracked) {
85
- pendingRequests++;
86
- const done = () => {
87
- if (this.__anchorTracked) {
88
- this.__anchorTracked = false;
89
- pendingRequests = Math.max(0, pendingRequests - 1);
90
- }
91
- };
92
- this.addEventListener('loadend', done);
93
- }
94
- return origSend.apply(this, arguments);
95
- };
96
- }
61
+ // 只跟踪主题 request/cache 客户端,不替换浏览器原生 fetch/XHR。
62
+ const pendingRequests = new Set();
63
+ const onRequestStart = event => pendingRequests.add(event.detail.key);
64
+ const onRequestEnd = event => pendingRequests.delete(event.detail.key);
65
+ document.addEventListener('stellar:request-start', onRequestStart);
66
+ document.addEventListener('stellar:request-end', onRequestEnd);
97
67
  const cancel = () => {
98
68
  if (timer !== null) {
99
69
  clearInterval(timer);
@@ -104,6 +74,8 @@
104
74
  observer.disconnect();
105
75
  } catch (e) {}
106
76
  }
77
+ document.removeEventListener('stellar:request-start', onRequestStart);
78
+ document.removeEventListener('stellar:request-end', onRequestEnd);
107
79
  };
108
80
  const adjust = () => {
109
81
  const y = targetY();
@@ -118,7 +90,7 @@
118
90
  attempts++;
119
91
  const resourceQuiet = performance.now() - lastResourceTime > 3000;
120
92
  // 位置连续 1.8 秒无变化、无进行中的请求且最近 3 秒无资源加载才视为稳定;最长等待 30 秒
121
- if ((quiet >= 3 && pendingRequests === 0 && resourceQuiet) || attempts >= 50) {
93
+ if ((quiet >= 3 && pendingRequests.size === 0 && resourceQuiet) || attempts >= 50) {
122
94
  cancel();
123
95
  }
124
96
  };
@@ -150,38 +122,37 @@
150
122
  },
151
123
  root : `<%- config.root %>`,
152
124
  tag_plugins: {
153
- chat: Object.assign(<%- JSON.stringify(theme.tag_plugins.chat) %>),
125
+ chat: {api: <%- JSON.stringify(assets.services.chat.endpoint) %>},
154
126
  },
155
127
  // 图标由构建期生成的 /js/stellar-icons.js 填充(defer,早于 DOMContentLoaded 就绪)
156
128
  icons: {},
157
- services: <%- JSON.stringify(theme.data_services || {}) %>,
129
+ ui: { classes: <%- JSON.stringify(ui_capabilities()) %> },
130
+ services: <%- JSON.stringify(clientServices) %>,
158
131
  };
159
132
 
160
133
  // required plugins (only load if needs)
161
- if (`<%- theme.search.service %>`) {
134
+ if (`<%- searchProvider || '' %>`) {
162
135
  ctx.search = {};
163
- ctx.search.service = `<%- theme.search.service %>`;
136
+ ctx.search.service = `<%- searchProvider || '' %>`;
164
137
  if (ctx.search.service == 'local_search') {
165
- let service_obj = Object.assign({}, `<%- JSON.stringify(theme.search.local_search) %>`);
138
+ let service_obj = <%- JSON.stringify({
139
+ field: localSearch.scope,
140
+ path: '/search.json',
141
+ content: localSearch.includeContent,
142
+ lazy_load: true,
143
+ cache_ttl: localSearch.cacheTtlSeconds
144
+ }) %>;
166
145
  ctx.search[ctx.search.service] = service_obj;
146
+ ctx.search.path = service_obj.path;
167
147
  }
168
148
  }
169
149
  const def = {
170
- avatar: `<%- theme.default.avatar %>`,
171
- cover: `<%- theme.default.cover %>`,
172
- data_cache: <%- JSON.stringify(theme.data_cache || {}) %>,
173
- };
174
- const deps = {
175
- marked: `<%- url_for(theme.dependencies.marked) %>`,
176
- lazyload: `<%- url_for(theme.dependencies.lazyload.js) %>`
177
- }
178
-
179
- // 主题切换提示文案(供 /js/theme.js 使用)
180
- window.__STELLAR_I18N__ = {
181
- light: `<%- __('message.theme_switched.light') %>`,
182
- dark: `<%- __('message.theme_switched.dark') %>`,
183
- auto: `<%- __('message.theme_switched.auto') %>`,
150
+ avatar: `<%- stellar_config('fallbacks.avatar') %>`,
151
+ cover: `<%- stellar_config('fallbacks.cover') %>`,
184
152
  };
153
+ // 经典脚本仍消费这些只读投影。
154
+ window.ctx = ctx;
155
+ window.def = def;
185
156
 
186
157
  // 搜索跳转高亮:URL 带 ?kw= 时按需加载,定位正文中的匹配词
187
158
  (function () {
@@ -189,8 +160,7 @@
189
160
  if (!window.location.search) return;
190
161
  if (!new URLSearchParams(window.location.search).has('kw')) return;
191
162
  document.addEventListener('DOMContentLoaded', function () {
192
- // utils 由 /js/utils.js 定义(同时暴露 window.utils);
193
- // 未就绪时由 bootstrap 队列兜底,此处按就绪状态判断
163
+ // utils 由前置 /js/utils.js 同步定义(同时暴露 window.utils)。
194
164
  if (typeof utils === 'object' && typeof utils.js === 'function') {
195
165
  utils.js('<%- url_for('/js/search/highlight.js') %>', { defer: true }).catch(function () {});
196
166
  }
@@ -0,0 +1,55 @@
1
+ <%
2
+ var runtimeViewModel = typeof viewModel !== 'undefined' ? viewModel : null
3
+ var runtimeRender = content_config(page).render || {}
4
+ var runtimeExtensions = {
5
+ search: stellar_config('search'),
6
+ comments: stellar_config('comments'),
7
+ tags: stellar_config('tags'),
8
+ features: stellar_config('features'),
9
+ services: stellar_config('services')
10
+ }
11
+ var runtimeAssets = extension_assets()
12
+ var runtimeMathProvider = runtimeRender.math || runtimeExtensions.features.math.provider
13
+ var runtimeCommentOverrides = null
14
+ var runtimeHomeComments = stellar_config('profiles.home.comments')
15
+ if (!runtimeViewModel && runtimeHomeComments && runtimeHomeComments !== false && is_home_first_page()) {
16
+ runtimeCommentOverrides = typeof runtimeHomeComments === 'object' ? runtimeHomeComments : {enabled: true}
17
+ }
18
+ var runtimeErrorComments = stellar_config('profiles.error.comments')
19
+ if (!runtimeViewModel && page.layout === '404' && runtimeErrorComments && runtimeErrorComments !== false) {
20
+ runtimeCommentOverrides = typeof runtimeErrorComments === 'object' ? runtimeErrorComments : {enabled: true}
21
+ }
22
+ var runtimeComments = runtimeViewModel?.render?.article?.comments || comments_model(page, runtimeCommentOverrides)
23
+ var runtimeProfile = runtimeViewModel?.collection?.profile
24
+ || (page.notebookIndex?.mode === 'notes' ? 'notebook' : page.layout || '')
25
+ var runtimeMessages = {
26
+ copy: {
27
+ label: __('btn.copy'),
28
+ copied: __('message.copied'),
29
+ denied: __('message.copy_denied'),
30
+ unsupported: __('message.copy_unsupported')
31
+ }
32
+ }
33
+ if (runtimeExtensions.features.colorSchemeSwitch.enabled) {
34
+ runtimeMessages.colorScheme = {
35
+ light: __('message.color_scheme_switched.light'),
36
+ dark: __('message.color_scheme_switched.dark'),
37
+ auto: __('message.color_scheme_switched.auto')
38
+ }
39
+ }
40
+ var runtimeJson = browser_runtime_manifest({
41
+ root: config.root,
42
+ extensions: runtimeExtensions,
43
+ assets: runtimeAssets,
44
+ render: runtimeRender,
45
+ comments: runtimeComments,
46
+ profile: runtimeProfile,
47
+ colorScheme: stellar_config('appearance.colorScheme'),
48
+ messages: runtimeMessages
49
+ })
50
+ %>
51
+ <% if (runtimeMathProvider === 'katex') { %>
52
+ <link rel="stylesheet" href="<%- url_for(runtimeAssets.features.katexCss) %>" integrity="sha384-//SZkxyB7axjCAopkAL1E1rve+ZSPKapD89Lo/lLhcsXR+zOYl5z6zJZEFXil+q0" crossorigin="anonymous">
53
+ <% } %>
54
+ <script type="application/json" id="stellar-runtime-config"><%- runtimeJson %></script>
55
+ <script type="module" src="<%- url_for(runtimeAssets.runtime.bootstrap) %>?v=<%- stellar_info('version') %>&runtime=1"></script>
@@ -1,31 +1,25 @@
1
1
  <%
2
+ var renderViewModel = typeof viewModel !== 'undefined' ? viewModel : null
2
3
 
3
4
  function custom_inject() {
4
- var el = '';
5
- for (let item of (config.inject?.script || [])) {
6
- el += item;
7
- }
8
- for (let item of (theme.inject?.script || [])) {
9
- el += item;
10
- }
11
- for (let item of (page.inject?.script || [])) {
12
- el += item;
13
- }
14
- return el;
5
+ return stellar_inject('bodyEnd', renderViewModel ? renderViewModel.render.document.bodyEndInject : content_config(page).inject?.bodyEnd)
15
6
  }
16
7
 
17
- // tagtree 脚本仅在与 tagtree 小部件渲染相同的条件下加载(见 _partial/sidebar/index_leftbar.ejs)
8
+ // tagtree 脚本仅在最终 Region 实例包含 tagtree 时加载。
18
9
  function tagtreeWidgetOnPage() {
19
- const leftbar = page && page.leftbar;
20
- if (!Array.isArray(leftbar)) {
10
+ var layout = renderViewModel?.render.layout
11
+ || page.notebookIndex?.collection?.layout
12
+ || {};
13
+ var regions = [layout.topbar, layout.leftbar, layout.rightbar];
14
+ var widgets = regions.flatMap(function (region) {
15
+ return Array.isArray(region?.widgets) ? region.widgets : []
16
+ })
17
+ if (!Array.isArray(widgets)) {
21
18
  return false;
22
19
  }
23
- return leftbar.some(function (w) {
24
- if (typeof w === 'string') {
25
- return w === 'tagtree';
26
- }
20
+ return widgets.some(function (w) {
27
21
  if (w && typeof w === 'object') {
28
- return w.override === 'tagtree' || w.layout === 'tagtree';
22
+ return w.id === 'tagtree' || w.layout === 'tagtree' || w.override === 'tagtree';
29
23
  }
30
24
  return false;
31
25
  });
@@ -34,36 +28,18 @@ function tagtreeWidgetOnPage() {
34
28
  %>
35
29
  <%- partial('scripts/defines') %>
36
30
 
37
- <%- partial('scripts/bootstrap') %>
38
-
39
- <!-- core: utils 必须同步加载(页尾内联插件片段在解析期即调用 utils.initPlugin) -->
31
+ <!-- core: 迁移期 DOM/资源兼容工具;Extension 注册与生命周期由 ESM runtime 所有 -->
40
32
  <script src="<%- url_for('/js/utils.js') %>?v=<%- stellar_info('version') %>"></script>
41
- <script>
42
- // 解析期检测 utils 是否已就绪;未就绪时同步补载,
43
- // 保证「utils 先于页尾插件片段定义」的不变量。
44
- if (typeof utils === 'undefined') {
45
- document.write('<script src="<%- url_for('/js/utils.js') %>?v=<%- stellar_info('version') %>"><\/script>');
46
- }
47
- </script>
48
33
  <script defer src="<%- url_for('/js/stellar-icons.js') %>?v=<%- stellar_info('version') %>"></script>
49
- <!-- 非首屏图标异步加载:占位符替换依赖构建期生成的 js/icons/{ns}.json -->
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>
52
- <script src="<%- url_for(`${theme.stellar.main_js}?v=${stellar_info('version')}`) %>" defer></script>
53
- <script defer src="<%- url_for('/js/theme.js') %>?v=<%- stellar_info('version') %>"></script>
34
+ <script src="<%- url_for(`${stellar_info('main_js')}?v=${stellar_info('version')}`) %>" defer></script>
35
+ <script defer src="<%- url_for('/js/profile.js') %>?v=<%- stellar_info('version') %>"></script>
54
36
 
55
37
  <% if (tagtreeWidgetOnPage()) { %>
56
- <script>window.__STELLAR_TAGTREE__ = { expand_active: <%- JSON.stringify(!!(theme.widgets.tagtree && theme.widgets.tagtree.expand_active)) %> };</script>
38
+ <script>window.__STELLAR_TAGTREE__ = { expand_active: <%- JSON.stringify(!!(stellar_data('widgets').tagtree && stellar_data('widgets').tagtree.expand_active)) %> };</script>
57
39
  <script defer src="<%- url_for('/js/tagtree.js') %>?v=<%- stellar_info('version') %>"></script>
58
40
  <% } %>
59
41
 
60
- <%- partial('scripts/lazyload') %>
61
-
62
- <!-- optional -->
63
- <%- partial('comments/script') %>
64
-
65
- <script defer src="<%- url_for('/js/services.js') %>?v=<%- stellar_info('version') %>"></script>
66
- <%- partial('../_plugins/index') %>
42
+ <%- partial('scripts/runtime', {viewModel: typeof viewModel !== 'undefined' ? viewModel : null}) %>
67
43
 
68
44
  <!-- inject -->
69
45
  <%- custom_inject() %>
@@ -0,0 +1,33 @@
1
+ <%
2
+ var localSearch = stellar_config('search.provider') === 'local'
3
+ %>
4
+ <dialog class="search-dialog" id="site-search-dialog" aria-labelledby="site-search-title">
5
+ <div class="search-dialog__panel search-wrapper" id="site-search-wrapper" searching="false">
6
+ <div class="search-dialog__header">
7
+ <div class="search-form" role="search">
8
+ <span class="search-button" aria-hidden="true"><%- icon('default:search', '', true) %></span>
9
+ <input type="search" class="search-input" id="site-search-input" aria-labelledby="site-search-title" placeholder="<%- escape_html(__('btn.search')) %>">
10
+ </div>
11
+ <button type="button" class="search-dialog__close" data-shell-action="close-search" aria-label="<%- escape_html(__('search.close')) %>">×</button>
12
+ </div>
13
+ <% if (localSearch) { %>
14
+ <div class="search-dialog__scope" role="radiogroup" aria-labelledby="site-search-title" hidden>
15
+ <label class="search-dialog__scope-option" data-search-scope-option="all">
16
+ <input class="ui-radio" type="radio" name="site-search-scope" value="all">
17
+ <span><%- __('search.scope_all') %></span>
18
+ </label>
19
+ <label class="search-dialog__scope-option" data-search-scope-option="blog" hidden>
20
+ <input class="ui-radio" type="radio" name="site-search-scope" value="blog">
21
+ <span><%- __('search.scope_blog') %></span>
22
+ </label>
23
+ <label class="search-dialog__scope-option" data-search-scope-option="current" hidden>
24
+ <input class="ui-radio" type="radio" name="site-search-scope" value="current">
25
+ <span data-search-scope-current-label></span>
26
+ </label>
27
+ </div>
28
+ <% } %>
29
+ <h2 class="search-dialog__title" id="site-search-title"><%- __('search.search') %></h2>
30
+ <div class="search-result" id="site-search-result"></div>
31
+ <div class="search-no-result"><%- __('search.no_results') %></div>
32
+ </div>
33
+ </dialog>
@@ -0,0 +1 @@
1
+ <kbd class="search-shortcut" data-search-shortcut aria-hidden="true"></kbd>
@@ -0,0 +1,127 @@
1
+ <%
2
+ var brand = (typeof brandModel !== 'undefined' ? brandModel : null) || {}
3
+ var brandPlacement = typeof placement !== 'undefined' ? placement : 'leftbar'
4
+ var activeViewModel = typeof viewModel !== 'undefined' ? viewModel : null
5
+ var brandStyle = brandPlacement === 'topbar'
6
+ ? 'topbar'
7
+ : brand.style === 'compact' ? 'compact' : 'regular'
8
+
9
+ function layoutBrandNavigation() {
10
+ if (brandPlacement !== 'leftbar' || brand.source !== 'collection' || brand.backButton !== true) return ''
11
+ const href = brand.backHref
12
+ if (!href) return ''
13
+ return `<div class="brand-navigation"><a class="brand-navigation__back cap" href="${pretty_url(href)}"><span class="ui-icon">${icon('default:back', '', true)}</span><span>${__(brand.backLabel)}</span></a></div>`
14
+ }
15
+
16
+ function layoutBrandSearch() {
17
+ if (brandPlacement !== 'leftbar' || brand.source !== 'collection' || brand.search !== true) return ''
18
+ if (!stellar_config('search.provider')) return ''
19
+ var body = partial('_partial/sidebar/search', {
20
+ item: {algoliaFilterPath: 'auto'},
21
+ instanceId: 'collection-brand-search',
22
+ presentation: 'leftbar',
23
+ hoverSurface: true,
24
+ viewModel: activeViewModel
25
+ })
26
+ return body.trim().length > 0 ? `<div class="brand-search">${body}</div>` : ''
27
+ }
28
+
29
+ function layoutBrandTitle() {
30
+ const name = brandText(brand.name)
31
+ if (!name) return ''
32
+ const tagline = brandText(brand.tagline)
33
+ const href = brandText(brand.href)
34
+ const tag = href ? 'a' : 'div'
35
+ var el = `<${tag} class="brand-title"${href ? ` href="${escape_html(url_for(href))}"` : ''}>`
36
+ el += `<div class="brand-name">${escape_html(name)}</div>`
37
+ if (brandStyle !== 'topbar' && tagline) {
38
+ el += `<div class="brand-tagline cap">${escape_html(tagline)}</div>`
39
+ }
40
+ el += `</${tag}>`
41
+ return el
42
+ }
43
+
44
+ function layoutBrandImage() {
45
+ var image = brand.image
46
+ var src = brandText(image?.src)
47
+ if (!src) return ''
48
+ var href = brandText(brand.href)
49
+ var tag = href ? 'a' : 'div'
50
+ var variant = image?.variant || 'icon'
51
+ var el = `<${tag} class="brand-image brand-image--${variant}"${href ? ` href="${escape_html(url_for(href))}"` : ''}>`
52
+ if (src) {
53
+ el += `<img no-lazy src="${escape_html(src)}" alt="">`
54
+ } else {
55
+ el += icon('default:documents', '', true)
56
+ }
57
+ el += `</${tag}>`
58
+ return el
59
+ }
60
+
61
+ function layoutCollectionStats(apiBase) {
62
+ var repository = brandText(activeViewModel?.collection?.source?.repository)
63
+ if (!repository) return ''
64
+ var encodedRepository = repository.split('/').map(encodeURIComponent).join('/')
65
+ var githubHref = `https://github.com/${encodedRepository}`
66
+ var repoApi = `${apiBase}/repos/${encodedRepository}`
67
+ var items = [
68
+ {key: 'stargazers_count', label: 'stars', href: githubHref + '/stargazers'},
69
+ {key: 'forks_count', label: 'forks', href: githubHref + '/forks'},
70
+ {key: 'latest-tag-name', label: 'tag', href: githubHref + '/tags', api: repoApi + '/tags'}
71
+ ]
72
+ var el = `<div class="brand-stats data-service ds-ghinfo" data-api="${escape_html(repoApi)}">`
73
+ items.forEach(item => {
74
+ var classes = ui_classes(item.api ? 'brand-stat data-service ds-ghinfo' : 'brand-stat')
75
+ var service = item.api ? ` data-api="${escape_html(item.api)}" index="0"` : ''
76
+ el += `<a class="${classes}"${service} href="${escape_html(item.href)}">`
77
+ el += `<span class="brand-stat__value" type="text" id="${item.key}"></span>`
78
+ el += `<span class="brand-stat__label">${item.label}</span></a>`
79
+ })
80
+ el += `</div>`
81
+ return el
82
+ }
83
+
84
+ function layoutSiteStats(apiBase) {
85
+ var username = typeof brandGithubUsername === 'function' ? brandGithubUsername() : ''
86
+ if (!username) return ''
87
+ var encodedUsername = encodeURIComponent(username)
88
+ var githubHref = `https://github.com/${encodedUsername}`
89
+ var items = [
90
+ {key: 'followers', label: 'followers', href: githubHref + '?tab=followers'},
91
+ {key: 'following', label: 'following', href: githubHref + '?tab=following'},
92
+ {key: 'public_repos', label: 'repos', href: githubHref + '?tab=repositories'}
93
+ ]
94
+ var el = `<div class="brand-stats data-service ds-ghinfo" data-api="${escape_html(apiBase + '/users/' + encodedUsername)}">`
95
+ items.forEach(item => {
96
+ el += `<a class="${ui_classes('brand-stat')}" href="${escape_html(item.href)}">`
97
+ el += `<span class="brand-stat__value" type="text" id="${item.key}"></span>`
98
+ el += `<span class="brand-stat__label">${item.label}</span></a>`
99
+ })
100
+ el += `</div>`
101
+ return el
102
+ }
103
+
104
+ function layoutBrandGithub() {
105
+ if (brandPlacement !== 'leftbar' || brandStyle !== 'regular') return ''
106
+ var apiBase = brandText(stellar_config('services.github.apiUrl')).replace(/\/+$/, '')
107
+ if (!apiBase) return ''
108
+ return brand.source === 'collection'
109
+ ? layoutCollectionStats(apiBase)
110
+ : layoutSiteStats(apiBase)
111
+ }
112
+
113
+ function layoutBrand() {
114
+ if (!brand.image?.src && !brand.name) return ''
115
+ var el = `<header class="brand-header brand-header--${brandStyle}${brandPlacement === 'main' ? ' mobile-only' : ''}">`
116
+ el += layoutBrandNavigation()
117
+ el += `<div class="brand-wrap">`
118
+ el += layoutBrandImage()
119
+ el += layoutBrandTitle()
120
+ el += layoutBrandGithub()
121
+ el += `</div>`
122
+ el += layoutBrandSearch()
123
+ el += `</header>`
124
+ return el
125
+ }
126
+ %>
127
+ <%- layoutBrand() %>
@@ -1,7 +1,58 @@
1
1
  <%
2
2
  function layoutDiv() {
3
+ var menuViewModel = typeof viewModel !== 'undefined' ? viewModel : null
4
+ var menuItems = Array.isArray(menu) ? menu : []
5
+ var pageConfig = content_config(page)
6
+ var currentMenu = typeof activeMenu !== 'undefined'
7
+ ? activeMenu
8
+ : pageConfig.activeMenu ?? page.navigation?.menu
9
+ if (pageConfig.activeMenu == null) {
10
+ var pathMenu = menu_active_by_path(page.path, menuItems)
11
+ if (pathMenu != null) currentMenu = pathMenu
12
+ }
3
13
  var entries = []
4
- for (var menuItem of (theme.menubar.items || [])) {
14
+ for (var menuItem of menuItems) {
15
+ if (menuItem?.type === 'search') {
16
+ if (!stellar_config('search.provider')) continue
17
+ var searchProvider = stellar_config('search.provider')
18
+ var algoliaFilterPath = ''
19
+ var wikiViewModel = menuViewModel?.collection?.profile === 'wiki' ? menuViewModel : null
20
+ var notebookViewModel = menuViewModel?.collection?.profile === 'notebook' ? menuViewModel : null
21
+ var notebookIndex = page.notebookIndex?.mode === 'notes' ? page.notebookIndex.collection : null
22
+ if (wikiViewModel) {
23
+ algoliaFilterPath = wikiViewModel.render.layout.algoliaFilterPath || ''
24
+ } else if (notebookViewModel) {
25
+ algoliaFilterPath = notebookViewModel.render.layout.algoliaFilterPath || ''
26
+ } else if (notebookIndex) {
27
+ algoliaFilterPath = notebookIndex.layout.algoliaFilterPath || ''
28
+ }
29
+ if (algoliaFilterPath.length > 0 && !algoliaFilterPath.startsWith('/')) algoliaFilterPath = '/' + algoliaFilterPath
30
+ var searchScope = search_scope(page, menuViewModel)
31
+ var searchTitle = __('btn.search')
32
+ var searchAttrs = {
33
+ 'aria-label': searchTitle,
34
+ 'data-shell-action': 'open-search',
35
+ 'data-search-instance': 'menu-search'
36
+ }
37
+ if (searchProvider === 'algolia' && algoliaFilterPath.length > 0) {
38
+ searchAttrs['data-algolia-filter-path'] = algoliaFilterPath
39
+ }
40
+ if (searchScope) {
41
+ searchAttrs['data-search-domain'] = searchScope.current
42
+ searchAttrs['data-search-domain-label'] = searchScope.currentLabel
43
+ searchAttrs['data-search-domain-blog'] = searchScope.hasBlog ? 'true' : 'false'
44
+ }
45
+ entries.push({
46
+ type: 'button',
47
+ title: searchTitle,
48
+ icon: menuItem.icon || 'default:search',
49
+ theme: menuItem.accent,
50
+ className: menuItem.accent?.length > 0 ? 'search-trigger is-themed' : 'search-trigger',
51
+ after: partial('_partial/search/shortcut'),
52
+ attrs: searchAttrs
53
+ })
54
+ continue
55
+ }
5
56
  if (menuItem == null || menuItem.id == null || menuItem.url == null || menuItem.title == null) {
6
57
  continue
7
58
  }
@@ -9,21 +60,15 @@ function layoutDiv() {
9
60
  href: pretty_url(menuItem.url),
10
61
  title: __(menuItem.title),
11
62
  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
63
+ theme: menuItem.accent,
64
+ active: menuItem.id == currentMenu,
65
+ className: menuItem.accent?.length > 0 ? 'is-themed' : ''
16
66
  })
17
67
  }
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 ''
25
- }
26
- return `<nav class="menu dis-select${where == 'main' ? ' mobile-hidden' : ''}">${body}</nav>`
68
+ return partial('_partial/primitives/navigation', {
69
+ placement: ['main', 'topbar', 'rightbar'].includes(where) ? where : 'leftbar',
70
+ entries: entries
71
+ })
27
72
  }
28
73
  %>
29
74