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,235 @@
1
+ import { REQUEST_CACHE_PREFIX } from '../request-cache.js';
2
+
3
+ export const SEARCH_CACHE_KEY = 'search_cache_v5';
4
+
5
+ function utf8Bytes(value) {
6
+ const text = String(value || '');
7
+ if (typeof TextEncoder === 'function') return new TextEncoder().encode(text).byteLength;
8
+ let bytes = 0;
9
+ for (const character of text) {
10
+ const point = character.codePointAt(0);
11
+ bytes += point <= 0x7f ? 1 : point <= 0x7ff ? 2 : point <= 0xffff ? 3 : 4;
12
+ }
13
+ return bytes;
14
+ }
15
+
16
+ function entryBytes(key, value) {
17
+ return utf8Bytes(key) + utf8Bytes(value);
18
+ }
19
+
20
+ export function measureCacheSizes(storage) {
21
+ let target = storage;
22
+ if (arguments.length === 0) {
23
+ try { target = globalThis.localStorage; } catch (error) { void error; target = null; }
24
+ }
25
+ if (!target) return Object.freeze({ search: 0, dynamic: 0, all: 0, failed: true });
26
+ let search = 0;
27
+ let dynamic = 0;
28
+ try {
29
+ const searchValue = target.getItem(SEARCH_CACHE_KEY);
30
+ if (searchValue !== null) search = entryBytes(SEARCH_CACHE_KEY, searchValue);
31
+ for (let index = 0; index < target.length; index++) {
32
+ const key = target.key(index);
33
+ if (!key?.startsWith(REQUEST_CACHE_PREFIX)) continue;
34
+ const value = target.getItem(key);
35
+ if (value !== null) dynamic += entryBytes(key, value);
36
+ }
37
+ } catch (error) {
38
+ void error;
39
+ return Object.freeze({ search: 0, dynamic: 0, all: 0, failed: true });
40
+ }
41
+ return Object.freeze({ search, dynamic, all: search + dynamic, failed: false });
42
+ }
43
+
44
+ export function formatCacheSize(bytes) {
45
+ if (!Number.isFinite(bytes) || bytes < 0) return '—';
46
+ if (bytes < 1024) return `${bytes} B`;
47
+ const units = ['KB', 'MB', 'GB'];
48
+ let value = bytes / 1024;
49
+ let unit = units[0];
50
+ for (let index = 1; index < units.length && value >= 1024; index++) {
51
+ value /= 1024;
52
+ unit = units[index];
53
+ }
54
+ const precision = value < 10 ? 1 : 0;
55
+ return `${value.toFixed(precision).replace(/\.0$/, '')} ${unit}`;
56
+ }
57
+
58
+ function resultMessage(page, result) {
59
+ if (result?.partial) return page.dataset.messagePartial;
60
+ return result?.ok ? page.dataset.messageSuccess : page.dataset.messageFailure;
61
+ }
62
+
63
+ function setStatus(element, message, state) {
64
+ if (!element) return;
65
+ element.textContent = message || '';
66
+ element.dataset.state = state || '';
67
+ }
68
+
69
+ function fallbackSearchClear() {
70
+ try {
71
+ globalThis.localStorage?.removeItem(SEARCH_CACHE_KEY);
72
+ return { ok: true, partial: false, removed: 1, failed: 0 };
73
+ } catch (error) {
74
+ return { ok: false, partial: false, removed: 0, failed: 1 };
75
+ }
76
+ }
77
+
78
+ function combine(results) {
79
+ const succeeded = results.filter(result => result?.ok).length;
80
+ const partial = results.some(result => result?.partial) || (succeeded > 0 && succeeded < results.length);
81
+ return { ok: succeeded === results.length && !partial, partial };
82
+ }
83
+
84
+ export function mount(root, context) {
85
+ const page = root.querySelector?.('.settings-page');
86
+ if (!page) return;
87
+ const provider = String(page.dataset.provider || '').toLowerCase();
88
+ const api = globalThis.window?.stellarProfile;
89
+ const form = page.querySelector('[data-profile-form]');
90
+ const note = page.querySelector('[data-provider-note]');
91
+ const status = page.querySelector('[data-profile-status]');
92
+ const avatar = page.querySelector('[data-profile-avatar]');
93
+ const avatarFallback = page.querySelector('[data-profile-avatar-fallback]');
94
+ const nameInput = form?.querySelector('[name="name"]');
95
+ const emailInput = form?.querySelector('[name="email"]');
96
+ const urlInput = form?.querySelector('[name="url"]');
97
+ const logoutButton = page.querySelector('[data-profile-logout]');
98
+ const supported = !!api?.supports?.(provider);
99
+ const fallbackAvatar = page.dataset.fallbackAvatar || '';
100
+ let avatarRevision = 0;
101
+
102
+ function avatarRenderIsCurrent(revision) {
103
+ return revision === avatarRevision;
104
+ }
105
+
106
+ function showAvatarIcon(revision) {
107
+ if (!avatar || !avatarRenderIsCurrent(revision)) return;
108
+ avatar.onerror = null;
109
+ avatar.hidden = true;
110
+ avatar.removeAttribute('src');
111
+ if (avatarFallback) avatarFallback.hidden = false;
112
+ }
113
+
114
+ function showAvatarImage(url, revision, onError) {
115
+ if (!avatar || !avatarRenderIsCurrent(revision)) return;
116
+ avatar.hidden = false;
117
+ if (avatarFallback) avatarFallback.hidden = true;
118
+ avatar.onerror = () => {
119
+ if (avatarRenderIsCurrent(revision)) onError();
120
+ };
121
+ avatar.src = url;
122
+ }
123
+
124
+ function showFallbackAvatar(revision) {
125
+ if (!avatarRenderIsCurrent(revision)) return;
126
+ if (!fallbackAvatar) {
127
+ showAvatarIcon(revision);
128
+ return;
129
+ }
130
+ showAvatarImage(fallbackAvatar, revision, () => showAvatarIcon(revision));
131
+ }
132
+
133
+ function showGravatar(identity, revision) {
134
+ if (!identity.email || typeof api?.getGravatarUrl !== 'function') {
135
+ showFallbackAvatar(revision);
136
+ return;
137
+ }
138
+ showAvatarIcon(revision);
139
+ Promise.resolve(api.getGravatarUrl(identity.email, 160)).then(url => {
140
+ if (!avatarRenderIsCurrent(revision)) return;
141
+ if (!url) {
142
+ showFallbackAvatar(revision);
143
+ return;
144
+ }
145
+ showAvatarImage(url, revision, () => showFallbackAvatar(revision));
146
+ }, () => showFallbackAvatar(revision));
147
+ }
148
+
149
+ function renderAvatar(identity) {
150
+ const revision = ++avatarRevision;
151
+ if (!avatar) return;
152
+ if (identity.avatar) {
153
+ showAvatarImage(identity.avatar, revision, () => showGravatar(identity, revision));
154
+ return;
155
+ }
156
+ showGravatar(identity, revision);
157
+ }
158
+
159
+ function renderCacheSizes() {
160
+ const sizes = measureCacheSizes();
161
+ ['search', 'dynamic', 'all'].forEach(type => {
162
+ const element = page.querySelector(`[data-cache-size="${type}"]`);
163
+ if (element) element.textContent = sizes.failed ? '—' : formatCacheSize(sizes[type]);
164
+ });
165
+ }
166
+
167
+ function render() {
168
+ const identity = api?.readIdentity?.(provider) || { name: '', email: '', url: '', avatar: '' };
169
+ if (nameInput) nameInput.value = identity.name || '';
170
+ if (emailInput) emailInput.value = identity.email || '';
171
+ if (urlInput) urlInput.value = identity.url || '';
172
+ if (form) form.hidden = !supported;
173
+ if (logoutButton) logoutButton.hidden = !supported;
174
+ if (note) note.hidden = supported;
175
+ renderAvatar(identity);
176
+ }
177
+
178
+ function onSubmit(event) {
179
+ event.preventDefault();
180
+ if (!form?.reportValidity?.()) return;
181
+ const result = api?.writeIdentity?.(provider, {
182
+ name: nameInput?.value || '',
183
+ email: emailInput?.value || '',
184
+ url: urlInput?.value || ''
185
+ }) || { ok: false };
186
+ const validationMessage = result.validation ? page.dataset[`validation${result.validation[0].toUpperCase()}${result.validation.slice(1)}`] : '';
187
+ setStatus(status, validationMessage || resultMessage(page, result), result.ok ? 'success' : result.partial ? 'partial' : 'failure');
188
+ if (result.ok || result.partial) render();
189
+ }
190
+
191
+ function onLogout() {
192
+ const result = api?.logout?.(provider) || { ok: false };
193
+ setStatus(status, resultMessage(page, result), result.ok ? 'success' : result.partial ? 'partial' : 'failure');
194
+ if (result.ok || result.partial) render();
195
+ }
196
+
197
+ function clearSearch() {
198
+ return globalThis.window?.stellarLocalSearch?.clearCache?.() || fallbackSearchClear();
199
+ }
200
+
201
+ function onCacheAction(event) {
202
+ const button = event.currentTarget;
203
+ const action = button.dataset.cacheAction;
204
+ const results = [];
205
+ if (action === 'search' || action === 'all') results.push(clearSearch());
206
+ if (action === 'dynamic' || action === 'all') results.push(context.request.clearCache());
207
+ const result = combine(results);
208
+ const cacheStatus = page.querySelector('[data-cache-status]');
209
+ if (result.ok) setStatus(cacheStatus, '', '');
210
+ else setStatus(cacheStatus, resultMessage(page, result), result.partial ? 'partial' : 'failure');
211
+ renderCacheSizes();
212
+ }
213
+
214
+ function onStorage() {
215
+ render();
216
+ renderCacheSizes();
217
+ }
218
+
219
+ form?.addEventListener('submit', onSubmit);
220
+ logoutButton?.addEventListener('click', onLogout);
221
+ const cacheButtons = Array.from(page.querySelectorAll('[data-cache-action]'));
222
+ cacheButtons.forEach(button => button.addEventListener('click', onCacheAction));
223
+ globalThis.window?.addEventListener?.('storage', onStorage);
224
+ globalThis.window?.addEventListener?.('stellar:profile-change', render);
225
+ render();
226
+ renderCacheSizes();
227
+ return () => {
228
+ avatarRevision++;
229
+ form?.removeEventListener('submit', onSubmit);
230
+ logoutButton?.removeEventListener('click', onLogout);
231
+ cacheButtons.forEach(button => button.removeEventListener('click', onCacheAction));
232
+ globalThis.window?.removeEventListener?.('storage', onStorage);
233
+ globalThis.window?.removeEventListener?.('stellar:profile-change', render);
234
+ };
235
+ }
@@ -0,0 +1,385 @@
1
+ /*
2
+ * Wiki Ferrofluid WebGL renderer.
3
+ * Shader adapted from React Bits Ferrofluid: https://github.com/DavidHDev/react-bits
4
+ * React Bits is MIT licensed; see THIRD-PARTY-NOTICES.md.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const MAX_COLORS = 8;
10
+
11
+ const vertexShader = `
12
+ attribute vec2 position;
13
+ varying vec2 vUv;
14
+
15
+ void main() {
16
+ vUv = position * 0.5 + 0.5;
17
+ gl_Position = vec4(position, 0.0, 1.0);
18
+ }
19
+ `;
20
+
21
+ const fragmentShader = `
22
+ precision highp float;
23
+
24
+ uniform vec3 iResolution;
25
+ uniform vec2 iMouse;
26
+ uniform float iTime;
27
+
28
+ uniform vec3 uColor0;
29
+ uniform vec3 uColor1;
30
+ uniform vec3 uColor2;
31
+ uniform vec3 uColor3;
32
+ uniform vec3 uColor4;
33
+ uniform vec3 uColor5;
34
+ uniform vec3 uColor6;
35
+ uniform vec3 uColor7;
36
+ uniform int uColorCount;
37
+
38
+ uniform vec2 uFlow;
39
+ uniform float uSpeed;
40
+ uniform float uScale;
41
+ uniform float uTurbulence;
42
+ uniform float uFluidity;
43
+ uniform float uRimWidth;
44
+ uniform float uSharpness;
45
+ uniform float uShimmer;
46
+ uniform float uGlow;
47
+ uniform float uOpacity;
48
+ uniform float uMouseEnabled;
49
+ uniform float uMouseStrength;
50
+ uniform float uMouseRadius;
51
+
52
+ varying vec2 vUv;
53
+
54
+ #define PI 3.14159265
55
+
56
+ vec3 palette(float h) {
57
+ int count = uColorCount;
58
+ if (count < 1) count = 1;
59
+ int idx = int(floor(clamp(h, 0.0, 0.999999) * float(count)));
60
+ if (idx <= 0) return uColor0;
61
+ if (idx == 1) return uColor1;
62
+ if (idx == 2) return uColor2;
63
+ if (idx == 3) return uColor3;
64
+ if (idx == 4) return uColor4;
65
+ if (idx == 5) return uColor5;
66
+ if (idx == 6) return uColor6;
67
+ return uColor7;
68
+ }
69
+
70
+ float hash(vec3 p3) {
71
+ p3 = fract(p3 * 0.1031);
72
+ p3 += dot(p3, p3.zyx + 33.33);
73
+ return fract((p3.x + p3.y) * p3.z);
74
+ }
75
+
76
+ float smin(float a, float b, float k) {
77
+ float r = exp2(-a / k) + exp2(-b / k);
78
+ return -k * log2(r);
79
+ }
80
+
81
+ float sinlerp(float a, float b, float w) {
82
+ return mix(a, b, (sin(w * PI - PI / 2.0) + 1.0) / 2.0);
83
+ }
84
+
85
+ float vn(vec2 p, float s, float seed) {
86
+ vec2 cellp = floor(p / s);
87
+ vec2 relp = mod(p, s);
88
+ float g1 = hash(vec3(cellp, seed));
89
+ float g2 = hash(vec3(cellp.x + 1.0, cellp.y, seed));
90
+ float g3 = hash(vec3(cellp.x + 1.0, cellp.y + 1.0, seed));
91
+ float g4 = hash(vec3(cellp.x, cellp.y + 1.0, seed));
92
+ float bx = sinlerp(g1, g2, relp.x / s);
93
+ float tx = sinlerp(g4, g3, relp.x / s);
94
+ return sinlerp(bx, tx, relp.y / s);
95
+ }
96
+
97
+ float dbn(vec2 p, float s, float seed) {
98
+ float o = s / 2.0;
99
+ float n0 = vn(p, s, seed);
100
+ float n1 = vn(p + vec2(o, o), s, seed + 0.1);
101
+ float n2 = vn(p + vec2(-o, o), s, seed + 0.2);
102
+ float n3 = vn(p + vec2(o, -o), s, seed + 0.3);
103
+ float n4 = vn(p + vec2(-o, -o), s, seed + 0.4);
104
+ return (2.0 * n0 + 1.5 * n1 + 1.25 * n2 + 1.125 * n3 + n4) / 7.0;
105
+ }
106
+
107
+ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
108
+ float ref = 700.0 / max(uScale, 0.05);
109
+ vec2 p = fragCoord / iResolution.y * ref;
110
+
111
+ float spd = 200.0 * uSpeed;
112
+ float t = iTime;
113
+
114
+ vec2 dir = uFlow;
115
+ vec2 perp = vec2(-dir.y, dir.x);
116
+
117
+ float distort1 = vn(p + perp * (t * spd), 60.0, 10.0) * 50.0 * uTurbulence;
118
+ float distort2 = vn(p - perp * (t * spd), 120.0, 15.0) * 100.0 * uTurbulence;
119
+
120
+ float peaks = dbn(p + distort1 + dir * (t * spd * 0.5), 40.0, 1.0);
121
+ float peaks2 = dbn(p + distort2 - dir * (t * spd * 0.5), 40.0, 0.0);
122
+ float mapeaks = smin(peaks, peaks2, max(uFluidity, 0.001));
123
+
124
+ float mGlow = 0.0;
125
+ if (uMouseEnabled > 0.5) {
126
+ vec2 mp = iMouse / iResolution.y * ref;
127
+ float md = length(p - mp) / ref;
128
+ float rr = max(uMouseRadius, 0.02);
129
+ mGlow = exp(-md * md / (rr * rr)) * uMouseStrength;
130
+ }
131
+
132
+ float band = (uRimWidth - abs((mapeaks - 0.4) * 2.0)) * 5.0;
133
+ float ltn = clamp(band - vn(p + dir * (t * spd * 0.5), 60.0, 12.0) * uShimmer, 0.0, 1.0);
134
+ ltn = pow(ltn, uSharpness) * uGlow;
135
+ ltn *= clamp(1.0 - mGlow, 0.0, 1.0);
136
+
137
+ float h = clamp(0.5 + (peaks - peaks2) * 0.8, 0.0, 1.0);
138
+ vec3 col = palette(h);
139
+ vec3 outc = col * ltn;
140
+ float a = clamp(max(outc.r, max(outc.g, outc.b)), 0.0, 1.0);
141
+ fragColor = vec4(outc, a * uOpacity);
142
+ }
143
+
144
+ void main() {
145
+ vec4 color;
146
+ mainImage(color, vUv * iResolution.xy);
147
+ gl_FragColor = color;
148
+ }
149
+ `;
150
+
151
+ function finiteOrDefault(value, fallback) {
152
+ return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
153
+ }
154
+
155
+ function booleanOrDefault(value, fallback) {
156
+ return typeof value === 'boolean' ? value : fallback;
157
+ }
158
+
159
+ function colorOrDefault(value, fallback) {
160
+ return typeof value === 'string' && /^#?[a-f\d]{6}$/i.test(value) ? value : fallback;
161
+ }
162
+
163
+ function colorsOrDefault(value, fallback) {
164
+ if (!Array.isArray(value) || value.length < 1 || value.length > MAX_COLORS) return fallback.slice();
165
+ if (value.some(item => typeof item !== 'string' || !/^#?[a-f\d]{6}$/i.test(item))) return fallback.slice();
166
+ return value.slice();
167
+ }
168
+
169
+ function directionOrDefault(value, fallback) {
170
+ return ['up', 'down', 'left', 'right'].includes(value) ? value : fallback;
171
+ }
172
+
173
+ function normalizeParams(value, defaults) {
174
+ const source = value && typeof value === 'object' && !Array.isArray(value) ? value : {};
175
+ const explicitDpr = source.dpr === null ? null : finiteOrDefault(source.dpr, defaults.dpr);
176
+ return {
177
+ colors: colorsOrDefault(source.colors, defaults.colors),
178
+ backgroundColor: colorOrDefault(source.backgroundColor, defaults.backgroundColor),
179
+ speed: finiteOrDefault(source.speed, defaults.speed),
180
+ scale: finiteOrDefault(source.scale, defaults.scale),
181
+ turbulence: finiteOrDefault(source.turbulence, defaults.turbulence),
182
+ fluidity: finiteOrDefault(source.fluidity, defaults.fluidity),
183
+ rimWidth: finiteOrDefault(source.rimWidth, defaults.rimWidth),
184
+ sharpness: finiteOrDefault(source.sharpness, defaults.sharpness),
185
+ shimmer: finiteOrDefault(source.shimmer, defaults.shimmer),
186
+ glow: finiteOrDefault(source.glow, defaults.glow),
187
+ flowDirection: directionOrDefault(source.flowDirection, defaults.flowDirection),
188
+ opacity: finiteOrDefault(source.opacity, defaults.opacity),
189
+ mouseInteraction: booleanOrDefault(source.mouseInteraction, defaults.mouseInteraction),
190
+ mouseStrength: finiteOrDefault(source.mouseStrength, defaults.mouseStrength),
191
+ mouseRadius: finiteOrDefault(source.mouseRadius, defaults.mouseRadius),
192
+ mouseDampening: finiteOrDefault(source.mouseDampening, defaults.mouseDampening),
193
+ paused: booleanOrDefault(source.paused, defaults.paused),
194
+ dpr: typeof explicitDpr === 'number' && explicitDpr > 0 ? explicitDpr : null,
195
+ mixBlendMode: typeof source.mixBlendMode === 'string' && source.mixBlendMode.trim().length > 0
196
+ ? source.mixBlendMode
197
+ : defaults.mixBlendMode
198
+ };
199
+ }
200
+
201
+ function hexToRgb(value) {
202
+ const normalized = value.replace(/^#/, '');
203
+ return [
204
+ parseInt(normalized.slice(0, 2), 16) / 255,
205
+ parseInt(normalized.slice(2, 4), 16) / 255,
206
+ parseInt(normalized.slice(4, 6), 16) / 255
207
+ ];
208
+ }
209
+
210
+ function prepareColors(colors) {
211
+ const rgb = colors.map(hexToRgb);
212
+ while (rgb.length < MAX_COLORS) rgb.push(rgb[rgb.length - 1].slice());
213
+ return { values: rgb, count: colors.length };
214
+ }
215
+
216
+ function flowVector(direction) {
217
+ if (direction === 'up') return [0, 1];
218
+ if (direction === 'left') return [-1, 0];
219
+ if (direction === 'right') return [1, 0];
220
+ return [0, -1];
221
+ }
222
+
223
+ function createShader(gl, type, source) {
224
+ const shader = gl.createShader(type);
225
+ gl.shaderSource(shader, source);
226
+ gl.compileShader(shader);
227
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
228
+ const message = gl.getShaderInfoLog(shader) || 'Unknown shader compilation error';
229
+ gl.deleteShader(shader);
230
+ throw new Error(message);
231
+ }
232
+ return shader;
233
+ }
234
+
235
+ function createProgram(gl) {
236
+ const vertex = createShader(gl, gl.VERTEX_SHADER, vertexShader);
237
+ let fragment;
238
+ try {
239
+ fragment = createShader(gl, gl.FRAGMENT_SHADER, fragmentShader);
240
+ } catch (error) {
241
+ gl.deleteShader(vertex);
242
+ throw error;
243
+ }
244
+ const program = gl.createProgram();
245
+ gl.attachShader(program, vertex);
246
+ gl.attachShader(program, fragment);
247
+ gl.linkProgram(program);
248
+ gl.deleteShader(vertex);
249
+ gl.deleteShader(fragment);
250
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
251
+ const message = gl.getProgramInfoLog(program) || 'Unknown program link error';
252
+ gl.deleteProgram(program);
253
+ throw new Error(message);
254
+ }
255
+ return program;
256
+ }
257
+
258
+ function uniformLocations(gl, program) {
259
+ const names = [
260
+ 'iResolution', 'iMouse', 'iTime',
261
+ 'uColor0', 'uColor1', 'uColor2', 'uColor3',
262
+ 'uColor4', 'uColor5', 'uColor6', 'uColor7', 'uColorCount',
263
+ 'uFlow', 'uSpeed', 'uScale', 'uTurbulence', 'uFluidity',
264
+ 'uRimWidth', 'uSharpness', 'uShimmer', 'uGlow', 'uOpacity',
265
+ 'uMouseEnabled', 'uMouseStrength', 'uMouseRadius'
266
+ ];
267
+ const locations = {};
268
+ names.forEach(name => {
269
+ locations[name] = gl.getUniformLocation(program, name);
270
+ });
271
+ return locations;
272
+ }
273
+
274
+ export function createRenderer(canvas, options, defaults) {
275
+ if (!canvas) return null;
276
+ const params = normalizeParams(options, defaults);
277
+
278
+ let gl;
279
+ try {
280
+ gl = canvas.getContext('webgl', {
281
+ alpha: true,
282
+ antialias: true,
283
+ premultipliedAlpha: false,
284
+ powerPreference: 'high-performance'
285
+ });
286
+ } catch (error) {
287
+ void error;
288
+ return null;
289
+ }
290
+ if (!gl) return null;
291
+
292
+ let program;
293
+ try {
294
+ program = createProgram(gl);
295
+ } catch (error) {
296
+ void error;
297
+ const loseContext = gl.getExtension('WEBGL_lose_context');
298
+ if (loseContext) loseContext.loseContext();
299
+ return null;
300
+ }
301
+
302
+ gl.useProgram(program);
303
+ const locations = uniformLocations(gl, program);
304
+ const buffer = gl.createBuffer();
305
+ const position = gl.getAttribLocation(program, 'position');
306
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
307
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
308
+ -1.0, -1.0,
309
+ 3.0, -1.0,
310
+ -1.0, 3.0
311
+ ]), gl.STATIC_DRAW);
312
+ gl.enableVertexAttribArray(position);
313
+ gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);
314
+ gl.enable(gl.BLEND);
315
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
316
+ gl.clearColor(0, 0, 0, 0);
317
+
318
+ const colors = prepareColors(params.colors);
319
+ colors.values.forEach((color, index) => {
320
+ const location = locations[`uColor${index}`];
321
+ gl.uniform3f(location, color[0], color[1], color[2]);
322
+ });
323
+ const flow = flowVector(params.flowDirection);
324
+ gl.uniform1i(locations.uColorCount, colors.count);
325
+ gl.uniform2f(locations.uFlow, flow[0], flow[1]);
326
+ gl.uniform1f(locations.uSpeed, params.speed);
327
+ gl.uniform1f(locations.uScale, params.scale);
328
+ gl.uniform1f(locations.uTurbulence, params.turbulence);
329
+ gl.uniform1f(locations.uFluidity, params.fluidity);
330
+ gl.uniform1f(locations.uRimWidth, params.rimWidth);
331
+ gl.uniform1f(locations.uSharpness, params.sharpness);
332
+ gl.uniform1f(locations.uShimmer, params.shimmer);
333
+ gl.uniform1f(locations.uGlow, params.glow);
334
+ gl.uniform1f(locations.uOpacity, params.opacity);
335
+ gl.uniform1f(locations.uMouseEnabled, params.mouseInteraction ? 1 : 0);
336
+ gl.uniform1f(locations.uMouseStrength, params.mouseStrength);
337
+ gl.uniform1f(locations.uMouseRadius, params.mouseRadius);
338
+
339
+ const previousMixBlendMode = canvas.style.mixBlendMode;
340
+ if (params.mixBlendMode) canvas.style.mixBlendMode = params.mixBlendMode;
341
+
342
+ const mouse = { x: 0, y: 0 };
343
+ let lastTime = null;
344
+ let rendered = false;
345
+ return Object.freeze({
346
+ pointer: params.mouseInteraction,
347
+ resize(rect, devicePixelRatio) {
348
+ const dpr = params.dpr || devicePixelRatio;
349
+ const width = Math.max(1, Math.round(rect.width * dpr));
350
+ const height = Math.max(1, Math.round(rect.height * dpr));
351
+ if (canvas.width === width && canvas.height === height) return;
352
+ canvas.width = width;
353
+ canvas.height = height;
354
+ gl.viewport(0, 0, width, height);
355
+ gl.uniform3f(locations.iResolution, width, height, 1);
356
+ },
357
+ render(time, pointer) {
358
+ if (params.paused && rendered) return;
359
+ const targetX = pointer?.active === true ? pointer.x * canvas.width : 0;
360
+ const targetY = pointer?.active === true ? (1 - pointer.y) * canvas.height : 0;
361
+ if (params.mouseDampening <= 0 || lastTime === null) {
362
+ mouse.x = targetX;
363
+ mouse.y = targetY;
364
+ } else {
365
+ const elapsed = Math.max(0, (time - lastTime) / 1000);
366
+ const factor = Math.min(1, 1 - Math.exp(-elapsed / Math.max(params.mouseDampening, 0.0001)));
367
+ mouse.x += (targetX - mouse.x) * factor;
368
+ mouse.y += (targetY - mouse.y) * factor;
369
+ }
370
+ lastTime = time;
371
+ gl.uniform1f(locations.iTime, time * 0.001);
372
+ gl.uniform2f(locations.iMouse, mouse.x, mouse.y);
373
+ gl.clear(gl.COLOR_BUFFER_BIT);
374
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
375
+ rendered = true;
376
+ },
377
+ destroy() {
378
+ canvas.style.mixBlendMode = previousMixBlendMode;
379
+ gl.deleteBuffer(buffer);
380
+ gl.deleteProgram(program);
381
+ const loseContext = gl.getExtension('WEBGL_lose_context');
382
+ if (loseContext) loseContext.loseContext();
383
+ }
384
+ });
385
+ }