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
package/source/js/main.js CHANGED
@@ -45,6 +45,26 @@ const util = {
45
45
  }
46
46
  },
47
47
 
48
+ share: async (button) => {
49
+ if (!button) return;
50
+ const data = {
51
+ title: button.dataset.shareTitle || '',
52
+ text: button.dataset.shareText || button.dataset.shareTitle || '',
53
+ url: button.dataset.shareUrl || ''
54
+ };
55
+ try {
56
+ const supported = typeof navigator.share === 'function'
57
+ && (typeof navigator.canShare !== 'function' || navigator.canShare(data));
58
+ if (supported) {
59
+ await navigator.share(data);
60
+ return;
61
+ }
62
+ } catch (error) {
63
+ if (error?.name === 'AbortError') return;
64
+ }
65
+ util.copy(button.dataset.copyTarget, button.dataset.copyMessage);
66
+ },
67
+
48
68
  toggle: (id) => {
49
69
  const el = document.getElementById(id);
50
70
  if (el) {
@@ -95,30 +115,260 @@ const hud = {
95
115
 
96
116
  // defines
97
117
 
98
- const l_body = document.querySelector('.l_body');
118
+ const siteShell = document.querySelector('.site-shell');
119
+ const leftbarStateKey = 'stellar:v2:leftbar-state';
120
+ const leftbarDrawerQuery = '(max-width: 768px)';
121
+ const leftbarHiddenQuery = '(max-width: 768px)';
122
+ const rightbarDrawerQuery = '(max-width: 1180px)';
123
+ let shellDrawerTrigger = null;
124
+ let searchDialogTrigger = null;
125
+ let searchDialogRestoreFocus = true;
126
+ let shellInputModality = 'pointer';
99
127
 
100
- const sidebar = {
101
- leftbar: () => {
102
- if (l_body) {
103
- l_body.toggleAttribute('leftbar');
104
- l_body.removeAttribute('rightbar');
105
- }
106
- },
107
- rightbar: () => {
108
- if (l_body) {
109
- l_body.toggleAttribute('rightbar');
110
- l_body.removeAttribute('leftbar');
111
- }
112
- },
113
- dismiss: () => {
114
- if (l_body) {
115
- l_body.removeAttribute('leftbar');
116
- l_body.removeAttribute('rightbar');
117
- }
118
- },
119
- toggleTOC: () => {
120
- document.querySelector('#data-toc').classList.toggle('collapse');
128
+ function regionElement(region) {
129
+ return document.getElementById(region + '-region');
130
+ }
131
+
132
+ function regionUsesDrawer(region) {
133
+ if (typeof window.matchMedia !== 'function') return false;
134
+ return window.matchMedia(region === 'leftbar' ? leftbarDrawerQuery : rightbarDrawerQuery).matches;
135
+ }
136
+
137
+ function regionHiddenWhenClosed(region) {
138
+ if (typeof window.matchMedia !== 'function') return false;
139
+ if (region === 'leftbar') return window.matchMedia(leftbarHiddenQuery).matches;
140
+ return regionUsesDrawer(region);
141
+ }
142
+
143
+ function setRegionInteractive(region, interactive) {
144
+ const element = regionElement(region);
145
+ if (!element) return;
146
+ const hidden = regionHiddenWhenClosed(region);
147
+ element.inert = hidden && !interactive;
148
+ if (hidden && !interactive) {
149
+ element.setAttribute('aria-hidden', 'true');
150
+ } else {
151
+ element.removeAttribute('aria-hidden');
152
+ }
153
+ }
154
+
155
+ function syncLeftbarControls() {
156
+ const drawer = regionUsesDrawer('leftbar');
157
+ const expanded = drawer
158
+ ? siteShell?.dataset.drawer === 'leftbar'
159
+ : document.documentElement.dataset.leftbarState !== 'collapsed';
160
+ document.querySelectorAll('[data-shell-action="toggle-leftbar"]').forEach(function (button) {
161
+ button.setAttribute('aria-expanded', String(expanded));
162
+ button.setAttribute('aria-label', drawer
163
+ ? (expanded ? 'Close leftbar' : 'Open leftbar')
164
+ : (expanded ? 'Collapse leftbar' : 'Expand leftbar'));
165
+ });
166
+ }
167
+
168
+ function syncDrawerControls() {
169
+ const openRegion = siteShell?.dataset.drawer || '';
170
+ ['leftbar', 'rightbar'].forEach(function (region) {
171
+ const open = openRegion === region && regionUsesDrawer(region);
172
+ document.querySelectorAll('[data-shell-action="toggle-' + region + '-drawer"]').forEach(function (button) {
173
+ button.setAttribute('aria-expanded', String(open));
174
+ button.setAttribute('aria-label', (open ? 'Close ' : 'Open ') + region);
175
+ });
176
+ setRegionInteractive(region, open || !regionUsesDrawer(region));
177
+ });
178
+ }
179
+
180
+ function focusRegion(region) {
181
+ const element = regionElement(region);
182
+ element?.querySelector('a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])')?.focus();
183
+ }
184
+
185
+ function dismissDrawer(options) {
186
+ if (!siteShell) return;
187
+ const restoreFocus = options?.restoreFocus !== false;
188
+ const wasOpen = !!siteShell.dataset.drawer;
189
+ delete siteShell.dataset.drawer;
190
+ syncDrawerControls();
191
+ syncLeftbarControls();
192
+ if (wasOpen && restoreFocus && shellDrawerTrigger?.focus) shellDrawerTrigger.focus();
193
+ if (wasOpen) shellDrawerTrigger = null;
194
+ }
195
+
196
+ function toggleDrawer(region, trigger) {
197
+ if (!siteShell || !regionUsesDrawer(region) || !regionElement(region)) return;
198
+ const open = siteShell.dataset.drawer === region;
199
+ if (open) {
200
+ dismissDrawer();
201
+ return;
202
+ }
203
+ shellDrawerTrigger = trigger || document.activeElement;
204
+ siteShell.dataset.drawer = region;
205
+ syncDrawerControls();
206
+ syncLeftbarControls();
207
+ focusRegion(region);
208
+ }
209
+
210
+ function toggleLeftbarState() {
211
+ const root = document.documentElement;
212
+ const state = root.dataset.leftbarState === 'collapsed' ? 'expanded' : 'collapsed';
213
+ root.dataset.leftbarState = state;
214
+ try { localStorage.setItem(leftbarStateKey, state); } catch (error) {}
215
+ syncLeftbarControls();
216
+ }
217
+
218
+ function toggleLeftbar(trigger) {
219
+ if (regionUsesDrawer('leftbar')) {
220
+ toggleDrawer('leftbar', trigger);
221
+ return;
222
+ }
223
+ toggleLeftbarState();
224
+ }
225
+
226
+ function searchDialogElement() {
227
+ return document.getElementById('site-search-dialog');
228
+ }
229
+
230
+ function searchScopeOption(dialog, value) {
231
+ return dialog?.querySelector('[data-search-scope-option="' + value + '"]');
232
+ }
233
+
234
+ function applySearchScope(dialog, value, refresh = true) {
235
+ const input = dialog?.querySelector('.search-input');
236
+ const group = dialog?.querySelector('.search-dialog__scope');
237
+ if (!input || !group) return;
238
+ const selected = ['all', 'blog', 'current'].includes(value) ? value : 'all';
239
+ const option = searchScopeOption(dialog, selected);
240
+ const radio = option?.querySelector('input[type="radio"]');
241
+ if (!option || option.hidden || !radio) return;
242
+
243
+ if (selected === 'current') {
244
+ input.dataset.domain = group.dataset.currentDomain || '';
245
+ } else if (selected === 'blog') {
246
+ input.dataset.domain = 'blog';
247
+ } else {
248
+ input.dataset.domain = '';
249
+ }
250
+ radio.checked = true;
251
+ if (refresh && input.value.trim().length > 0) {
252
+ input.dispatchEvent(new Event('input', { bubbles: true }));
253
+ }
254
+ }
255
+
256
+ function configureSearchScope(dialog, trigger) {
257
+ const group = dialog?.querySelector('.search-dialog__scope');
258
+ if (!group) return false;
259
+ const blogOption = searchScopeOption(dialog, 'blog');
260
+ const currentOption = searchScopeOption(dialog, 'current');
261
+ const currentLabel = currentOption?.querySelector('[data-search-scope-current-label]');
262
+ const currentDomain = trigger?.dataset?.searchDomain || '';
263
+ const hasBlog = trigger?.dataset?.searchDomainBlog === 'true';
264
+ const hasCurrent = currentDomain.length > 0 && currentDomain !== 'blog';
265
+
266
+ blogOption.hidden = !hasBlog;
267
+ currentOption.hidden = !hasCurrent;
268
+ if (currentLabel) currentLabel.textContent = trigger?.dataset?.searchDomainLabel || '';
269
+ group.dataset.currentDomain = currentDomain;
270
+ group.hidden = 1 + Number(hasBlog) + Number(hasCurrent) < 2;
271
+
272
+ const selected = hasCurrent ? 'current' : (hasBlog ? 'blog' : 'all');
273
+ applySearchScope(dialog, selected, false);
274
+ return true;
275
+ }
276
+
277
+ function closeSearch() {
278
+ const dialog = searchDialogElement();
279
+ if (!dialog?.open) return;
280
+ dialog.close();
281
+ document.documentElement.removeAttribute('data-search-open');
282
+ if (searchDialogRestoreFocus && searchDialogTrigger?.focus) searchDialogTrigger.focus();
283
+ else if (searchDialogTrigger?.blur) searchDialogTrigger.blur();
284
+ searchDialogTrigger = null;
285
+ searchDialogRestoreFocus = true;
286
+ }
287
+
288
+ function openSearch(trigger, restoreFocus = true) {
289
+ const dialog = searchDialogElement();
290
+ const input = dialog?.querySelector('.search-input');
291
+ const wrapper = dialog?.querySelector('.search-wrapper');
292
+ const result = dialog?.querySelector('.search-result');
293
+ if (!dialog || !input) return;
294
+ searchDialogTrigger = trigger || document.activeElement;
295
+ searchDialogRestoreFocus = restoreFocus;
296
+ input.value = '';
297
+ if (!configureSearchScope(dialog, trigger)) {
298
+ input.dataset.algoliaFilterPath = trigger?.dataset?.algoliaFilterPath || '';
299
+ }
300
+ if (wrapper) {
301
+ wrapper.setAttribute('searching', 'false');
302
+ wrapper.classList.remove('noresult');
121
303
  }
304
+ result?.replaceChildren();
305
+ if (!dialog.open) dialog.showModal();
306
+ document.documentElement.setAttribute('data-search-open', '');
307
+ input.focus();
308
+ }
309
+
310
+ function toggleToc(trigger) {
311
+ const widget = document.querySelector('#data-toc');
312
+ if (!widget) return;
313
+ const collapsed = widget.classList.toggle('collapse');
314
+ trigger?.classList.toggle('is-active', collapsed);
315
+ trigger?.setAttribute('aria-pressed', String(collapsed));
316
+ }
317
+
318
+ const shellActions = {
319
+ 'toggle-leftbar-drawer': function (trigger) { toggleDrawer('leftbar', trigger); },
320
+ 'toggle-rightbar-drawer': function (trigger) { toggleDrawer('rightbar', trigger); },
321
+ 'dismiss-drawer': function () { dismissDrawer(); },
322
+ 'toggle-leftbar': function (trigger) { toggleLeftbar(trigger); },
323
+ 'open-search': function (trigger) { openSearch(trigger, shellInputModality !== 'pointer'); },
324
+ 'close-search': function () { closeSearch(); },
325
+ 'toggle-toc': function (trigger) { toggleToc(trigger); }
326
+ };
327
+
328
+ syncLeftbarControls();
329
+ syncDrawerControls();
330
+ document.addEventListener('pointerdown', function () {
331
+ shellInputModality = 'pointer';
332
+ });
333
+ document.addEventListener('click', function (event) {
334
+ const trigger = event.target?.closest?.('[data-shell-action]');
335
+ const action = trigger?.dataset?.shellAction;
336
+ if (!action || typeof shellActions[action] !== 'function') return;
337
+ event.preventDefault();
338
+ shellActions[action](trigger);
339
+ });
340
+ document.addEventListener('keydown', function (event) {
341
+ shellInputModality = 'keyboard';
342
+ if (event.key === 'Escape') {
343
+ if (searchDialogElement()?.open) closeSearch();
344
+ else dismissDrawer();
345
+ }
346
+ });
347
+ const searchDialog = searchDialogElement();
348
+ searchDialog?.addEventListener('cancel', function (event) {
349
+ event.preventDefault();
350
+ closeSearch();
351
+ });
352
+ searchDialog?.addEventListener('click', function (event) {
353
+ if (event.target === searchDialog) closeSearch();
354
+ });
355
+ searchDialog?.addEventListener('change', function (event) {
356
+ if (event.target?.name !== 'site-search-scope') return;
357
+ applySearchScope(searchDialog, event.target.value);
358
+ });
359
+ if (typeof window.matchMedia === 'function') {
360
+ [leftbarDrawerQuery, leftbarHiddenQuery, rightbarDrawerQuery].filter(function (query, index, queries) {
361
+ return queries.indexOf(query) === index;
362
+ }).map(function (query) {
363
+ return window.matchMedia(query);
364
+ }).forEach(function (media) {
365
+ const handleChange = function () {
366
+ dismissDrawer({ restoreFocus: false });
367
+ syncDrawerControls();
368
+ };
369
+ if (typeof media.addEventListener === 'function') media.addEventListener('change', handleChange);
370
+ else if (typeof media.addListener === 'function') media.addListener(handleChange);
371
+ });
122
372
  }
123
373
 
124
374
  // 通用平滑滚动(自定义动画,TOC / 回到顶部 / 参与讨论共用)
@@ -188,7 +438,7 @@ function rebuildToc(scope) {
188
438
  const li = document.createElement('li');
189
439
  li.className = 'toc-item toc-level-' + level;
190
440
  const a = document.createElement('a');
191
- a.className = 'toc-link';
441
+ a.className = 'toc-link ' + ctx.ui.classes.interactive;
192
442
  a.href = '#' + encodeURIComponent(id);
193
443
  const span = document.createElement('span');
194
444
  span.className = 'toc-text';
@@ -339,7 +589,7 @@ const init = {
339
589
  }, 50);
340
590
  });
341
591
  },
342
- sidebar: () => {
592
+ tocLinks: () => {
343
593
  utils.dom("#data-toc a.toc-link").click(function (e) {
344
594
  const href = this.getAttribute("href");
345
595
  const id = href && href.indexOf("#") === 0 ? decodeURIComponent(href.slice(1)) : null;
@@ -353,11 +603,11 @@ const init = {
353
603
  window.history.pushState(null, "", href);
354
604
  }
355
605
  }
356
- window.sidebar.dismiss();
606
+ dismissDrawer();
357
607
  });
358
608
  },
359
609
  wikiStart: () => {
360
- utils.dom('#l_cover .l_cover.wiki .start-wrap a.button.start').click(function (e) {
610
+ utils.dom('#site-cover .cover-content.wiki .start-wrap a.button.start').click(function (e) {
361
611
  const href = this.getAttribute("href");
362
612
  const id = href && href.indexOf("#") === 0 ? decodeURIComponent(href.slice(1)) : null;
363
613
  const target = id && document.getElementById(id);
@@ -413,17 +663,9 @@ const init = {
413
663
  }
414
664
  });
415
665
 
416
- const galaxyCanvases = document.querySelectorAll('.wiki-cover-background.galaxy canvas');
417
- const reduceMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
418
- if (reduceMotion || galaxyCanvases.length === 0) return;
419
- utils.js('/js/plugins/galaxy.js').then(function () {
420
- if (stellar.galaxy && typeof stellar.galaxy.mountAll === 'function') {
421
- stellar.galaxy.mountAll(galaxyCanvases);
422
- }
423
- }).catch(function () {});
424
666
  },
425
667
  leftbarScroll: () => {
426
- const container = document.querySelector('.l_left .widgets');
668
+ const container = document.querySelector('.site-region--leftbar .site-region__body');
427
669
  if (container == null) {
428
670
  return;
429
671
  }
@@ -438,8 +680,8 @@ const init = {
438
680
  if (notebookEl != null) {
439
681
  return 'notebook:' + encode(notebookEl.getAttribute('data-notebook'));
440
682
  }
441
- const body = document.querySelector('.l_body');
442
- return 'layout:' + encode((body && body.getAttribute('layout')) || 'default');
683
+ const body = document.body;
684
+ return 'layout:' + encode((body && body.dataset.pageLayout) || 'default');
443
685
  }
444
686
  window.addEventListener('pagehide', function () {
445
687
  try {
@@ -475,16 +717,16 @@ const init = {
475
717
  }
476
718
  } catch (e) {}
477
719
  },
478
- navbarPin: () => {
479
- // 列表页 navbar top 背景条状态切换:未滚动/未吸顶为卡片样式(var(--card) + 文章卡片同款阴影),
480
- // 页面滚动达到阈值且吸顶后恢复玻璃效果。在吸顶边界切换 .pinned 类,视觉由 CSS 控制。
481
- // 吸顶判定直接测 navbar 的实际视口位置,而非用 scrollY 推算:
720
+ listingNavPin: () => {
721
+ // Listing Nav 在吸顶边界切换 .is-pinned 类,视觉由 CSS 控制。
722
+ // 页面有 Topbar 时,pinned Listing Nav 进入 Topbar 内并复用其表面;无 Topbar 时保持独立容器外观。
723
+ // 吸顶判定直接测 Listing Nav 的实际视口位置,而非用 scrollY 推算:
482
724
  // 移动端浏览器顶栏伸缩会改变 scrollY(展开顶栏时 scrollY 减小),
483
- // 即使 navbar 仍吸顶也可能跌破阈值,导致玻璃效果误消失。
484
- // 无轮播区页面(如 wiki)的 navbar 在页面顶部即已吸顶,需额外要求页面实际滚动达到阈值,
725
+ // 即使 Listing Nav 仍吸顶也可能跌破阈值,导致玻璃效果误消失。
726
+ // 无轮播区页面(如 wiki)的 Listing Nav 在页面顶部即已吸顶,需额外要求页面实际滚动达到阈值,
485
727
  // 否则默认保持卡片样式;回到顶部(滚动小于阈值)恢复卡片。
486
- const navbars = document.querySelectorAll('.navbar.top');
487
- if (navbars.length === 0) {
728
+ const listingNavs = document.querySelectorAll('.listing-nav');
729
+ if (listingNavs.length === 0) {
488
730
  return;
489
731
  }
490
732
  // 视口顶部允许的偏差(px),吸收亚像素/取整误差
@@ -495,22 +737,22 @@ const init = {
495
737
  function update() {
496
738
  const scrolled = window.scrollY >= SCROLL_THRESHOLD;
497
739
  states.forEach((state) => {
498
- const top = state.navbar.getBoundingClientRect().top;
499
- state.bar.classList.toggle('pinned', scrolled && top <= state.stickyTop + TOLERANCE);
740
+ const top = state.listingNav.getBoundingClientRect().top;
741
+ state.surface.classList.toggle('is-pinned', scrolled && top <= state.stickyTop + TOLERANCE);
500
742
  });
501
743
  }
502
744
  function measure() {
503
745
  states = [];
504
- navbars.forEach((navbar) => {
505
- const bar = navbar.querySelector('.navbar-blur');
506
- if (bar == null) {
746
+ listingNavs.forEach((listingNav) => {
747
+ const surface = listingNav.querySelector('.listing-nav__surface');
748
+ if (surface == null) {
507
749
  return;
508
750
  }
509
751
  // getComputedStyle().top 自动兼容桌面 var(--gap-page) 与移动端 8pt
510
- const stickyTop = parseFloat(getComputedStyle(navbar).top) || 16;
752
+ const stickyTop = parseFloat(getComputedStyle(listingNav).top) || 16;
511
753
  states.push({
512
- navbar: navbar,
513
- bar: bar,
754
+ listingNav: listingNav,
755
+ surface: surface,
514
756
  stickyTop: stickyTop
515
757
  });
516
758
  });
@@ -579,9 +821,9 @@ const init = {
579
821
  // 真实主站域名优先从 encoded(base64)反解,避免被「批量替换域名」的克隆站把提示指向自己
580
822
  const getOriginalHost = () => {
581
823
  try {
582
- return atob(canonical.encoded || '') || canonical.originalHost || '';
824
+ return atob(canonical.encoded || '') || canonical.host || '';
583
825
  } catch (e) {
584
- return canonical.originalHost || '';
826
+ return canonical.host || '';
585
827
  }
586
828
  };
587
829
  function originStatusCheck() {
@@ -646,7 +888,7 @@ const init = {
646
888
  if (isCurrentHostValid) {
647
889
  return;
648
890
  }
649
- if (canonical.officialHosts?.includes(currentHost)) {
891
+ if (canonical.allowedHosts?.includes(currentHost)) {
650
892
  showTip(true);
651
893
  return;
652
894
  }
@@ -660,7 +902,7 @@ const init = {
660
902
  if (isCanonicalHostValid && isCurrentHostValid) {
661
903
  return;
662
904
  }
663
- showTip(canonical.officialHosts?.includes(currentHost));
905
+ showTip(canonical.allowedHosts?.includes(currentHost));
664
906
  }
665
907
 
666
908
  }
@@ -668,17 +910,18 @@ const init = {
668
910
 
669
911
  // Stellar namespace
670
912
  window.stellar = window.stellar || {};
913
+ stellar.toast = hud.toast;
671
914
 
672
915
  /**
673
916
  * Initialize page components
674
917
  */
675
918
  stellar.initPage = function () {
676
919
  init.toc();
677
- init.sidebar();
920
+ init.tocLinks();
678
921
  init.wikiStart();
679
922
  init.wikiCover();
680
923
  init.leftbarScroll();
681
- init.navbarPin();
924
+ init.listingNavPin();
682
925
  init.relativeDate(document.querySelectorAll('#post-meta time'));
683
926
  init.registerTabsTag();
684
927
  };
@@ -7,7 +7,7 @@
7
7
  // contrast 黑白对比(两种变量同色)
8
8
  // split 大字(标题/headline)用 contrast,小字(caption/subtitle/面包屑等)用 theme
9
9
  // 用户显式覆盖优先:元素已有内联 --text-banner 或内联 color 时跳过。
10
- // 由 layout/_plugins/adaptive-text.ejs 在页面存在目标元素时懒加载。
10
+ // 由 runtime feature adapter 在页面存在目标元素时按需加载并挂载。
11
11
 
12
12
  function parseCssUrl(value) {
13
13
  if (!value) {
@@ -114,15 +114,16 @@ function setWikiOverlayColor(el, rgb) {
114
114
  // Wiki 卡片主题色确定后(包括提取失败时的 CSS 回退)通知索引页。
115
115
  // 覆盖层会同时等待原图 load,避免先用默认主题色绘制、再切换为平均色。
116
116
  function notifyWikiOverlayReady(el) {
117
- if (!el || !el.dispatchEvent || !document.createEvent) {
117
+ var ownerDocument = el && el.ownerDocument;
118
+ if (!el || !el.dispatchEvent || !ownerDocument || !ownerDocument.createEvent) {
118
119
  return;
119
120
  }
120
- var event = document.createEvent('Event');
121
+ var event = ownerDocument.createEvent('Event');
121
122
  event.initEvent('wiki-overlay-ready', true, false);
122
123
  el.dispatchEvent(event);
123
124
  }
124
125
 
125
- function applyToElement(el) {
126
+ function applyToElement(el, isActive) {
126
127
  // 用户显式覆盖优先:首次处理时元素已有内联 --text-banner 或内联 color 则跳过
127
128
  // (插件自身写入的变量不视为用户覆盖,主题切换重算时需重新应用)
128
129
  var isFirst = !appliedElements.has(el);
@@ -151,6 +152,7 @@ function applyToElement(el) {
151
152
  } else {
152
153
  var backdrop = resolveBackdrop(el);
153
154
  stellar.color.getAverageColor(bg.url, { background: backdrop }).then(function (rgb) {
155
+ if (!isActive()) return;
154
156
  if (rgb) {
155
157
  apply(rgb);
156
158
  } else if (styleName === 'split' && el.parentElement && (' ' + el.parentElement.className + ' ').indexOf(' wiki-card-cover ') !== -1) {
@@ -161,30 +163,45 @@ function applyToElement(el) {
161
163
  }
162
164
  }
163
165
 
164
- var adaptiveTextElements = [];
165
166
  var appliedElements = new WeakSet();
166
167
 
167
- function applyAdaptiveText(elements) {
168
+ function applyAdaptiveText(elements, isActive) {
168
169
  if (!window.stellar || !window.stellar.color || !elements || elements.length === 0) {
169
170
  return;
170
171
  }
171
- adaptiveTextElements = Array.prototype.slice.call(elements);
172
+ var active = typeof isActive === 'function' ? isActive : function () { return true; };
172
173
  for (var i = 0; i < elements.length; i++) {
173
- applyToElement(elements[i]);
174
+ applyToElement(elements[i], active);
174
175
  }
175
176
  }
176
177
 
177
- // 主题明暗切换时重算:透明背景图的合成背景随 data-theme 变化,颜色需重新计算
178
- if (window.MutationObserver && document.documentElement) {
179
- new MutationObserver(function () {
180
- applyAdaptiveText(adaptiveTextElements);
181
- if (typeof window.refreshPinNavColor === 'function') {
182
- window.refreshPinNavColor();
183
- }
184
- }).observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
178
+ function mountAdaptiveText(elements) {
179
+ var active = true;
180
+ var mountedElements = Array.prototype.slice.call(elements || []);
181
+ var ownerDocument = mountedElements[0] && mountedElements[0].ownerDocument;
182
+ var observer = null;
183
+ var isActive = function () { return active; };
184
+ applyAdaptiveText(mountedElements, isActive);
185
+ // 主题明暗切换时重算:透明背景图的合成背景随 data-theme 变化,颜色需重新计算
186
+ if (window.MutationObserver && ownerDocument && ownerDocument.documentElement) {
187
+ observer = new MutationObserver(function () {
188
+ applyAdaptiveText(mountedElements, isActive);
189
+ if (typeof window.refreshPinNavColor === 'function') {
190
+ window.refreshPinNavColor();
191
+ }
192
+ });
193
+ observer.observe(ownerDocument.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
194
+ }
195
+ return function () {
196
+ active = false;
197
+ mountedElements = [];
198
+ observer?.disconnect();
199
+ observer = null;
200
+ };
185
201
  }
186
202
 
187
203
  // 供轮播箭头等场景复用:解析元素背后实际渲染背景色
188
204
  window.resolveAdaptiveBackdrop = resolveBackdrop;
189
205
 
190
206
  window.applyAdaptiveText = applyAdaptiveText;
207
+ window.stellarAdaptiveText = { mount: mountAdaptiveText };
@@ -8,31 +8,12 @@
8
8
 
9
9
  window.stellar = window.stellar || {};
10
10
 
11
- const DEFAULT_SPOTLIGHT_COLOR = 'rgba(255, 255, 255, 0.25)';
12
- const DEFAULT_MAX_TILT = 3;
13
- const MAX_TILT_LIMIT = 8;
11
+ const MAX_TILT = 3;
14
12
  const mounted = new Map();
15
13
  const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
16
14
  const finePointer = window.matchMedia('(hover: hover) and (pointer: fine)');
17
15
  let lifecycleBound = false;
18
16
 
19
- function normalizeConfig() {
20
- const raw = typeof ctx !== 'undefined' && ctx.card_hover ? ctx.card_hover : {};
21
- let maxTilt = Number(raw.maxTilt);
22
- if (raw.maxTilt === null || raw.maxTilt === '' || !Number.isFinite(maxTilt)) {
23
- maxTilt = DEFAULT_MAX_TILT;
24
- }
25
- maxTilt = Math.max(0, Math.min(MAX_TILT_LIMIT, maxTilt));
26
-
27
- let spotlightColor = typeof raw.spotlightColor === 'string' && raw.spotlightColor.trim()
28
- ? raw.spotlightColor.trim()
29
- : DEFAULT_SPOTLIGHT_COLOR;
30
- if (window.CSS && typeof window.CSS.supports === 'function' && !window.CSS.supports('color', spotlightColor)) {
31
- spotlightColor = DEFAULT_SPOTLIGHT_COLOR;
32
- }
33
- return { maxTilt: maxTilt, spotlightColor: spotlightColor };
34
- }
35
-
36
17
  function canAnimate() {
37
18
  return !reduceMotion.matches && finePointer.matches;
38
19
  }
@@ -117,8 +98,6 @@
117
98
  const hasTilt = element.classList.contains('card-hover--tilt');
118
99
  if (!hasSpotlight && !hasTilt) return;
119
100
 
120
- const config = normalizeConfig();
121
- document.documentElement.style.setProperty('--card-hover-spotlight-color', config.spotlightColor);
122
101
  let spotlight = null;
123
102
  if (hasSpotlight) {
124
103
  spotlight = document.createElement('span');
@@ -131,7 +110,7 @@
131
110
  element: element,
132
111
  spotlight: spotlight,
133
112
  hasTilt: hasTilt,
134
- maxTilt: config.maxTilt,
113
+ maxTilt: MAX_TILT,
135
114
  frame: null,
136
115
  pointer: null,
137
116
  onPointerEnter: null,
@@ -243,7 +222,6 @@
243
222
  document.removeEventListener('stellar:mdrender', handleRenderedMarkdown);
244
223
  document.removeEventListener('visibilitychange', handleVisibility);
245
224
  window.removeEventListener('pagehide', resetAll);
246
- document.documentElement.style.removeProperty('--card-hover-spotlight-color');
247
225
  }
248
226
 
249
227
  stellar.cardHover = {
@@ -2,9 +2,13 @@ function createCopyButtons(codeElements) {
2
2
  codeElements.forEach((codeElement) => {
3
3
  if (codeElement.querySelector('.copy-btn')) return;
4
4
  // 创建复制按钮
5
- const codeCopyBtn = document.createElement('div');
5
+ const codeCopyBtn = document.createElement('button');
6
+ const copyIcon = ctx.icons['copy:copy'] || '';
6
7
  codeCopyBtn.className = 'copy-btn';
7
- codeCopyBtn.textContent = ctx.copycode.default_text;
8
+ codeCopyBtn.type = 'button';
9
+ codeCopyBtn.innerHTML = copyIcon;
10
+ codeCopyBtn.setAttribute('aria-label', ctx.copycode.label);
11
+ codeCopyBtn.setAttribute('title', ctx.copycode.label);
8
12
  codeElement.appendChild(codeCopyBtn);
9
13
 
10
14
  // 添加点击事件监听
@@ -13,27 +17,17 @@ function createCopyButtons(codeElements) {
13
17
  if (navigator.clipboard) {
14
18
  try {
15
19
  await navigator.clipboard.writeText(codeToCopy);
16
- codeCopyBtn.textContent = ctx.copycode.success_text;
17
- codeCopyBtn.classList.add('success');
18
- hud.toast(ctx.copycode.toast, 2500);
20
+ hud.toast(ctx.copycode.copied, ctx.copycode.toast_ms);
19
21
  } catch (error) {
20
- codeCopyBtn.textContent = '未获得用户许可';
21
- codeCopyBtn.classList.add('warning');
22
+ hud.toast(ctx.copycode.denied, ctx.copycode.toast_ms);
22
23
  }
23
24
  } else {
24
- codeCopyBtn.textContent = '浏览器不支持/非HTTPS';
25
- codeCopyBtn.classList.add('warning');
25
+ hud.toast(ctx.copycode.unsupported, ctx.copycode.toast_ms);
26
26
  }
27
-
28
- // 3秒后恢复默认文本
29
- setTimeout(() => {
30
- codeCopyBtn.textContent = ctx.copycode.default_text;
31
- codeCopyBtn.classList.remove('success', 'warning');
32
- }, 3000);
33
27
  });
34
28
  });
35
29
  }
36
30
 
37
31
  if (typeof window.codeElements !== 'undefined') {
38
32
  createCopyButtons(window.codeElements);
39
- }
33
+ }