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.
- package/CHANGELOG.md +63 -5
- package/CONTRIBUTING.md +29 -120
- package/README.md +66 -73
- package/README_EN.md +66 -73
- package/_config.yml +890 -743
- package/_data/icons.yml +43 -171
- package/_data/widgets.yml +1 -1
- package/assets/icon-v2-x512.webp +0 -0
- package/languages/en.yml +59 -2
- package/languages/zh-CN.yml +59 -2
- package/languages/zh-TW.yml +59 -2
- package/layout/404.ejs +7 -4
- package/layout/_partial/comments/artalk/layout.ejs +3 -2
- package/layout/_partial/comments/beaudar/layout.ejs +2 -2
- package/layout/_partial/comments/giscus/layout.ejs +2 -5
- package/layout/_partial/comments/layout.ejs +11 -40
- package/layout/_partial/comments/twikoo/layout.ejs +3 -2
- package/layout/_partial/comments/utterances/layout.ejs +2 -2
- package/layout/_partial/comments/waline/layout.ejs +3 -2
- package/layout/_partial/components/collection-item.ejs +30 -13
- package/layout/_partial/components/collection.ejs +2 -2
- package/layout/_partial/cover/index.ejs +5 -2
- package/layout/_partial/cover/wiki_cover.ejs +39 -28
- package/layout/_partial/dropdown.ejs +26 -14
- package/layout/_partial/head.ejs +96 -69
- package/layout/_partial/main/article/contributors.ejs +12 -9
- package/layout/_partial/main/article/post_footer.ejs +47 -0
- package/layout/_partial/main/article/post_read_next.ejs +22 -0
- package/layout/_partial/main/article/post_related.ejs +14 -0
- package/layout/_partial/main/article/post_tags.ejs +12 -0
- package/layout/_partial/main/article/share.ejs +55 -0
- package/layout/_partial/main/footer.ejs +11 -10
- package/layout/_partial/main/{navbar/nav_tabs_blog.ejs → listing_nav/blog.ejs} +21 -18
- package/layout/_partial/main/listing_nav/wiki.ejs +56 -0
- package/layout/_partial/main/navbar/article_banner.ejs +39 -35
- package/layout/_partial/main/navbar/breadcrumb/blog.ejs +15 -28
- package/layout/_partial/main/navbar/breadcrumb/note.ejs +12 -5
- package/layout/_partial/main/navbar/breadcrumb/wiki.ejs +15 -11
- package/layout/_partial/main/navbar/dateinfo.ejs +28 -12
- package/layout/_partial/main/navbar/ghinfo.ejs +5 -3
- package/layout/_partial/main/notebook/note_card.ejs +12 -17
- package/layout/_partial/main/notebook/note_tags.ejs +3 -4
- package/layout/_partial/main/notebook/notebook_card.ejs +11 -4
- package/layout/_partial/main/pin_slider.ejs +48 -43
- package/layout/_partial/main/post_list/archive_item.ejs +8 -4
- package/layout/_partial/main/post_list/latest_post_card.ejs +10 -4
- package/layout/_partial/main/post_list/post_card.ejs +39 -90
- package/layout/_partial/main/post_list/topic_card.ejs +7 -5
- package/layout/_partial/main/post_list/wiki_card.ejs +16 -13
- package/layout/_partial/menubtn.ejs +3 -3
- package/layout/_partial/primitives/item.ejs +8 -0
- package/layout/_partial/primitives/navigation.ejs +17 -0
- package/layout/_partial/primitives/region.ejs +16 -0
- package/layout/_partial/primitives/section.ejs +8 -0
- package/layout/_partial/primitives/shell.ejs +35 -0
- package/layout/_partial/regions/widgets.ejs +84 -0
- package/layout/_partial/scripts/defines.ejs +46 -76
- package/layout/_partial/scripts/runtime.ejs +55 -0
- package/layout/_partial/scripts.ejs +18 -42
- package/layout/_partial/search/dialog.ejs +33 -0
- package/layout/_partial/search/shortcut.ejs +1 -0
- package/layout/_partial/sidebar/brand.ejs +127 -0
- package/layout/_partial/sidebar/menu.ejs +59 -14
- package/layout/_partial/sidebar/search.ejs +58 -41
- package/layout/_partial/widgets/actions.ejs +25 -0
- package/layout/_partial/widgets/author.ejs +9 -5
- package/layout/_partial/widgets/components/linklist.ejs +1 -2
- package/layout/_partial/widgets/ghissues.ejs +7 -10
- package/layout/_partial/widgets/ghrepo.ejs +23 -16
- package/layout/_partial/widgets/ghuser.ejs +3 -3
- package/layout/_partial/widgets/recent.ejs +25 -21
- package/layout/_partial/widgets/related.ejs +25 -34
- package/layout/_partial/widgets/settings.ejs +17 -0
- package/layout/_partial/widgets/tagtree.ejs +18 -10
- package/layout/_partial/widgets/toc.ejs +17 -9
- package/layout/_partial/widgets/tree.ejs +12 -13
- package/layout/archive.ejs +20 -13
- package/layout/categories.ejs +4 -3
- package/layout/collection-preview.ejs +8 -9
- package/layout/index.ejs +32 -43
- package/layout/index_topic.ejs +6 -13
- package/layout/index_wiki.ejs +17 -34
- package/layout/layout.ejs +195 -65
- package/layout/notebooks.ejs +3 -3
- package/layout/notes.ejs +4 -4
- package/layout/page.ejs +95 -50
- package/layout/settings.ejs +109 -0
- package/layout/tags.ejs +4 -3
- package/{THIRD-PARTY-NOTICES.md → legal/THIRD-PARTY-NOTICES.md} +8 -4
- package/package.json +28 -6
- package/scripts/commands/stellar.js +55 -0
- package/scripts/events/index.js +15 -9
- package/scripts/events/lib/authors.js +9 -7
- package/scripts/events/lib/config-hot-reload.js +65 -0
- package/scripts/events/lib/config-schema.js +20 -0
- package/scripts/events/lib/config.js +22 -63
- package/scripts/events/lib/content-config.js +5 -0
- package/scripts/events/lib/doc_tree.js +99 -9
- package/scripts/events/lib/links.js +6 -3
- package/scripts/events/lib/notebooks.js +200 -8
- package/scripts/events/lib/utils.js +4 -4
- package/scripts/events/lib/version-check.js +57 -3
- package/scripts/filters/index.js +2 -27
- package/scripts/filters/lib/img_lazyload.js +4 -126
- package/scripts/filters/lib/img_onerror.js +14 -17
- package/scripts/filters/lib/page-view-model.js +152 -0
- package/scripts/generators/404.js +13 -8
- package/scripts/generators/author.js +16 -11
- package/scripts/generators/notebooks.js +68 -39
- package/scripts/generators/search.js +62 -67
- package/scripts/generators/settings.js +43 -0
- package/scripts/generators/stellar-icons.js +13 -5
- package/scripts/generators/topic.js +24 -13
- package/scripts/generators/wiki.js +49 -21
- package/scripts/helpers/ai_label.js +6 -12
- package/scripts/helpers/brand.js +17 -0
- package/scripts/helpers/browser_runtime_manifest.js +12 -0
- package/scripts/helpers/caption.js +8 -0
- package/scripts/helpers/category_color.js +13 -13
- package/scripts/helpers/collection.js +18 -0
- package/scripts/helpers/comments_model.js +14 -0
- package/scripts/helpers/extension_assets.js +8 -0
- package/scripts/helpers/json_ld.js +16 -7
- package/scripts/helpers/mdrender.js +3 -9
- package/scripts/helpers/menu.js +9 -0
- package/scripts/helpers/post_view_model.js +23 -0
- package/scripts/helpers/regions.js +37 -0
- package/scripts/helpers/scrollreveal.js +7 -7
- package/scripts/helpers/search_domain.js +34 -0
- package/scripts/helpers/service_provider.js +9 -0
- package/scripts/helpers/settings.js +16 -0
- package/scripts/helpers/share_action.js +8 -0
- package/scripts/helpers/stellar_config.js +12 -0
- package/scripts/helpers/stellar_data.js +8 -0
- package/scripts/helpers/stellar_info.js +21 -16
- package/scripts/helpers/stellar_inject.js +9 -0
- package/scripts/helpers/ui_classes.js +12 -0
- package/scripts/lib/ai_label.js +14 -11
- package/scripts/lib/brand.js +41 -0
- package/scripts/lib/browser-runtime.js +115 -0
- package/scripts/lib/caption.js +21 -0
- package/scripts/lib/collection-pipeline/adapters/notebook.js +62 -0
- package/scripts/lib/collection-pipeline/adapters/post.js +20 -0
- package/scripts/lib/collection-pipeline/adapters/topic.js +82 -0
- package/scripts/lib/collection-pipeline/adapters/wiki.js +20 -0
- package/scripts/lib/collection-pipeline/index.js +169 -0
- package/scripts/lib/collection-pipeline/registry.js +27 -0
- package/scripts/lib/collection-pipeline/shared.js +164 -0
- package/scripts/lib/comments.js +60 -0
- package/scripts/lib/config-inject.js +20 -0
- package/scripts/lib/config-schema.js +1095 -0
- package/scripts/lib/content-config.js +157 -0
- package/scripts/lib/content-defaults.js +56 -0
- package/scripts/lib/content-membership.js +233 -0
- package/scripts/lib/contribution-contract.js +165 -0
- package/scripts/lib/contribution-registry.js +429 -0
- package/scripts/lib/doc_tree.js +46 -44
- package/scripts/lib/doctor.js +296 -0
- package/scripts/lib/front-matter.js +29 -0
- package/scripts/lib/hero-effect-registry.js +262 -0
- package/scripts/lib/html-images.js +128 -0
- package/scripts/lib/internal-constants.js +152 -0
- package/scripts/lib/layout-config.js +62 -0
- package/scripts/lib/mdrender_html.js +36 -23
- package/scripts/lib/menu.js +36 -0
- package/scripts/lib/model-schema.js +90 -0
- package/scripts/lib/models/index.js +1542 -0
- package/scripts/lib/new-note.js +172 -0
- package/scripts/lib/page-view-model-registry.js +103 -0
- package/scripts/lib/regions.js +132 -0
- package/scripts/lib/runtime-data.js +23 -0
- package/scripts/lib/safe-path.js +44 -0
- package/scripts/lib/search-domain.js +59 -0
- package/scripts/lib/seo.js +10 -10
- package/scripts/lib/service-provider.js +12 -0
- package/scripts/lib/settings.js +16 -0
- package/scripts/lib/share-services.js +96 -0
- package/scripts/lib/source-config.js +27 -0
- package/scripts/lib/theme-metadata.js +22 -0
- package/scripts/lib/ui-capabilities.js +32 -0
- package/scripts/lib/widget-registry.js +150 -0
- package/scripts/lib/wiki_readme.js +15 -11
- package/scripts/schema/config-rules.js +290 -0
- package/scripts/schema/config-schema.js +163 -0
- package/scripts/schema/content-config-rules.js +264 -0
- package/scripts/schema/content-config-schema.js +321 -0
- package/scripts/schema/model-schema.js +971 -0
- package/scripts/schema/schema-utils.js +48 -0
- package/scripts/tags/index.js +2 -3
- package/scripts/tags/lib/albums.js +6 -6
- package/scripts/tags/lib/banner.js +10 -4
- package/scripts/tags/lib/box.js +1 -1
- package/scripts/tags/lib/button.js +1 -1
- package/scripts/tags/lib/chat.js +9 -31
- package/scripts/tags/lib/checkbox.js +6 -1
- package/scripts/tags/lib/copy.js +4 -1
- package/scripts/tags/lib/dropdown.js +6 -4
- package/scripts/tags/lib/emoji.js +5 -10
- package/scripts/tags/lib/friends.js +4 -4
- package/scripts/tags/lib/gallery.js +7 -7
- package/scripts/tags/lib/ghcard.js +5 -3
- package/scripts/tags/lib/gist.js +16 -0
- package/scripts/tags/lib/grid.js +4 -2
- package/scripts/tags/lib/hashtag.js +1 -1
- package/scripts/tags/lib/icon.js +1 -1
- package/scripts/tags/lib/image.js +1 -1
- package/scripts/tags/lib/link.js +6 -3
- package/scripts/tags/lib/mark.js +8 -3
- package/scripts/tags/lib/md.js +1 -1
- package/scripts/tags/lib/note.js +1 -1
- package/scripts/tags/lib/okr.js +18 -1
- package/scripts/tags/lib/posters.js +6 -6
- package/scripts/tags/lib/quot.js +15 -5
- package/scripts/tags/lib/rating.js +7 -4
- package/scripts/tags/lib/sites.js +8 -6
- package/scripts/tags/lib/tip.js +6 -6
- package/scripts/tags/lib/toc.js +2 -2
- package/scripts/tags/lib/vote.js +8 -5
- package/source/css/_appearances/_mixins.styl +258 -0
- package/source/css/_appearances/card.styl +30 -0
- package/source/css/_appearances/flat.styl +44 -0
- package/source/css/_appearances/glass.styl +138 -0
- package/source/css/_appearances/minimal.styl +325 -0
- package/source/css/_common/base.styl +5 -9
- package/source/css/_common/blockquote.styl +0 -1
- package/source/css/_common/button.styl +1 -1
- package/source/css/_common/device.styl +24 -42
- package/source/css/_common/dropdown.styl +20 -14
- package/source/css/_common/highlight.styl +5 -12
- package/source/css/_common/icon.styl +38 -0
- package/source/css/_common/input.styl +33 -0
- package/source/css/_common/loading.styl +2 -2
- package/source/css/_common/svg.styl +0 -8
- package/source/css/_components/collection.styl +21 -64
- package/source/css/_components/layout.styl +524 -84
- package/source/css/_components/list.styl +36 -22
- package/source/css/_components/main.styl +16 -19
- package/source/css/_components/md.styl +8 -13
- package/source/css/_components/pages/article-indent.styl +1 -1
- package/source/css/_components/pages/article-story.styl +7 -6
- package/source/css/_components/pages/article-tech.styl +5 -5
- package/source/css/_components/pages/collection-preview.styl +1 -5
- package/source/css/_components/pages/settings.styl +184 -0
- package/source/css/_components/partial/article-banner.styl +2 -2
- package/source/css/_components/partial/article-footer.styl +0 -32
- package/source/css/_components/partial/article-share.styl +48 -0
- package/source/css/_components/partial/article-tags.styl +1 -1
- package/source/css/_components/partial/cover.styl +13 -13
- package/source/css/_components/partial/listing-nav.styl +68 -0
- package/source/css/_components/partial/paginator.styl +1 -1
- package/source/css/_components/pin-slider.styl +3 -3
- package/source/css/_components/sidebar/brand.styl +135 -0
- package/source/css/_components/sidebar/footer.styl +14 -31
- package/source/css/_components/sidebar/nav-area.styl +4 -0
- package/source/css/_components/sidebar/search.styl +176 -26
- package/source/css/_components/sidebar/sidebar.styl +76 -110
- package/source/css/_components/tag-plugins/banner.styl +16 -28
- package/source/css/_components/tag-plugins/checkbox.styl +7 -22
- package/source/css/_components/tag-plugins/copy.styl +3 -5
- package/source/css/_components/tag-plugins/gallery.styl +2 -2
- package/source/css/_components/tag-plugins/grid.styl +3 -0
- package/source/css/_components/tag-plugins/image.styl +2 -1
- package/source/css/_components/tag-plugins/link.styl +0 -1
- package/source/css/_components/tag-plugins/mark.styl +19 -4
- package/source/css/_components/tag-plugins/mdrender.styl +2 -2
- package/source/css/_components/tag-plugins/navbar.styl +23 -17
- package/source/css/_components/tag-plugins/note.styl +2 -2
- package/source/css/_components/tag-plugins/okr.styl +1 -2
- package/source/css/_components/tag-plugins/quot.styl +5 -0
- package/source/css/_components/tag-plugins/rating.styl +3 -2
- package/source/css/_components/tag-plugins/timeline.styl +1 -1
- package/source/css/_components/tag-plugins/tip.styl +1 -1
- package/source/css/_components/tag-plugins/vote.styl +3 -2
- package/source/css/_components/widgets/ghrepo.styl +2 -1
- package/source/css/_components/widgets/ghuser.styl +4 -6
- package/source/css/_components/widgets/settings.styl +49 -0
- package/source/css/_components/widgets/timeline.styl +2 -2
- package/source/css/_components/widgets/toc.styl +12 -33
- package/source/css/_components/widgets/widgets.styl +14 -19
- package/source/css/_components/wiki-card.styl +2 -2
- package/source/css/_custom.styl +26 -32
- package/source/css/_defines/func.styl +47 -37
- package/source/css/_defines/theme_base.styl +8 -10
- package/source/css/_plugins/card-hover.styl +1 -1
- package/source/css/_plugins/copycode.styl +16 -19
- package/source/css/_plugins/index.styl +3 -8
- package/source/css/_plugins/lazyload.styl +1 -1
- package/source/css/main.styl +12 -0
- package/source/js/main.js +302 -59
- package/source/js/plugins/adaptive-text.js +33 -16
- package/source/js/plugins/card-hover.js +2 -24
- package/source/js/plugins/copycode.js +10 -16
- package/source/js/plugins/voice.js +51 -48
- package/source/js/profile.js +316 -0
- package/source/js/runtime/asset-loader.js +61 -0
- package/source/js/runtime/extension-registry.js +149 -0
- package/source/js/runtime/extensions/card-hover.js +5 -0
- package/source/js/runtime/extensions/color-scheme-switch.js +101 -0
- package/source/js/runtime/extensions/comments.js +200 -0
- package/source/js/runtime/extensions/deferred-icons.js +53 -0
- package/source/js/{plugins → runtime/extensions}/dropdown.js +69 -30
- package/source/js/runtime/extensions/feature.js +200 -0
- package/source/js/runtime/extensions/hero-effect.js +179 -0
- package/source/js/runtime/extensions/reveal.js +80 -0
- package/source/js/runtime/extensions/search.js +23 -0
- package/source/js/{services.js → runtime/extensions/services.js} +67 -54
- package/source/js/runtime/extensions/settings.js +235 -0
- package/source/js/runtime/hero-effects/ferrofluid.js +385 -0
- package/source/js/{plugins → runtime/hero-effects}/galaxy.js +53 -177
- package/source/js/runtime/hero-effects/light-rays.js +293 -0
- package/source/js/runtime/index.js +75 -0
- package/source/js/runtime/legacy-request-adapter.js +47 -0
- package/source/js/runtime/request-cache.js +219 -0
- package/source/js/search/algolia-search.js +76 -51
- package/source/js/search/highlight.js +1 -1
- package/source/js/search/local-search.js +96 -57
- package/source/js/search/shortcut.js +31 -20
- package/source/js/services/friends_and_posts.js +6 -1
- package/source/js/services/rating.js +14 -6
- package/source/js/services/siteinfo.js +3 -7
- package/source/js/services/timeline.js +1 -1
- package/source/js/services/vote.js +7 -5
- package/source/js/services/weibo.js +1 -1
- package/source/js/utils.js +19 -444
- package/.claude/skills/stellar-theme-dev/SKILL.md +0 -51
- package/_data/chat_users.yml +0 -0
- package/layout/_partial/comments/artalk/script.ejs +0 -76
- package/layout/_partial/comments/beaudar/script.ejs +0 -33
- package/layout/_partial/comments/giscus/script.ejs +0 -31
- package/layout/_partial/comments/script.ejs +0 -3
- package/layout/_partial/comments/twikoo/script.ejs +0 -25
- package/layout/_partial/comments/utterances/script.ejs +0 -33
- package/layout/_partial/comments/waline/script.ejs +0 -43
- package/layout/_partial/main/article/article_footer.ejs +0 -164
- package/layout/_partial/main/article/article_tags.ejs +0 -16
- package/layout/_partial/main/article/read_next.ejs +0 -63
- package/layout/_partial/main/article/related_posts.ejs +0 -16
- package/layout/_partial/main/navbar/nav_tabs_wiki.ejs +0 -52
- package/layout/_partial/scripts/bootstrap.ejs +0 -37
- package/layout/_partial/scripts/lazyload.ejs +0 -89
- package/layout/_partial/sidebar/grad-def.ejs +0 -26
- package/layout/_partial/sidebar/index_leftbar.ejs +0 -191
- package/layout/_partial/sidebar/index_rightbar.ejs +0 -94
- package/layout/_partial/sidebar/logo.ejs +0 -99
- package/layout/_plugins/adaptive_text.ejs +0 -11
- package/layout/_plugins/card_hover.ejs +0 -32
- package/layout/_plugins/copycode.ejs +0 -17
- package/layout/_plugins/fancybox.ejs +0 -53
- package/layout/_plugins/heti.ejs +0 -21
- package/layout/_plugins/index.ejs +0 -50
- package/layout/_plugins/mathjax.ejs +0 -49
- package/layout/_plugins/mermaid.ejs +0 -33
- package/layout/_plugins/preload.ejs +0 -9
- package/layout/_plugins/scrollreveal.ejs +0 -102
- package/layout/_plugins/search/algolia_search.ejs +0 -12
- package/layout/_plugins/search/local_search.ejs +0 -9
- package/layout/_plugins/swiper.ejs +0 -27
- package/layout/_plugins/tianli_gpt.ejs +0 -14
- package/scripts/commands/new-note.js +0 -49
- package/scripts/events/lib/merge_posts.js +0 -48
- package/scripts/events/lib/topic_tree.js +0 -48
- package/scripts/helpers/related_posts.js +0 -51
- package/scripts/helpers/subtitle.js +0 -8
- package/scripts/lib/notebooks.js +0 -181
- package/scripts/lib/subtitle.js +0 -28
- package/scripts/tags/lib/about.js +0 -48
- package/source/css/_components/page-transition.styl +0 -22
- package/source/css/_components/partial/navbar.styl +0 -51
- package/source/css/_components/sidebar/logo.styl +0 -111
- package/source/css/_components/tag-plugins/about.styl +0 -56
- package/source/css/_components/widgets/tree.styl +0 -0
- package/source/css/_plugins/scrollreveal.styl +0 -6
- package/source/css/_plugins/tianli_gpt.styl +0 -213
- package/source/css/plugins/mermaid.styl +0 -137
- package/source/js/icons.js +0 -60
- package/source/js/services/fcircle.js +0 -35
- package/source/js/theme.js +0 -61
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
function isDarkColorScheme(documentRef, mediaQuery) {
|
|
2
|
+
const explicit = documentRef.documentElement.getAttribute('data-theme');
|
|
3
|
+
if (explicit === 'dark') return true;
|
|
4
|
+
if (explicit === 'light') return false;
|
|
5
|
+
return mediaQuery.matches;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function listenColorScheme(documentRef, mediaQuery, callback) {
|
|
9
|
+
documentRef.addEventListener('stellar:color-scheme-change', callback);
|
|
10
|
+
if (typeof mediaQuery.addEventListener === 'function') {
|
|
11
|
+
mediaQuery.addEventListener('change', callback);
|
|
12
|
+
} else if (typeof mediaQuery.addListener === 'function') {
|
|
13
|
+
mediaQuery.addListener(callback);
|
|
14
|
+
}
|
|
15
|
+
return function cleanup() {
|
|
16
|
+
documentRef.removeEventListener('stellar:color-scheme-change', callback);
|
|
17
|
+
if (typeof mediaQuery.removeEventListener === 'function') {
|
|
18
|
+
mediaQuery.removeEventListener('change', callback);
|
|
19
|
+
} else if (typeof mediaQuery.removeListener === 'function') {
|
|
20
|
+
mediaQuery.removeListener(callback);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
1
25
|
/**该方法用来绘制一个有填充色的圆角矩形
|
|
2
26
|
*@param cxt:canvas的上下文环境
|
|
3
27
|
*@param x:左上角x轴坐标
|
|
@@ -152,6 +176,7 @@ function drawPlayLine(context, playLineHeight, playLineX, playLineColor) {
|
|
|
152
176
|
}
|
|
153
177
|
|
|
154
178
|
function createVoiceDom(audios) {
|
|
179
|
+
const cleanups = [];
|
|
155
180
|
for (let i = 0; i < audios.length; ++i) {
|
|
156
181
|
let audio = audios[i];
|
|
157
182
|
audio.loop = false;
|
|
@@ -159,13 +184,17 @@ function createVoiceDom(audios) {
|
|
|
159
184
|
|
|
160
185
|
if (audio.classList.contains('qq')) {
|
|
161
186
|
if (isNaN(audio.duration) || audio.duration === Infinity) {
|
|
162
|
-
|
|
187
|
+
const handleDurationChange = ()=>{
|
|
163
188
|
// createCssStyle(audio, i);
|
|
164
|
-
createWaveDom(audio);
|
|
165
|
-
|
|
189
|
+
const cleanup = createWaveDom(audio);
|
|
190
|
+
if (cleanup) cleanups.push(cleanup);
|
|
191
|
+
};
|
|
192
|
+
audio.addEventListener('durationchange', handleDurationChange, { once: true });
|
|
193
|
+
cleanups.push(() => audio.removeEventListener('durationchange', handleDurationChange));
|
|
166
194
|
} else {
|
|
167
195
|
// createCssStyle(audio, i);
|
|
168
|
-
createWaveDom(audio);
|
|
196
|
+
const cleanup = createWaveDom(audio);
|
|
197
|
+
if (cleanup) cleanups.push(cleanup);
|
|
169
198
|
}
|
|
170
199
|
} else if (audio.classList.contains('wechat')) {
|
|
171
200
|
if (isNaN(audio.duration) || audio.duration === Infinity) {
|
|
@@ -177,6 +206,10 @@ function createVoiceDom(audios) {
|
|
|
177
206
|
}
|
|
178
207
|
}
|
|
179
208
|
}
|
|
209
|
+
return function cleanup() {
|
|
210
|
+
for (let index = cleanups.length - 1; index >= 0; index -= 1) cleanups[index]();
|
|
211
|
+
cleanups.length = 0;
|
|
212
|
+
};
|
|
180
213
|
}
|
|
181
214
|
|
|
182
215
|
// cancas 效果更好
|
|
@@ -213,22 +246,20 @@ function createWaveDom(audio) {
|
|
|
213
246
|
var context = audioCanvas.getContext("2d"); //获取画布context的上下文环境
|
|
214
247
|
context.scale(dpr, 1);
|
|
215
248
|
|
|
216
|
-
|
|
217
|
-
let
|
|
249
|
+
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
250
|
+
let activeWaveColor;
|
|
251
|
+
let inactiveWaveColor;
|
|
218
252
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (utils.dark.mode === "dark") {
|
|
225
|
-
activeWaveColor = '#ccc';
|
|
226
|
-
inactiveWaveColor = '#707070';
|
|
253
|
+
function updateWaveColors() {
|
|
254
|
+
const dark = isDarkColorScheme(document, mediaQuery);
|
|
255
|
+
activeWaveColor = dark ? '#ccc' : '#333';
|
|
256
|
+
inactiveWaveColor = dark ? '#707070' : '#999';
|
|
227
257
|
if (audioCanvas.classList.contains('right')) {
|
|
228
|
-
activeWaveColor = '#fff9';
|
|
229
|
-
inactiveWaveColor = '#6797b780';
|
|
258
|
+
activeWaveColor = dark ? '#fff9' : '#ffffff';
|
|
259
|
+
inactiveWaveColor = dark ? '#6797b780' : '#1373b3';
|
|
230
260
|
}
|
|
231
261
|
}
|
|
262
|
+
updateWaveColors();
|
|
232
263
|
|
|
233
264
|
let playFlag = false;
|
|
234
265
|
|
|
@@ -237,21 +268,7 @@ function createWaveDom(audio) {
|
|
|
237
268
|
|
|
238
269
|
audio.addEventListener('timeupdate', ()=>{
|
|
239
270
|
if (audio.currentTime > 0) {
|
|
240
|
-
|
|
241
|
-
activeWaveColor = '#ccc';
|
|
242
|
-
inactiveWaveColor = '#707070';
|
|
243
|
-
if (audioCanvas.classList.contains('right')) {
|
|
244
|
-
activeWaveColor = '#fff9';
|
|
245
|
-
inactiveWaveColor = '#6797b780';
|
|
246
|
-
}
|
|
247
|
-
} else {
|
|
248
|
-
activeWaveColor = '#333';
|
|
249
|
-
inactiveWaveColor = '#999';
|
|
250
|
-
if (audioCanvas.classList.contains('right')) {
|
|
251
|
-
activeWaveColor = '#ffffff';
|
|
252
|
-
inactiveWaveColor = '#1373b3';
|
|
253
|
-
}
|
|
254
|
-
}
|
|
271
|
+
updateWaveColors();
|
|
255
272
|
utils.requestAnimationFrame(()=>{
|
|
256
273
|
context.clearRect(0, 0, audioCanvas.width, audioCanvas.height);
|
|
257
274
|
drawWaveWhenPlaying(context,audioCanvas.height,(audio.currentTime / audio.duration) * audioCanvas.width / dpr,waveHeightArr,1,activeWaveColor,inactiveWaveColor);
|
|
@@ -285,23 +302,9 @@ function createWaveDom(audio) {
|
|
|
285
302
|
}
|
|
286
303
|
});
|
|
287
304
|
|
|
288
|
-
|
|
305
|
+
const cleanupColorScheme = listenColorScheme(document, mediaQuery, function () {
|
|
289
306
|
if (!playFlag) {
|
|
290
|
-
|
|
291
|
-
activeWaveColor = '#ccc';
|
|
292
|
-
inactiveWaveColor = '#707070';
|
|
293
|
-
if (audioCanvas.classList.contains('right')) {
|
|
294
|
-
activeWaveColor = '#fff9';
|
|
295
|
-
inactiveWaveColor = '#6797b780';
|
|
296
|
-
}
|
|
297
|
-
} else {
|
|
298
|
-
activeWaveColor = '#333';
|
|
299
|
-
inactiveWaveColor = '#999';
|
|
300
|
-
if (audioCanvas.classList.contains('right')) {
|
|
301
|
-
activeWaveColor = '#ffffff';
|
|
302
|
-
inactiveWaveColor = '#1373b3';
|
|
303
|
-
}
|
|
304
|
-
}
|
|
307
|
+
updateWaveColors();
|
|
305
308
|
if (audio.currentTime > 0) {
|
|
306
309
|
utils.requestAnimationFrame(()=>{
|
|
307
310
|
context.clearRect(0, 0, audioCanvas.width, audioCanvas.height);
|
|
@@ -316,7 +319,7 @@ function createWaveDom(audio) {
|
|
|
316
319
|
}
|
|
317
320
|
}
|
|
318
321
|
});
|
|
319
|
-
|
|
322
|
+
return cleanupColorScheme;
|
|
320
323
|
} else {
|
|
321
324
|
console.log("浏览器不支持canvas");
|
|
322
325
|
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
var SUPPORTED_PROVIDERS = ['artalk', 'waline', 'twikoo'];
|
|
3
|
+
var SETTINGS_ICON = 'default:settings';
|
|
4
|
+
var PROFILE_ICON = 'default:profile';
|
|
5
|
+
var GRAVATAR_BASE_URL = 'https://gravatar.com/avatar/';
|
|
6
|
+
var localStore = null;
|
|
7
|
+
var sessionStore = null;
|
|
8
|
+
var gravatarHashes = new Map();
|
|
9
|
+
var widgetRenderRevisions = new WeakMap();
|
|
10
|
+
try { localStore = localStorage; } catch (error) {}
|
|
11
|
+
try { sessionStore = sessionStorage; } catch (error) {}
|
|
12
|
+
|
|
13
|
+
function parseStorage(store, key) {
|
|
14
|
+
try {
|
|
15
|
+
var raw = store?.getItem?.(key);
|
|
16
|
+
if (!raw) return null;
|
|
17
|
+
var value = JSON.parse(raw);
|
|
18
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : null;
|
|
19
|
+
} catch (error) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function hasStorageValue(store, key) {
|
|
25
|
+
try { return store?.getItem?.(key) != null; } catch (error) { return false; }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function writeStorage(store, key, value) {
|
|
29
|
+
try {
|
|
30
|
+
store?.setItem?.(key, JSON.stringify(value));
|
|
31
|
+
return !!store?.setItem;
|
|
32
|
+
} catch (error) { return false; }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function removeStorage(store, key) {
|
|
36
|
+
try {
|
|
37
|
+
store?.removeItem?.(key);
|
|
38
|
+
return !!store?.removeItem;
|
|
39
|
+
} catch (error) { return false; }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function cleanText(value, maxLength) {
|
|
43
|
+
return typeof value === 'string' ? value.trim().slice(0, maxLength || 80) : '';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function cleanEmail(value) {
|
|
47
|
+
var email = cleanText(value, 254);
|
|
48
|
+
if (!email) return '';
|
|
49
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) ? email : null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function cleanHttpUrl(value) {
|
|
53
|
+
if (typeof value !== 'string' || value.trim().length === 0) return '';
|
|
54
|
+
var candidate = value.trim();
|
|
55
|
+
if (candidate.startsWith('//')) candidate = 'https:' + candidate;
|
|
56
|
+
try {
|
|
57
|
+
var parsed = new URL(candidate);
|
|
58
|
+
return parsed.protocol === 'http:' || parsed.protocol === 'https:' ? parsed.href : null;
|
|
59
|
+
} catch (error) { return null; }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function normalizeGravatarEmail(value) {
|
|
63
|
+
var email = cleanEmail(value);
|
|
64
|
+
return email ? email.toLowerCase() : '';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function hashToHex(buffer) {
|
|
68
|
+
return Array.from(new Uint8Array(buffer), function (byte) {
|
|
69
|
+
return byte.toString(16).padStart(2, '0');
|
|
70
|
+
}).join('');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getGravatarHash(email) {
|
|
74
|
+
var normalized = normalizeGravatarEmail(email);
|
|
75
|
+
if (!normalized) return Promise.resolve('');
|
|
76
|
+
if (gravatarHashes.has(normalized)) return gravatarHashes.get(normalized);
|
|
77
|
+
var cryptoApi = window.crypto;
|
|
78
|
+
if (!cryptoApi?.subtle?.digest || typeof TextEncoder !== 'function') return Promise.resolve('');
|
|
79
|
+
var promise;
|
|
80
|
+
try {
|
|
81
|
+
promise = Promise.resolve(cryptoApi.subtle.digest('SHA-256', new TextEncoder().encode(normalized)))
|
|
82
|
+
.then(hashToHex)
|
|
83
|
+
.catch(function () { return ''; });
|
|
84
|
+
} catch (error) {
|
|
85
|
+
promise = Promise.resolve('');
|
|
86
|
+
}
|
|
87
|
+
gravatarHashes.set(normalized, promise);
|
|
88
|
+
return promise;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function getGravatarUrl(email, size) {
|
|
92
|
+
var requestedSize = Math.round(Number(size));
|
|
93
|
+
var safeSize = Number.isFinite(requestedSize) ? Math.min(2048, Math.max(1, requestedSize)) : 80;
|
|
94
|
+
return getGravatarHash(email).then(function (hash) {
|
|
95
|
+
return hash ? GRAVATAR_BASE_URL + hash + '?s=' + safeSize + '&r=g&d=404' : '';
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function emptyIdentity(provider) {
|
|
100
|
+
var id = String(provider || '').toLowerCase();
|
|
101
|
+
return { provider: id, supported: SUPPORTED_PROVIDERS.includes(id), name: '', email: '', url: '', avatar: '' };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function readIdentity(provider) {
|
|
105
|
+
var id = String(provider || '').toLowerCase();
|
|
106
|
+
var identity = emptyIdentity(id);
|
|
107
|
+
var value = null;
|
|
108
|
+
if (id === 'artalk') {
|
|
109
|
+
value = parseStorage(localStore, 'ArtalkUser') || {};
|
|
110
|
+
identity.name = cleanText(value.name || value.nick);
|
|
111
|
+
identity.email = cleanText(value.email || value.mail, 254);
|
|
112
|
+
identity.url = cleanHttpUrl(value.link || value.url) || '';
|
|
113
|
+
identity.avatar = cleanHttpUrl(value.avatar) || '';
|
|
114
|
+
return identity;
|
|
115
|
+
}
|
|
116
|
+
if (id === 'waline') {
|
|
117
|
+
value = parseStorage(sessionStore, 'WALINE_USER') || parseStorage(localStore, 'WALINE_USER') || {};
|
|
118
|
+
var meta = parseStorage(sessionStore, 'WALINE_USER_META') || parseStorage(localStore, 'WALINE_USER_META') || {};
|
|
119
|
+
identity.name = cleanText(value.display_name || value.nick || meta.nick);
|
|
120
|
+
identity.email = cleanText(value.email || value.mail || meta.mail, 254);
|
|
121
|
+
identity.url = cleanHttpUrl(value.url || value.link || meta.link) || '';
|
|
122
|
+
identity.avatar = cleanHttpUrl(value.avatar || meta.avatar) || '';
|
|
123
|
+
return identity;
|
|
124
|
+
}
|
|
125
|
+
if (id === 'twikoo') {
|
|
126
|
+
value = parseStorage(localStore, 'twikoo') || {};
|
|
127
|
+
identity.name = cleanText(value.nick);
|
|
128
|
+
identity.email = cleanText(value.mail, 254);
|
|
129
|
+
identity.url = cleanHttpUrl(value.link) || '';
|
|
130
|
+
identity.avatar = cleanHttpUrl(value.avatar) || '';
|
|
131
|
+
return identity;
|
|
132
|
+
}
|
|
133
|
+
return identity;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function dispatchChange(provider) {
|
|
137
|
+
try {
|
|
138
|
+
window.dispatchEvent?.(new CustomEvent('stellar:profile-change', { detail: { provider: provider } }));
|
|
139
|
+
} catch (error) {
|
|
140
|
+
window.dispatchEvent?.({ type: 'stellar:profile-change', detail: { provider: provider } });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function updateExistingCopies(key, updater) {
|
|
145
|
+
return [localStore, sessionStore]
|
|
146
|
+
.filter(function (store) { return hasStorageValue(store, key); })
|
|
147
|
+
.map(function (store) { return writeStorage(store, key, updater(parseStorage(store, key) || {})); });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function writeIdentity(provider, fields) {
|
|
151
|
+
var id = String(provider || '').toLowerCase();
|
|
152
|
+
if (!SUPPORTED_PROVIDERS.includes(id)) return { ok: false, unsupported: true, partial: false };
|
|
153
|
+
var rawName = typeof fields?.name === 'string' ? fields.name.trim() : '';
|
|
154
|
+
var name = cleanText(rawName);
|
|
155
|
+
var email = cleanEmail(fields?.email);
|
|
156
|
+
var url = cleanHttpUrl(fields?.url);
|
|
157
|
+
if (!name || rawName.length > 80) return { ok: false, validation: 'name', partial: false };
|
|
158
|
+
if (email === null) return { ok: false, validation: 'email', partial: false };
|
|
159
|
+
if (url === null) return { ok: false, validation: 'url', partial: false };
|
|
160
|
+
|
|
161
|
+
var results = [];
|
|
162
|
+
if (id === 'artalk') {
|
|
163
|
+
var artalk = parseStorage(localStore, 'ArtalkUser') || {};
|
|
164
|
+
results.push(writeStorage(localStore, 'ArtalkUser', Object.assign({}, artalk, { name: name, email: email, link: url })));
|
|
165
|
+
} else if (id === 'twikoo') {
|
|
166
|
+
var twikoo = parseStorage(localStore, 'twikoo') || {};
|
|
167
|
+
results.push(writeStorage(localStore, 'twikoo', Object.assign({}, twikoo, { nick: name, mail: email, link: url })));
|
|
168
|
+
} else {
|
|
169
|
+
results = results.concat(
|
|
170
|
+
updateExistingCopies('WALINE_USER', function (current) { return Object.assign({}, current, { display_name: name, email: email, url: url }); }),
|
|
171
|
+
updateExistingCopies('WALINE_USER_META', function (current) { return Object.assign({}, current, { nick: name, mail: email, link: url }); })
|
|
172
|
+
);
|
|
173
|
+
if (results.length === 0) results.push(writeStorage(localStore, 'WALINE_USER_META', { nick: name, mail: email, link: url }));
|
|
174
|
+
}
|
|
175
|
+
var succeeded = results.filter(Boolean).length;
|
|
176
|
+
var outcome = { ok: succeeded === results.length && succeeded > 0, partial: succeeded > 0 && succeeded < results.length };
|
|
177
|
+
if (succeeded > 0) dispatchChange(id);
|
|
178
|
+
return outcome;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function logout(provider) {
|
|
182
|
+
var id = String(provider || '').toLowerCase();
|
|
183
|
+
if (!SUPPORTED_PROVIDERS.includes(id)) return { ok: false, unsupported: true, partial: false };
|
|
184
|
+
var targets = id === 'artalk'
|
|
185
|
+
? [[localStore, 'ArtalkUser']]
|
|
186
|
+
: id === 'twikoo'
|
|
187
|
+
? [[localStore, 'twikoo']]
|
|
188
|
+
: [[localStore, 'WALINE_USER'], [sessionStore, 'WALINE_USER'], [localStore, 'WALINE_USER_META'], [sessionStore, 'WALINE_USER_META']];
|
|
189
|
+
var results = targets.map(function (target) { return removeStorage(target[0], target[1]); });
|
|
190
|
+
var succeeded = results.filter(Boolean).length;
|
|
191
|
+
var outcome = { ok: succeeded === results.length, partial: succeeded > 0 && succeeded < results.length };
|
|
192
|
+
if (succeeded > 0) dispatchChange(id);
|
|
193
|
+
return outcome;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function renderIconMedia(avatar, key) {
|
|
197
|
+
var media = document.createElement('span');
|
|
198
|
+
media.className = 'ui-icon';
|
|
199
|
+
media.innerHTML = window.stellarIcons?.[key] || '';
|
|
200
|
+
avatar.replaceChildren(media);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function renderImageMedia(avatar, url, onError) {
|
|
204
|
+
var media = document.createElement('img');
|
|
205
|
+
media.className = 'settings-widget__image';
|
|
206
|
+
media.alt = '';
|
|
207
|
+
media.onerror = onError || function () { renderIconMedia(avatar, PROFILE_ICON); };
|
|
208
|
+
media.src = url;
|
|
209
|
+
avatar.replaceChildren(media);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function isCurrentWidgetRender(widget, revision) {
|
|
213
|
+
return widgetRenderRevisions.get(widget) === revision;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function renderWidgetFallback(widget, avatar, fallbackAvatar, revision) {
|
|
217
|
+
if (!isCurrentWidgetRender(widget, revision)) return;
|
|
218
|
+
if (!fallbackAvatar) {
|
|
219
|
+
renderIconMedia(avatar, PROFILE_ICON);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
renderImageMedia(avatar, fallbackAvatar, function () {
|
|
223
|
+
if (isCurrentWidgetRender(widget, revision)) renderIconMedia(avatar, PROFILE_ICON);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function renderWidgetGravatar(widget, avatar, identity, fallbackAvatar, revision) {
|
|
228
|
+
if (!identity.email) {
|
|
229
|
+
renderWidgetFallback(widget, avatar, fallbackAvatar, revision);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
renderIconMedia(avatar, PROFILE_ICON);
|
|
233
|
+
getGravatarUrl(identity.email, 64).then(function (url) {
|
|
234
|
+
if (!isCurrentWidgetRender(widget, revision)) return;
|
|
235
|
+
if (!url) {
|
|
236
|
+
renderWidgetFallback(widget, avatar, fallbackAvatar, revision);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
renderImageMedia(avatar, url, function () {
|
|
240
|
+
renderWidgetFallback(widget, avatar, fallbackAvatar, revision);
|
|
241
|
+
});
|
|
242
|
+
}, function () {
|
|
243
|
+
renderWidgetFallback(widget, avatar, fallbackAvatar, revision);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function renderWidget(widget) {
|
|
248
|
+
var revision = (widgetRenderRevisions.get(widget) || 0) + 1;
|
|
249
|
+
widgetRenderRevisions.set(widget, revision);
|
|
250
|
+
var settingsName = widget.dataset.settingsLabel || widget.getAttribute('aria-label') || 'Settings';
|
|
251
|
+
var identityEnabled = widget.dataset.profileIdentityEnabled === 'true';
|
|
252
|
+
var identity = readIdentity(widget.dataset.profileProvider);
|
|
253
|
+
var hasIdentity = identityEnabled && identity.supported && !!identity.name;
|
|
254
|
+
var name = hasIdentity ? identity.name : settingsName;
|
|
255
|
+
var nameElement = widget.querySelector('.settings-widget__name');
|
|
256
|
+
var avatar = widget.querySelector('.settings-widget__avatar');
|
|
257
|
+
if (nameElement) nameElement.textContent = name;
|
|
258
|
+
widget.setAttribute('aria-label', name);
|
|
259
|
+
widget.setAttribute('title', name);
|
|
260
|
+
if (!avatar) return;
|
|
261
|
+
if (!hasIdentity) {
|
|
262
|
+
renderIconMedia(avatar, SETTINGS_ICON);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
var fallbackAvatar = widget.dataset.profileFallbackAvatar || '';
|
|
266
|
+
if (identity.avatar) {
|
|
267
|
+
renderImageMedia(avatar, identity.avatar, function () {
|
|
268
|
+
renderWidgetGravatar(widget, avatar, identity, fallbackAvatar, revision);
|
|
269
|
+
});
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
renderWidgetGravatar(widget, avatar, identity, fallbackAvatar, revision);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
var mountedRoots = new WeakMap();
|
|
276
|
+
function mount(root) {
|
|
277
|
+
root = root || document;
|
|
278
|
+
if (mountedRoots.has(root)) return mountedRoots.get(root);
|
|
279
|
+
var refresh = function () { root.querySelectorAll?.('.settings-widget').forEach(renderWidget); };
|
|
280
|
+
var delayedRefresh = function () { setTimeout(refresh, 0); };
|
|
281
|
+
var onInteraction = function (event) {
|
|
282
|
+
if (event.target?.closest?.('#comments, .comments, [class*="comment"]')) delayedRefresh();
|
|
283
|
+
};
|
|
284
|
+
window.addEventListener?.('storage', refresh);
|
|
285
|
+
window.addEventListener?.('focus', refresh);
|
|
286
|
+
window.addEventListener?.('stellar:profile-change', refresh);
|
|
287
|
+
root.addEventListener?.('input', onInteraction);
|
|
288
|
+
root.addEventListener?.('change', onInteraction);
|
|
289
|
+
root.addEventListener?.('submit', onInteraction);
|
|
290
|
+
root.addEventListener?.('click', onInteraction);
|
|
291
|
+
refresh();
|
|
292
|
+
var cleanup = function () {
|
|
293
|
+
window.removeEventListener?.('storage', refresh);
|
|
294
|
+
window.removeEventListener?.('focus', refresh);
|
|
295
|
+
window.removeEventListener?.('stellar:profile-change', refresh);
|
|
296
|
+
root.removeEventListener?.('input', onInteraction);
|
|
297
|
+
root.removeEventListener?.('change', onInteraction);
|
|
298
|
+
root.removeEventListener?.('submit', onInteraction);
|
|
299
|
+
root.removeEventListener?.('click', onInteraction);
|
|
300
|
+
mountedRoots.delete(root);
|
|
301
|
+
};
|
|
302
|
+
mountedRoots.set(root, cleanup);
|
|
303
|
+
return cleanup;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
window.stellarProfile = {
|
|
307
|
+
mount: mount,
|
|
308
|
+
supports: function (provider) { return SUPPORTED_PROVIDERS.includes(String(provider || '').toLowerCase()); },
|
|
309
|
+
getGravatarUrl: getGravatarUrl,
|
|
310
|
+
readIdentity: readIdentity,
|
|
311
|
+
writeIdentity: writeIdentity,
|
|
312
|
+
logout: logout
|
|
313
|
+
};
|
|
314
|
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function () { mount(document); }, { once: true });
|
|
315
|
+
else mount(document);
|
|
316
|
+
})();
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
function resolveAsset(root, value) {
|
|
2
|
+
if (typeof value !== 'string' || value.length === 0) return '';
|
|
3
|
+
if (!value.startsWith('/')) return value;
|
|
4
|
+
const base = typeof root === 'string' && root.length > 0 ? root : '/';
|
|
5
|
+
return `${base.replace(/\/$/, '')}${value}`;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function versionAsset(value, url, version) {
|
|
9
|
+
if (typeof value !== 'string' || !value.startsWith('/') || !version) return url;
|
|
10
|
+
const suffix = version.replace(/^\?/, '');
|
|
11
|
+
return `${url}${url.includes('?') ? '&' : '?'}${suffix}`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function createAssetLoader(options = {}) {
|
|
15
|
+
const documentRef = options.document || document;
|
|
16
|
+
const root = options.root || '/';
|
|
17
|
+
const version = options.version || '';
|
|
18
|
+
const scripts = new Map();
|
|
19
|
+
const styles = new Map();
|
|
20
|
+
|
|
21
|
+
function script(src, attributes = {}) {
|
|
22
|
+
const url = versionAsset(src, resolveAsset(root, src), version);
|
|
23
|
+
if (!url) return Promise.reject(new TypeError('[stellar runtime] script URL is required'));
|
|
24
|
+
if (scripts.has(url)) return scripts.get(url);
|
|
25
|
+
const promise = new Promise((resolve, reject) => {
|
|
26
|
+
const element = documentRef.createElement('script');
|
|
27
|
+
element.src = url;
|
|
28
|
+
element.async = attributes.async !== false;
|
|
29
|
+
Object.keys(attributes).forEach(key => {
|
|
30
|
+
if (key === 'async') return;
|
|
31
|
+
element[key] = attributes[key];
|
|
32
|
+
});
|
|
33
|
+
element.addEventListener('load', () => resolve(element), { once: true });
|
|
34
|
+
element.addEventListener('error', () => reject(new Error(`failed to load ${url}`)), { once: true });
|
|
35
|
+
documentRef.head.appendChild(element);
|
|
36
|
+
});
|
|
37
|
+
scripts.set(url, promise);
|
|
38
|
+
promise.catch(() => scripts.delete(url));
|
|
39
|
+
return promise;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function style(href, attributes = {}) {
|
|
43
|
+
const url = versionAsset(href, resolveAsset(root, href), version);
|
|
44
|
+
if (!url) return Promise.reject(new TypeError('[stellar runtime] style URL is required'));
|
|
45
|
+
if (styles.has(url)) return styles.get(url);
|
|
46
|
+
const promise = new Promise((resolve, reject) => {
|
|
47
|
+
const element = documentRef.createElement('link');
|
|
48
|
+
element.rel = 'stylesheet';
|
|
49
|
+
element.href = url;
|
|
50
|
+
Object.keys(attributes).forEach(key => element.setAttribute(key, attributes[key]));
|
|
51
|
+
element.addEventListener('load', () => resolve(element), { once: true });
|
|
52
|
+
element.addEventListener('error', () => reject(new Error(`failed to load ${url}`)), { once: true });
|
|
53
|
+
documentRef.head.appendChild(element);
|
|
54
|
+
});
|
|
55
|
+
styles.set(url, promise);
|
|
56
|
+
promise.catch(() => styles.delete(url));
|
|
57
|
+
return promise;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return Object.freeze({ script, style, resolve: value => resolveAsset(root, value) });
|
|
61
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
const PHASES = new Set(['import', 'mount', 'unmount']);
|
|
2
|
+
|
|
3
|
+
function validateDeclaration(declaration) {
|
|
4
|
+
if (!declaration || typeof declaration !== 'object' || Array.isArray(declaration)) {
|
|
5
|
+
throw new TypeError('[stellar runtime] extension declaration must be an object');
|
|
6
|
+
}
|
|
7
|
+
if (typeof declaration.id !== 'string' || !/^[a-z][a-z0-9-]*$/.test(declaration.id)) {
|
|
8
|
+
throw new TypeError('[stellar runtime] extension id is invalid');
|
|
9
|
+
}
|
|
10
|
+
if (typeof declaration.module !== 'string' || declaration.module.length === 0) {
|
|
11
|
+
throw new TypeError(`[stellar runtime] extension ${declaration.id} module is invalid`);
|
|
12
|
+
}
|
|
13
|
+
const unknown = Object.keys(declaration).filter(key => !['id', 'module', 'when', 'config'].includes(key));
|
|
14
|
+
if (unknown.length > 0) {
|
|
15
|
+
throw new TypeError(`[stellar runtime] extension ${declaration.id} has unknown field ${unknown[0]}`);
|
|
16
|
+
}
|
|
17
|
+
if (!declaration.when || typeof declaration.when !== 'object' || Array.isArray(declaration.when)) {
|
|
18
|
+
throw new TypeError(`[stellar runtime] extension ${declaration.id} when is invalid`);
|
|
19
|
+
}
|
|
20
|
+
const whenKeys = Object.keys(declaration.when);
|
|
21
|
+
if (whenKeys.length !== 1 || !['always', 'selector'].includes(whenKeys[0])) {
|
|
22
|
+
throw new TypeError(`[stellar runtime] extension ${declaration.id} must declare exactly one of always/selector`);
|
|
23
|
+
}
|
|
24
|
+
if (whenKeys[0] === 'always' && declaration.when.always !== true) {
|
|
25
|
+
throw new TypeError(`[stellar runtime] extension ${declaration.id} when.always must be true`);
|
|
26
|
+
}
|
|
27
|
+
if (whenKeys[0] === 'selector' && (typeof declaration.when.selector !== 'string' || declaration.when.selector.length === 0)) {
|
|
28
|
+
throw new TypeError(`[stellar runtime] extension ${declaration.id} when.selector is invalid`);
|
|
29
|
+
}
|
|
30
|
+
if (declaration.config == null || typeof declaration.config !== 'object' || Array.isArray(declaration.config)) {
|
|
31
|
+
throw new TypeError(`[stellar runtime] extension ${declaration.id} config is invalid`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function shouldMount(root, when) {
|
|
36
|
+
if (when?.always === true) return true;
|
|
37
|
+
if (typeof when?.selector !== 'string' || when.selector.length === 0) return false;
|
|
38
|
+
if (typeof root?.matches === 'function' && root.matches(when.selector)) return true;
|
|
39
|
+
return typeof root?.querySelector === 'function' && root.querySelector(when.selector) !== null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function createExtensionRegistry(options = {}) {
|
|
43
|
+
const importer = options.importer || (specifier => import(specifier));
|
|
44
|
+
const onError = typeof options.onError === 'function' ? options.onError : () => {};
|
|
45
|
+
const declarations = new Map();
|
|
46
|
+
const modulePromises = new Map();
|
|
47
|
+
const roots = new WeakMap();
|
|
48
|
+
|
|
49
|
+
function report(id, phase, error) {
|
|
50
|
+
const detail = { id, phase: PHASES.has(phase) ? phase : 'mount', error };
|
|
51
|
+
try {
|
|
52
|
+
onError(detail);
|
|
53
|
+
} catch (ignored) {
|
|
54
|
+
void ignored;
|
|
55
|
+
}
|
|
56
|
+
return { id, status: 'failed', phase: detail.phase, error };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function register(declaration) {
|
|
60
|
+
validateDeclaration(declaration);
|
|
61
|
+
if (declarations.has(declaration.id)) {
|
|
62
|
+
throw new TypeError(`[stellar runtime] duplicate extension id ${declaration.id}`);
|
|
63
|
+
}
|
|
64
|
+
declarations.set(declaration.id, Object.freeze({
|
|
65
|
+
id: declaration.id,
|
|
66
|
+
module: declaration.module,
|
|
67
|
+
when: declaration.when || {},
|
|
68
|
+
config: declaration.config || {}
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function load(declaration) {
|
|
73
|
+
if (!modulePromises.has(declaration.module)) {
|
|
74
|
+
modulePromises.set(declaration.module, Promise.resolve().then(() => importer(declaration.module)));
|
|
75
|
+
}
|
|
76
|
+
return modulePromises.get(declaration.module);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function unmount(root) {
|
|
80
|
+
const instances = roots.get(root) || [];
|
|
81
|
+
const results = [];
|
|
82
|
+
for (let index = instances.length - 1; index >= 0; index--) {
|
|
83
|
+
const instance = instances[index];
|
|
84
|
+
if (typeof instance.cleanup !== 'function') continue;
|
|
85
|
+
try {
|
|
86
|
+
await instance.cleanup();
|
|
87
|
+
results.push({ id: instance.id, status: 'unmounted' });
|
|
88
|
+
} catch (error) {
|
|
89
|
+
results.push(report(instance.id, 'unmount', error));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
roots.delete(root);
|
|
93
|
+
return results;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function mount(root, context = {}) {
|
|
97
|
+
if (!root || typeof root !== 'object') {
|
|
98
|
+
throw new TypeError('[stellar runtime] mount root must be an object');
|
|
99
|
+
}
|
|
100
|
+
await unmount(root);
|
|
101
|
+
const instances = [];
|
|
102
|
+
const results = [];
|
|
103
|
+
roots.set(root, instances);
|
|
104
|
+
|
|
105
|
+
for (const declaration of declarations.values()) {
|
|
106
|
+
let eligible;
|
|
107
|
+
try {
|
|
108
|
+
eligible = shouldMount(root, declaration.when);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
results.push(report(declaration.id, 'mount', error));
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (!eligible) {
|
|
114
|
+
results.push({ id: declaration.id, status: 'skipped' });
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
let module;
|
|
118
|
+
try {
|
|
119
|
+
module = await load(declaration);
|
|
120
|
+
} catch (error) {
|
|
121
|
+
results.push(report(declaration.id, 'import', error));
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (typeof module?.mount !== 'function') {
|
|
125
|
+
results.push(report(declaration.id, 'mount', new TypeError('module must export mount(root, context)')));
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const extensionContext = Object.freeze(Object.assign({}, context, {
|
|
129
|
+
extension: declaration,
|
|
130
|
+
reportError(error, phase = 'mount') {
|
|
131
|
+
return report(declaration.id, phase, error);
|
|
132
|
+
}
|
|
133
|
+
}));
|
|
134
|
+
try {
|
|
135
|
+
const result = await module.mount(root, extensionContext);
|
|
136
|
+
instances.push({
|
|
137
|
+
id: declaration.id,
|
|
138
|
+
cleanup: typeof result === 'function' ? result : null
|
|
139
|
+
});
|
|
140
|
+
results.push({ id: declaration.id, status: 'mounted' });
|
|
141
|
+
} catch (error) {
|
|
142
|
+
results.push(report(declaration.id, 'mount', error));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return results;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return Object.freeze({ register, mount, unmount });
|
|
149
|
+
}
|