mirror of
https://github.com/next-theme/hexo-theme-next.git
synced 2026-09-14 06:50:20 +00:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d25776ea38 | ||
|
|
3e4d43597d | ||
|
|
6037f68b7a | ||
|
|
6468f139cb | ||
|
|
d62fdfa759 | ||
|
|
96d34a0bb9 | ||
|
|
6560fa6dd6 | ||
|
|
66e3095a31 | ||
|
|
120d41d0fa | ||
|
|
4b249983ed | ||
|
|
ba0ab0e8f7 | ||
|
|
41c19bfc47 | ||
|
|
88cdd52f52 | ||
|
|
b3ac16962d | ||
|
|
5b71ed5eae | ||
|
|
5a476ca06f | ||
|
|
abf7ec2aeb | ||
|
|
f7f13675e5 | ||
|
|
db11583d55 | ||
|
|
abd6a790ba | ||
|
|
c0232764b3 | ||
|
|
091af0fd4f | ||
|
|
854e2006b6 | ||
|
|
5c7574fd3f | ||
|
|
4352696dba | ||
|
|
940b2263f6 | ||
|
|
8fa97a173a | ||
|
|
1b306b9824 | ||
|
|
d54ab89ea2 | ||
|
|
333b2c7908 |
2
.github/workflows/labeler.yml
vendored
2
.github/workflows/labeler.yml
vendored
@ -7,7 +7,7 @@ jobs:
|
|||||||
triage:
|
triage:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/labeler@v6
|
- uses: actions/labeler@v7
|
||||||
# https://github.com/peaceiris/actions-label-commenter#work-with-other-auto-label-actions
|
# https://github.com/peaceiris/actions-label-commenter#work-with-other-auto-label-actions
|
||||||
with:
|
with:
|
||||||
repo-token: "${{ secrets.GH_PAT }}"
|
repo-token: "${{ secrets.GH_PAT }}"
|
||||||
|
|||||||
4
.github/workflows/linter.yml
vendored
4
.github/workflows/linter.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@v7
|
||||||
- name: Use Node.js
|
- name: Use Node.js
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v7
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm install
|
run: npm install
|
||||||
- run: npm run eslint
|
- run: npm run eslint
|
||||||
@ -21,7 +21,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-python@v7
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
|
|||||||
2
.github/workflows/npm-publish.yml
vendored
2
.github/workflows/npm-publish.yml
vendored
@ -15,7 +15,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@v7
|
||||||
- name: Use Node.js
|
- name: Use Node.js
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: '24'
|
node-version: '24'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|||||||
2
.github/workflows/tester.yml
vendored
2
.github/workflows/tester.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
repository: hexojs/hexo-starter
|
repository: hexojs/hexo-starter
|
||||||
- name: Use Node.js
|
- name: Use Node.js
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v7
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm install
|
run: npm install
|
||||||
- name: Install hexo-tag-embed
|
- name: Install hexo-tag-embed
|
||||||
|
|||||||
@ -148,9 +148,9 @@ NexT send special thanks to these great services that sponsor our core infrastru
|
|||||||
|
|
||||||
<a href="https://www.netlify.com">
|
<a href="https://www.netlify.com">
|
||||||
<picture>
|
<picture>
|
||||||
<source media="(prefers-color-scheme: dark)" height="40" srcset="https://www.netlify.com/v3/img/components/full-logo-dark.svg">
|
<source media="(prefers-color-scheme: dark)" height="40" srcset="https://raw.githubusercontent.com/netlify/netlify-example-styles/main/public/images/logo-netlify-small-fullcolor-darkmode.svg">
|
||||||
<source media="(prefers-color-scheme: light)" height="40" srcset="https://www.netlify.com/v3/img/components/full-logo-light.svg">
|
<source media="(prefers-color-scheme: light)" height="40" srcset="https://raw.githubusercontent.com/netlify/netlify-example-styles/main/public/images/logo-netlify-small-fullcolor-lightmode.svg">
|
||||||
<img alt="Netlify Logo" height="40" src="https://www.netlify.com/v3/img/components/full-logo-light.svg">
|
<img alt="Netlify Logo" height="40" src="https://raw.githubusercontent.com/netlify/netlify-example-styles/main/public/images/logo-netlify-small-fullcolor-lightmode.svg">
|
||||||
</picture>
|
</picture>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|||||||
19
_config.yml
19
_config.yml
@ -88,7 +88,6 @@ open_graph:
|
|||||||
#twitter_id: <twitter:creator>
|
#twitter_id: <twitter:creator>
|
||||||
#twitter_site: <twitter:site>
|
#twitter_site: <twitter:site>
|
||||||
#twitter_image: <twitter:image>
|
#twitter_image: <twitter:image>
|
||||||
#google_plus: <g+:profile_link>
|
|
||||||
#fb_admins: <fb:admin_id>
|
#fb_admins: <fb:admin_id>
|
||||||
#fb_app_id: <fb:app_id>
|
#fb_app_id: <fb:app_id>
|
||||||
|
|
||||||
@ -105,7 +104,7 @@ menu:
|
|||||||
#home: / || fa fa-home
|
#home: / || fa fa-home
|
||||||
#about: /about/ || fa fa-user
|
#about: /about/ || fa fa-user
|
||||||
#tags: /tags/ || fa fa-tags
|
#tags: /tags/ || fa fa-tags
|
||||||
#categories: /categories/ || fa fa-th
|
#categories: /categories/ || fa fa-layer-group
|
||||||
#archives: /archives/ || fa fa-archive
|
#archives: /archives/ || fa fa-archive
|
||||||
#schedule: /schedule/ || fa fa-calendar
|
#schedule: /schedule/ || fa fa-calendar
|
||||||
#sitemap: /sitemap.xml || fa fa-sitemap
|
#sitemap: /sitemap.xml || fa fa-sitemap
|
||||||
@ -365,8 +364,6 @@ theme_color:
|
|||||||
body_scrollbar:
|
body_scrollbar:
|
||||||
# Place the scrollbar over the content.
|
# Place the scrollbar over the content.
|
||||||
overlay: false
|
overlay: false
|
||||||
# Reserving space for the scrollbar gutter even if the content is not overflowing.
|
|
||||||
stable: false
|
|
||||||
|
|
||||||
codeblock:
|
codeblock:
|
||||||
# Code Highlight theme
|
# Code Highlight theme
|
||||||
@ -575,6 +572,8 @@ math:
|
|||||||
enable: false
|
enable: false
|
||||||
# Available values: none | ams | all
|
# Available values: none | ams | all
|
||||||
tags: none
|
tags: none
|
||||||
|
# Available values: overflow | scroll | scale | truncate | linebreak
|
||||||
|
display_overflow: scroll
|
||||||
|
|
||||||
katex:
|
katex:
|
||||||
enable: false
|
enable: false
|
||||||
@ -600,10 +599,6 @@ fancybox: false
|
|||||||
# For more information: https://medium-zoom.francoischalifour.com
|
# For more information: https://medium-zoom.francoischalifour.com
|
||||||
mediumzoom: false
|
mediumzoom: false
|
||||||
|
|
||||||
# Vanilla JavaScript plugin for lazyloading images.
|
|
||||||
# For more information: https://apoorv.pro/lozad.js/demo/
|
|
||||||
lazyload: false
|
|
||||||
|
|
||||||
# Automatically insert whitespace between CJK and half-width characters.
|
# Automatically insert whitespace between CJK and half-width characters.
|
||||||
# For more information: https://github.com/vinta/pangu.js
|
# For more information: https://github.com/vinta/pangu.js
|
||||||
# Server-side plugin: https://github.com/next-theme/hexo-pangu
|
# Server-side plugin: https://github.com/next-theme/hexo-pangu
|
||||||
@ -814,13 +809,13 @@ plausible:
|
|||||||
script_url: # https://plausible.io/js/script.js
|
script_url: # https://plausible.io/js/script.js
|
||||||
site_domain: # www.example.com
|
site_domain: # www.example.com
|
||||||
|
|
||||||
# Another tool to show number of visitors to each article.
|
# Show the number of visitors to each article using the Firestore REST API.
|
||||||
# Visit https://console.firebase.google.com/u/0/ to get apiKey and projectId.
|
# Visit https://console.firebase.google.com/ to get projectId.
|
||||||
# Visit https://firebase.google.com/docs/firestore/ to get more information about firestore.
|
# Visit https://firebase.google.com/docs/firestore/use-rest-api for more information.
|
||||||
|
# Anonymous requests are authorized by your Firestore Security Rules.
|
||||||
firestore:
|
firestore:
|
||||||
enable: false
|
enable: false
|
||||||
collection: articles # Required, a string collection name to access firestore database
|
collection: articles # Required, a string collection name to access firestore database
|
||||||
apiKey: # Required
|
|
||||||
projectId: # Required
|
projectId: # Required
|
||||||
|
|
||||||
# Show Views / Visitors of the website / page with busuanzi.
|
# Show Views / Visitors of the website / page with busuanzi.
|
||||||
|
|||||||
69
_vendors.yml
69
_vendors.yml
@ -1,17 +1,12 @@
|
|||||||
# This file is automatically generated
|
# This file is automatically generated
|
||||||
# See https://github.com/next-theme/plugins
|
# See https://github.com/next-theme/plugins
|
||||||
|
|
||||||
anime:
|
|
||||||
name: animejs
|
|
||||||
version: 3.2.1
|
|
||||||
file: lib/anime.min.js
|
|
||||||
integrity: sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=
|
|
||||||
fontawesome:
|
fontawesome:
|
||||||
name: '@fortawesome/fontawesome-free'
|
name: '@fortawesome/fontawesome-free'
|
||||||
version: 7.0.0
|
version: 7.3.1
|
||||||
file: css/all.min.css
|
file: css/all.min.css
|
||||||
alias: font-awesome
|
alias: font-awesome
|
||||||
integrity: sha256-VHqXKFhhMxcpubYf9xiWdCiojEbY9NexQ4jh8AxbvcM=
|
integrity: sha256-4Lad8m4ZWW1Lgb9+sMVLYEfnIh7BjV1NQMEe79Pviks=
|
||||||
prism:
|
prism:
|
||||||
name: prismjs
|
name: prismjs
|
||||||
version: 1.30.0
|
version: 1.30.0
|
||||||
@ -32,21 +27,21 @@ prism_line_numbers:
|
|||||||
integrity: sha256-9cmf7tcLdXpKsPi/2AWE93PbZpTp4M4tqzFk+lWomjU=
|
integrity: sha256-9cmf7tcLdXpKsPi/2AWE93PbZpTp4M4tqzFk+lWomjU=
|
||||||
mathjax:
|
mathjax:
|
||||||
name: mathjax
|
name: mathjax
|
||||||
version: 3.2.2
|
version: 4.1.3
|
||||||
file: es5/tex-mml-chtml.js
|
file: tex-mml-chtml.js
|
||||||
integrity: sha256-MASABpB4tYktI2Oitl4t+78w/lyA+D7b/s9GEP0JOGI=
|
integrity: sha256-ihPtDDm7pTmSF9ViEsItlvsrBTsNkz0MOXMchzOwCQw=
|
||||||
katex:
|
katex:
|
||||||
name: katex
|
name: katex
|
||||||
version: 0.16.9
|
version: 0.18.4
|
||||||
file: dist/katex.min.css
|
file: dist/katex.min.css
|
||||||
alias: KaTeX
|
alias: KaTeX
|
||||||
integrity: sha256-UF1fgpAiu3tPJN/uCqEUHNe7pnr+QR0SQDNfgglgtcM=
|
integrity: sha256-GAwtd9Q019pR1mJcUKlk1P1v29ubyHlqCgFsMMSZMfs=
|
||||||
copy_tex_js:
|
copy_tex_js:
|
||||||
name: katex
|
name: katex
|
||||||
version: 0.16.9
|
version: 0.18.4
|
||||||
file: dist/contrib/copy-tex.min.js
|
file: dist/contrib/copy-tex.min.js
|
||||||
alias: KaTeX
|
alias: KaTeX
|
||||||
integrity: sha256-Us54+rSGDSTvIhKKUs4kygE2ipA0RXpWWh0/zLqw3bs=
|
integrity: sha256-xXHEm1IMuOkEAoczgoayF4Pjm0dmYLi1bZ4cA37q6v8=
|
||||||
pjax:
|
pjax:
|
||||||
name: '@next-theme/pjax'
|
name: '@next-theme/pjax'
|
||||||
version: 0.6.0
|
version: 0.6.0
|
||||||
@ -55,16 +50,16 @@ pjax:
|
|||||||
integrity: sha256-vxLn1tSKWD4dqbMRyv940UYw4sXgMtYcK6reefzZrao=
|
integrity: sha256-vxLn1tSKWD4dqbMRyv940UYw4sXgMtYcK6reefzZrao=
|
||||||
fancybox_js:
|
fancybox_js:
|
||||||
name: '@fancyapps/ui'
|
name: '@fancyapps/ui'
|
||||||
version: 5.0.31
|
version: 6.1.14
|
||||||
file: dist/fancybox/fancybox.umd.js
|
file: dist/fancybox/fancybox.umd.js
|
||||||
alias: fancyapps-ui
|
alias: fancyapps-ui
|
||||||
integrity: sha256-a+H7FYzJv6oU2hfsfDGM2Ohw/cR9v+hPfxHCLdmCrE8=
|
integrity: sha256-QnJV4HT5r8i89ovZGRD86iynZqHerEbT2xt69vDM36I=
|
||||||
fancybox_css:
|
fancybox_css:
|
||||||
name: '@fancyapps/ui'
|
name: '@fancyapps/ui'
|
||||||
version: 5.0.31
|
version: 6.1.14
|
||||||
file: dist/fancybox/fancybox.css
|
file: dist/fancybox/fancybox.css
|
||||||
alias: fancyapps-ui
|
alias: fancyapps-ui
|
||||||
integrity: sha256-gkQVf8UKZgQ0HyuxL/VnacadJ+D2Kox2TCEBuNQg5+w=
|
integrity: sha256-mdaMxrItEW6hMIVJJNPKuX0B7KCSJQVEv3mVY4N2we4=
|
||||||
mediumzoom:
|
mediumzoom:
|
||||||
name: medium-zoom
|
name: medium-zoom
|
||||||
version: 1.1.0
|
version: 1.1.0
|
||||||
@ -78,24 +73,24 @@ lazyload:
|
|||||||
integrity: sha256-mOFREFhqmHeQbXpK2lp4nA3qooVgACfh88fpJftLBbc=
|
integrity: sha256-mOFREFhqmHeQbXpK2lp4nA3qooVgACfh88fpJftLBbc=
|
||||||
pangu:
|
pangu:
|
||||||
name: pangu
|
name: pangu
|
||||||
version: 7.2.0
|
version: 7.2.1
|
||||||
file: dist/browser/pangu.umd.js
|
file: dist/browser/pangu.umd.js
|
||||||
integrity: sha256-JnmRRnJK7DC6RQJbAJb6AXOM9OmWzS6z8eYultk/48Y=
|
integrity: sha256-kRsxecBnwG6mULFPe2E6UKnNm8XfDOuDwCOprjLZuX8=
|
||||||
quicklink:
|
quicklink:
|
||||||
name: quicklink
|
name: quicklink
|
||||||
version: 3.0.1
|
version: 3.0.2
|
||||||
file: dist/quicklink.umd.js
|
file: dist/quicklink.umd.js
|
||||||
integrity: sha256-44BednzIpUeQJcY8qtLyarFu0UCCTbgmWOvaoehiFQQ=
|
integrity: sha256-Qo2BTvlsBVXIr5fEHp8xlBpUmkbDINr5cQZeSfMTES8=
|
||||||
disqusjs_js:
|
disqusjs_js:
|
||||||
name: disqusjs
|
name: disqusjs
|
||||||
version: 3.1.0
|
version: 3.2.1
|
||||||
file: dist/browser/disqusjs.es2015.umd.min.js
|
file: dist/browser/disqusjs.es2015.umd.min.js
|
||||||
integrity: sha256-gdbBT62HLzqWTnXQED3WQL/ItFCuWsA5gd6dM/wgdiE=
|
integrity: sha256-6a5jzAWTjd18Nq6rZZn1x9hZLDZAilRDnd0arUFteBM=
|
||||||
disqusjs_css:
|
disqusjs_css:
|
||||||
name: disqusjs
|
name: disqusjs
|
||||||
version: 3.1.0
|
version: 3.2.1
|
||||||
file: dist/browser/styles/disqusjs.css
|
file: dist/browser/styles/disqusjs.css
|
||||||
integrity: sha256-E4RzceSomICFY1PT/yDQrN8DsLSwdScDEiQAevHabp0=
|
integrity: sha256-9SkCf6N7ls5uSFyENCeHM+m7Qv3lHH9Eg/CxdCajarI=
|
||||||
gitalk_js:
|
gitalk_js:
|
||||||
name: gitalk
|
name: gitalk
|
||||||
version: 1.8.0
|
version: 1.8.0
|
||||||
@ -106,21 +101,11 @@ gitalk_css:
|
|||||||
version: 1.8.0
|
version: 1.8.0
|
||||||
file: dist/gitalk.css
|
file: dist/gitalk.css
|
||||||
integrity: sha256-AJnUHL7dBv6PGaeyPQJcgQPDjt/Hn/PvYZde1iqfp8U=
|
integrity: sha256-AJnUHL7dBv6PGaeyPQJcgQPDjt/Hn/PvYZde1iqfp8U=
|
||||||
firebase_app:
|
|
||||||
name: firebase
|
|
||||||
version: 12.2.1
|
|
||||||
file: firebase-app-compat.js
|
|
||||||
integrity: sha256-d21UgtIcA6c6qwr8nWk6lxs/KrpbWhknQN7qBjWA2Kk=
|
|
||||||
firebase_firestore:
|
|
||||||
name: firebase
|
|
||||||
version: 12.2.1
|
|
||||||
file: firebase-firestore-compat.js
|
|
||||||
integrity: sha256-leHatffkFuVGIg7ABaA5BDsqsEUtktL4tftb3OdQfg0=
|
|
||||||
algolia_search:
|
algolia_search:
|
||||||
name: algoliasearch
|
name: algoliasearch
|
||||||
version: 5.36.0
|
version: 5.57.0
|
||||||
file: dist/lite/builds/browser.umd.js
|
file: dist/lite/builds/browser.umd.js
|
||||||
integrity: sha256-o49zoHLDPBm9WLg6AiliAVi3axCRTqe3ltTxLvhzAVg=
|
integrity: sha256-NJrnXIMFVRGMM2IieD83YpTvsPPIFKKyj4K/MNyTRQU=
|
||||||
local_search:
|
local_search:
|
||||||
name: hexo-generator-searchdb
|
name: hexo-generator-searchdb
|
||||||
version: 1.5.0
|
version: 1.5.0
|
||||||
@ -164,11 +149,11 @@ creative_commons:
|
|||||||
alias: creativecommons-vocabulary
|
alias: creativecommons-vocabulary
|
||||||
wavedrom:
|
wavedrom:
|
||||||
name: wavedrom
|
name: wavedrom
|
||||||
version: 3.5.0
|
version: 3.6.2
|
||||||
file: wavedrom.min.js
|
file: wavedrom.min.js
|
||||||
integrity: sha256-INLAoJc6quTNfiMWkGZniYO2cxE8mHpddnLow1m6RFs=
|
integrity: sha256-3hPWxTqi1K1sjmlESiOTH6lfGwYmz8/SFqoAcPxrjwg=
|
||||||
wavedrom_skin:
|
wavedrom_skin:
|
||||||
name: wavedrom
|
name: wavedrom
|
||||||
version: 3.5.0
|
version: 3.6.2
|
||||||
file: skins/default.js
|
file: skins/default.js
|
||||||
integrity: sha256-fduc/Zszk5ezWws2uInY/ALWVmIrmV6VTgXbsYSReFI=
|
integrity: sha256-bUKjUpel+qLgflZd3EfdbhIorwRYgSkaZKM2s/Bi0VI=
|
||||||
|
|||||||
@ -147,9 +147,9 @@ Also, we welcome Issue or PR to our [official-plugins][official-plugins-url].
|
|||||||
|
|
||||||
<a href="https://www.netlify.com">
|
<a href="https://www.netlify.com">
|
||||||
<picture>
|
<picture>
|
||||||
<source media="(prefers-color-scheme: dark)" height="40" srcset="https://www.netlify.com/v3/img/components/full-logo-dark.svg">
|
<source media="(prefers-color-scheme: dark)" height="40" srcset="https://raw.githubusercontent.com/netlify/netlify-example-styles/main/public/images/logo-netlify-small-fullcolor-darkmode.svg">
|
||||||
<source media="(prefers-color-scheme: light)" height="40" srcset="https://www.netlify.com/v3/img/components/full-logo-light.svg">
|
<source media="(prefers-color-scheme: light)" height="40" srcset="https://raw.githubusercontent.com/netlify/netlify-example-styles/main/public/images/logo-netlify-small-fullcolor-lightmode.svg">
|
||||||
<img alt="Netlify Logo" height="40" src="https://www.netlify.com/v3/img/components/full-logo-light.svg">
|
<img alt="Netlify Logo" height="40" src="https://raw.githubusercontent.com/netlify/netlify-example-styles/main/public/images/logo-netlify-small-fullcolor-lightmode.svg">
|
||||||
</picture>
|
</picture>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|||||||
@ -148,9 +148,9 @@ NexT 特别感谢这些支持我们核心基础设施的优质服务:
|
|||||||
|
|
||||||
<a href="https://www.netlify.com">
|
<a href="https://www.netlify.com">
|
||||||
<picture>
|
<picture>
|
||||||
<source media="(prefers-color-scheme: dark)" height="40" srcset="https://www.netlify.com/v3/img/components/full-logo-dark.svg">
|
<source media="(prefers-color-scheme: dark)" height="40" srcset="https://raw.githubusercontent.com/netlify/netlify-example-styles/main/public/images/logo-netlify-small-fullcolor-darkmode.svg">
|
||||||
<source media="(prefers-color-scheme: light)" height="40" srcset="https://www.netlify.com/v3/img/components/full-logo-light.svg">
|
<source media="(prefers-color-scheme: light)" height="40" srcset="https://raw.githubusercontent.com/netlify/netlify-example-styles/main/public/images/logo-netlify-small-fullcolor-lightmode.svg">
|
||||||
<img alt="Netlify Logo" height="40" src="https://www.netlify.com/v3/img/components/full-logo-light.svg">
|
<img alt="Netlify Logo" height="40" src="https://raw.githubusercontent.com/netlify/netlify-example-styles/main/public/images/logo-netlify-small-fullcolor-lightmode.svg">
|
||||||
</picture>
|
</picture>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
{{- meta_generator() }}
|
{{- meta_generator() }}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
{%- if config.preconnect %}
|
{%- if theme.preconnect %}
|
||||||
<link rel="preload" href="{{ url_for(theme.css) }}/main.css" as="style">
|
<link rel="preload" href="{{ url_for(theme.css) }}/main.css" as="style">
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{{ next_pre() }}
|
{{ next_pre() }}
|
||||||
|
|||||||
@ -1,12 +1,8 @@
|
|||||||
<div class="site-brand-container">
|
<div class="site-brand-container">
|
||||||
<div class="site-nav-toggle">
|
<div class="site-nav-search popup-trigger" aria-label="{{ __('menu.search') }}" role="button">
|
||||||
<div class="toggle" aria-label="{{ __('accessibility.nav_toggle') }}" role="button">
|
{%- if theme.algolia_search.enable or theme.local_search.enable %}
|
||||||
{%- if theme.menu %}
|
<i class="fa fa-search fa-fw fa-lg"></i>
|
||||||
<span class="toggle-line"></span>
|
{%- endif %}
|
||||||
<span class="toggle-line"></span>
|
|
||||||
<span class="toggle-line"></span>
|
|
||||||
{%- endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="site-meta">
|
<div class="site-meta">
|
||||||
@ -27,11 +23,9 @@
|
|||||||
{%- endif %}
|
{%- endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="site-nav-right">
|
<div class="site-nav-toggle" aria-label="{{ __('accessibility.nav_toggle') }}" role="button">
|
||||||
<div class="toggle popup-trigger" aria-label="{{ __('menu.search') }}" role="button">
|
{%- if theme.menu %}
|
||||||
{%- if theme.algolia_search.enable or theme.local_search.enable %}
|
<i class="fa fa-ellipsis fa-fw fa-lg"></i>
|
||||||
<i class="fa fa-search fa-fw fa-lg"></i>
|
{%- endif %}
|
||||||
{%- endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -88,7 +88,7 @@
|
|||||||
{%- if config.symbols_count_time.symbols %}
|
{%- if config.symbols_count_time.symbols %}
|
||||||
<span class="post-meta-item" title="{{ __('symbols_count_time.count') }}">
|
<span class="post-meta-item" title="{{ __('symbols_count_time.count') }}">
|
||||||
<span class="post-meta-item-icon">
|
<span class="post-meta-item-icon">
|
||||||
<i class="far fa-file-word"></i>
|
<i class="far fa-file-lines"></i>
|
||||||
</span>
|
</span>
|
||||||
<span class="post-meta-item-text">{{ __('symbols_count_time.count') + __('symbol.colon') }}</span>
|
<span class="post-meta-item-text">{{ __('symbols_count_time.count') + __('symbol.colon') }}</span>
|
||||||
<span>{{ symbolsCount(post) }}</span>
|
<span>{{ symbolsCount(post) }}</span>
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
{%- if theme.algolia_search.enable or theme.local_search.enable %}
|
{%- if theme.algolia_search.enable or theme.local_search.enable %}
|
||||||
<div class="search-pop-overlay">
|
{# Keep the popup out of the render tree to prevent Safari from animating it before main.css is applied.
|
||||||
|
When main.css loads slowly, the unstyled search popup briefly appears and shrinks away. #}
|
||||||
|
<template id="search-popup-template"><div class="search-pop-overlay">
|
||||||
<div class="popup search-popup">
|
<div class="popup search-popup">
|
||||||
<div class="search-header">
|
<div class="search-header">
|
||||||
<span class="search-icon">
|
<span class="search-icon">
|
||||||
@ -20,5 +22,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div></template>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
<span class="toggle-line"></span>
|
<span class="toggle-line"></span>
|
||||||
<span class="toggle-line"></span>
|
<span class="toggle-line"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar-dimmer"></div>
|
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
{%- if theme.back2top.enable and not theme.back2top.sidebar %}
|
{%- if theme.back2top.enable and not theme.back2top.sidebar %}
|
||||||
|
|||||||
3
layout/_third-party/math/mathjax.njk
vendored
3
layout/_third-party/math/mathjax.njk
vendored
@ -1,4 +1,5 @@
|
|||||||
{{ next_data('mathjax', theme.math.mathjax, {
|
{{ next_data('mathjax', theme.math.mathjax, {
|
||||||
js: theme.vendors.mathjax
|
js: theme.vendors.mathjax,
|
||||||
|
font_path: theme.vendors.mathjax_font_path
|
||||||
}) }}
|
}) }}
|
||||||
{{ next_js('third-party/math/mathjax.js') }}
|
{{ next_js('third-party/math/mathjax.js') }}
|
||||||
|
|||||||
7
layout/_third-party/statistics/firestore.njk
vendored
7
layout/_third-party/statistics/firestore.njk
vendored
@ -1,6 +1,7 @@
|
|||||||
{%- if theme.firestore.enable %}
|
{%- if theme.firestore.enable %}
|
||||||
{{ next_vendors('firebase_app') }}
|
{{ next_data('firestore', {
|
||||||
{{ next_vendors('firebase_firestore') }}
|
collection: theme.firestore.collection,
|
||||||
{{ next_data('firestore', theme.firestore) }}
|
projectId: theme.firestore.projectId
|
||||||
|
}) }}
|
||||||
{{ next_js('third-party/statistics/firestore.js') }}
|
{{ next_js('third-party/statistics/firestore.js') }}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|||||||
10
package.json
10
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hexo-theme-next",
|
"name": "hexo-theme-next",
|
||||||
"version": "8.28.0",
|
"version": "8.29.0",
|
||||||
"description": "Elegant and powerful theme for Hexo.",
|
"description": "Elegant and powerful theme for Hexo.",
|
||||||
"main": "package.json",
|
"main": "package.json",
|
||||||
"files": [
|
"files": [
|
||||||
@ -36,13 +36,13 @@
|
|||||||
"homepage": "https://theme-next.js.org",
|
"homepage": "https://theme-next.js.org",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@next-theme/eslint-config": "0.0.5",
|
"@next-theme/eslint-config": "0.0.5",
|
||||||
"c8": "11.0.0",
|
"c8": "12.0.0",
|
||||||
"chai": "6.2.2",
|
"chai": "6.2.2",
|
||||||
"eslint": "10.6.0",
|
"eslint": "10.8.0",
|
||||||
"hexo": "8.1.2",
|
"hexo": "8.1.2",
|
||||||
"hexo-renderer-marked": "7.0.1",
|
"hexo-renderer-marked": "7.0.1",
|
||||||
"js-yaml": "4.2.0",
|
"js-yaml": "4.3.0",
|
||||||
"mocha": "11.7.6",
|
"mocha": "11.8.0",
|
||||||
"stylelint": "16.26.1",
|
"stylelint": "16.26.1",
|
||||||
"stylelint-stylus": "1.0.0"
|
"stylelint-stylus": "1.0.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ module.exports = hexo => {
|
|||||||
hexo.log.warn('Documentation: https://theme-next.js.org/docs/getting-started/configuration.html');
|
hexo.log.warn('Documentation: https://theme-next.js.org/docs/getting-started/configuration.html');
|
||||||
}
|
}
|
||||||
|
|
||||||
const { cache, changyan, growingio_analytics, language_switcher, leancloud_visitors } = hexo.theme.config;
|
const { body_scrollbar, cache, changyan, growingio_analytics, language_switcher, lazyload, leancloud_visitors } = hexo.theme.config;
|
||||||
const warning = function(...args) {
|
const warning = function(...args) {
|
||||||
hexo.log.warn(`Since ${args[0]} is turned on, the ${args[1]} is disabled to avoid potential hazards.`);
|
hexo.log.warn(`Since ${args[0]} is turned on, the ${args[1]} is disabled to avoid potential hazards.`);
|
||||||
};
|
};
|
||||||
@ -29,6 +29,14 @@ module.exports = hexo => {
|
|||||||
if (growingio_analytics) {
|
if (growingio_analytics) {
|
||||||
hexo.log.warn('growingio_analytics is deprecated. Please migrate to another analytics provider.');
|
hexo.log.warn('growingio_analytics is deprecated. Please migrate to another analytics provider.');
|
||||||
}
|
}
|
||||||
|
if (body_scrollbar?.stable) {
|
||||||
|
hexo.log.warn('The `body_scrollbar.stable` theme option is deprecated and will be removed in the next version.');
|
||||||
|
}
|
||||||
|
if (lazyload) {
|
||||||
|
hexo.log.warn('The `lazyload` theme option is deprecated and will be removed in the next version.');
|
||||||
|
hexo.log.warn('Use `marked.lazyload: true` in Hexo `_config.yml` instead. hexo-renderer-marked will add `loading="lazy"` to images.');
|
||||||
|
hexo.log.warn('Documentation: https://github.com/hexojs/hexo-renderer-marked#options');
|
||||||
|
}
|
||||||
if (leancloud_visitors?.enable) {
|
if (leancloud_visitors?.enable) {
|
||||||
hexo.log.warn('LeanCloud will stop public services on 2027-01-12. Please migrate your data as soon as possible.');
|
hexo.log.warn('LeanCloud will stop public services on 2027-01-12. Please migrate your data as soon as possible.');
|
||||||
hexo.log.warn('Announcement: https://docs.leancloud.app/sdk/announcements/sunset-announcement/');
|
hexo.log.warn('Announcement: https://docs.leancloud.app/sdk/announcements/sunset-announcement/');
|
||||||
|
|||||||
@ -57,5 +57,10 @@ module.exports = hexo => {
|
|||||||
// Omit integrity for local assets.
|
// Omit integrity for local assets.
|
||||||
integrity: plugins === 'local' ? undefined : value.integrity
|
integrity: plugins === 'local' ? undefined : value.integrity
|
||||||
};
|
};
|
||||||
|
if (key === 'mathjax') {
|
||||||
|
vendors.mathjax_font_path = plugins === 'local'
|
||||||
|
? url_for.call(hexo, 'lib/@mathjax/mathjax-newcm-font')
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -19,7 +19,9 @@ hexo.extend.helper.register('next_inject', function(point) {
|
|||||||
|
|
||||||
hexo.extend.helper.register('next_js', function(file, {
|
hexo.extend.helper.register('next_js', function(file, {
|
||||||
pjax = false,
|
pjax = false,
|
||||||
module = false
|
module = false,
|
||||||
|
async = false,
|
||||||
|
defer = !async
|
||||||
} = {}) {
|
} = {}) {
|
||||||
const { next_version } = this;
|
const { next_version } = this;
|
||||||
const { internal, custom_cdn_url } = this.theme.vendors;
|
const { internal, custom_cdn_url } = this.theme.vendors;
|
||||||
@ -32,18 +34,21 @@ hexo.extend.helper.register('next_js', function(file, {
|
|||||||
custom : custom_cdn_url
|
custom : custom_cdn_url
|
||||||
});
|
});
|
||||||
const src = links[internal] || links.local;
|
const src = links[internal] || links.local;
|
||||||
return `<script ${pjax ? 'data-pjax ' : ''}${module ? 'type="module" ' : ''}src="${src}" defer></script>`;
|
return `<script ${pjax ? 'data-pjax ' : ''}${module ? 'type="module" ' : ''}src="${src}"${defer ? ' defer' : ''}${async ? ' async' : ''}></script>`;
|
||||||
});
|
});
|
||||||
|
|
||||||
hexo.extend.helper.register('next_vendors', function(name) {
|
hexo.extend.helper.register('next_vendors', function(name, {
|
||||||
|
async = false,
|
||||||
|
defer = !async
|
||||||
|
} = {}) {
|
||||||
const { url, integrity } = this.theme.vendors[name];
|
const { url, integrity } = this.theme.vendors[name];
|
||||||
const type = url.endsWith('css') ? 'css' : 'js';
|
const type = url.endsWith('css') ? 'css' : 'js';
|
||||||
if (type === 'css') {
|
if (type === 'css') {
|
||||||
if (integrity) return `<link rel="stylesheet" href="${url}" integrity="${integrity}" crossorigin="anonymous">`;
|
if (integrity) return `<link rel="stylesheet" href="${url}" integrity="${integrity}" crossorigin="anonymous">`;
|
||||||
return `<link rel="stylesheet" href="${url}">`;
|
return `<link rel="stylesheet" href="${url}">`;
|
||||||
}
|
}
|
||||||
if (integrity) return `<script src="${url}" integrity="${integrity}" crossorigin="anonymous" defer></script>`;
|
if (integrity) return `<script src="${url}" integrity="${integrity}" crossorigin="anonymous"${defer ? ' defer' : ''}${async ? ' async' : ''}></script>`;
|
||||||
return `<script src="${url}" defer></script>`;
|
return `<script src="${url}"${defer ? ' defer' : ''}${async ? ' async' : ''}></script>`;
|
||||||
});
|
});
|
||||||
|
|
||||||
hexo.extend.helper.register('next_data', function(name, ...data) {
|
hexo.extend.helper.register('next_data', function(name, ...data) {
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
hexo.extend.helper.register('js_vendors', function() {
|
hexo.extend.helper.register('js_vendors', function() {
|
||||||
const { config, theme } = this;
|
const { config, theme } = this;
|
||||||
const vendors = ['anime'];
|
const vendors = [];
|
||||||
if (theme.prism.enable && !config.prismjs.preprocess) {
|
if (theme.prism.enable && !config.prismjs.preprocess) {
|
||||||
vendors.push('prism', 'prism_autoloader');
|
vendors.push('prism', 'prism_autoloader');
|
||||||
if (config.prismjs.line_number) {
|
if (config.prismjs.line_number) {
|
||||||
|
|||||||
@ -28,11 +28,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hexo-config('reward_settings.animation')) {
|
if (hexo-config('reward_settings.animation')) {
|
||||||
|
span {
|
||||||
|
transform-origin: center bottom;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover span {
|
&:hover span {
|
||||||
animation: next-roll .1s infinite linear;
|
animation: next-bounce 1.4s infinite ease-in-out;
|
||||||
// The animation may affect :hover of img in dark mode
|
// The animation may affect :hover of img in dark mode
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
&:hover span {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,12 +54,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes next-roll {
|
@keyframes next-bounce {
|
||||||
from {
|
0%, 100% {
|
||||||
transform: rotateZ(30deg);
|
transform: translateY(0) scale(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
12% {
|
||||||
transform: rotateZ(-30deg);
|
transform: translateY(0) scale(1.04, .96);
|
||||||
|
}
|
||||||
|
|
||||||
|
36% {
|
||||||
|
transform: translateY(-7px) scale(.98, 1.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
58% {
|
||||||
|
transform: translateY(0) scale(1.03, .97);
|
||||||
|
}
|
||||||
|
|
||||||
|
72% {
|
||||||
|
transform: translateY(-2px) scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
84% {
|
||||||
|
transform: translateY(0) scale(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
if (hexo-config('math.mathjax.enable')) {
|
if (hexo-config('math.mathjax.enable')) {
|
||||||
mjx-container[jax='CHTML'][display='true'], .has-jax {
|
.has-jax {
|
||||||
overflow: auto hidden;
|
overflow: auto hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,11 +12,10 @@
|
|||||||
.site-brand-container {
|
.site-brand-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
padding: 0 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.use-motion {
|
.use-motion {
|
||||||
.column, .site-brand-container .toggle {
|
.column {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,20 +1,19 @@
|
|||||||
.site-nav-toggle, .site-nav-right {
|
.site-nav-toggle, .site-nav-search {
|
||||||
|
color: var(--text-color);
|
||||||
display: none;
|
display: none;
|
||||||
|
padding: 0 10px;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
transition: transform .5s;
|
||||||
|
}
|
||||||
|
|
||||||
+mobile() {
|
+mobile() {
|
||||||
flex-column();
|
flex-column();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.toggle {
|
.site-nav-on .site-nav-toggle .fa {
|
||||||
color: var(--text-color);
|
transform: rotate(-90deg);
|
||||||
padding: 10px;
|
|
||||||
width: 22px;
|
|
||||||
|
|
||||||
.toggle-line {
|
|
||||||
background: var(--text-color);
|
|
||||||
border-radius: 1px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.site-nav {
|
.site-nav {
|
||||||
|
|||||||
@ -20,16 +20,10 @@
|
|||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.site-nav-toggle, .site-nav-right {
|
.site-nav-toggle, .site-nav-search {
|
||||||
|
color: white;
|
||||||
|
|
||||||
+tablet() {
|
+tablet() {
|
||||||
flex-column();
|
flex-column();
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle {
|
|
||||||
color: white;
|
|
||||||
|
|
||||||
.toggle-line {
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,6 @@ body { margin-top: 2rem; }
|
|||||||
}
|
}
|
||||||
|
|
||||||
.use-motion .column,
|
.use-motion .column,
|
||||||
.use-motion .site-brand-container .toggle,
|
|
||||||
.use-motion .footer { opacity: initial; }
|
.use-motion .footer { opacity: initial; }
|
||||||
|
|
||||||
.use-motion .site-title,
|
.use-motion .site-title,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
/* global CONFIG */
|
/* global NexT, CONFIG */
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
'use strict';
|
'use strict';
|
||||||
@ -13,12 +13,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
// If the page opens with a specific hash, just jump out
|
// If the page opens with a specific hash, just jump out
|
||||||
if (!isNaN(top) && location.hash === '') {
|
if (!isNaN(top) && location.hash === '') {
|
||||||
// Auto scroll to the position
|
// Auto scroll to the position
|
||||||
window.anime({
|
NexT.utils.scrollTo(window, top);
|
||||||
targets : document.scrollingElement,
|
|
||||||
duration : 200,
|
|
||||||
easing : 'linear',
|
|
||||||
scrollTop: top
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Register everything
|
// Register everything
|
||||||
@ -36,17 +31,25 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
// Save the position by clicking the icon
|
// Save the position by clicking the icon
|
||||||
link.addEventListener('click', () => {
|
link.addEventListener('click', () => {
|
||||||
doSaveScroll();
|
doSaveScroll();
|
||||||
window.anime({
|
if (typeof link.animate !== 'function') {
|
||||||
targets : link,
|
link.style.top = '-30px';
|
||||||
|
setTimeout(() => {
|
||||||
|
link.style.top = '';
|
||||||
|
}, 600);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const animation = link.animate([{}, { top: '-30px' }], {
|
||||||
duration: 200,
|
duration: 200,
|
||||||
easing : 'linear',
|
easing : 'linear',
|
||||||
top : -30,
|
fill : 'forwards'
|
||||||
complete: () => {
|
|
||||||
setTimeout(() => {
|
|
||||||
link.style.top = '';
|
|
||||||
}, 400);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
animation.finished.then(() => {
|
||||||
|
link.style.top = '-30px';
|
||||||
|
animation.cancel();
|
||||||
|
setTimeout(() => {
|
||||||
|
link.style.top = '';
|
||||||
|
}, 400);
|
||||||
|
}).catch(() => {});
|
||||||
});
|
});
|
||||||
scrollToMark();
|
scrollToMark();
|
||||||
document.addEventListener('pjax:success', scrollToMark);
|
document.addEventListener('pjax:success', scrollToMark);
|
||||||
|
|||||||
@ -14,16 +14,38 @@ NexT.motion.integrator = {
|
|||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
bootstrap() {
|
bootstrap() {
|
||||||
|
if (typeof Element.prototype.animate !== 'function') {
|
||||||
|
document.body.classList.remove('use-motion');
|
||||||
|
CONFIG.motion.enable = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!CONFIG.motion.async) this.queue = [this.queue.flat()];
|
if (!CONFIG.motion.async) this.queue = [this.queue.flat()];
|
||||||
this.queue.forEach(sequence => {
|
this.queue.forEach(sequence => this.schedule(sequence));
|
||||||
const timeline = window.anime.timeline({
|
},
|
||||||
duration: CONFIG.motion?.duration ?? 200,
|
schedule(sequence) {
|
||||||
easing : 'linear'
|
let cursor = 0;
|
||||||
});
|
sequence.forEach(item => {
|
||||||
sequence.forEach(item => {
|
const duration = item.duration ?? CONFIG.motion?.duration ?? 200;
|
||||||
if (item.deltaT) timeline.add(item, item.deltaT);
|
const start = Math.max(0, cursor - (item.overlap ?? 0));
|
||||||
else timeline.add(item);
|
const end = start + duration;
|
||||||
});
|
cursor = Math.max(cursor, end);
|
||||||
|
|
||||||
|
if (item.styles) {
|
||||||
|
const targets = typeof item.targets === 'string' ? document.querySelectorAll(item.targets) : [item.targets].filter(Boolean);
|
||||||
|
targets.forEach(target => {
|
||||||
|
const animation = target.animate([{}, item.styles], {
|
||||||
|
delay : start,
|
||||||
|
duration,
|
||||||
|
easing: 'linear',
|
||||||
|
fill : 'forwards'
|
||||||
|
});
|
||||||
|
animation.finished.then(() => {
|
||||||
|
Object.assign(target.style, item.styles);
|
||||||
|
animation.cancel();
|
||||||
|
}).catch(() => {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (item.complete) setTimeout(item.complete, end);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -35,18 +57,17 @@ NexT.motion.middleWares = {
|
|||||||
function getMistLineSettings(targets) {
|
function getMistLineSettings(targets) {
|
||||||
sequence.push({
|
sequence.push({
|
||||||
targets,
|
targets,
|
||||||
scaleX : [0, 1],
|
styles : { transform: 'scaleX(1)' },
|
||||||
duration: 500,
|
duration: 500,
|
||||||
deltaT : '-=200'
|
overlap : 200
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function pushToSequence(targets, sequenceQueue = false) {
|
function pushToSequence(targets, sequenceQueue = false) {
|
||||||
sequence.push({
|
sequence.push({
|
||||||
targets,
|
targets,
|
||||||
opacity: 1,
|
styles : { opacity: 1, top: '0px' },
|
||||||
top : 0,
|
overlap: sequenceQueue ? 200 : 0
|
||||||
deltaT : sequenceQueue ? '-=200' : '-=0'
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +85,7 @@ NexT.motion.middleWares = {
|
|||||||
sequence.push({
|
sequence.push({
|
||||||
targets,
|
targets,
|
||||||
complete: () => targets.classList.add('animated', menuItemTransition),
|
complete: () => targets.classList.add('animated', menuItemTransition),
|
||||||
deltaT : '-=200'
|
overlap : 200
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -92,7 +113,7 @@ NexT.motion.middleWares = {
|
|||||||
sequence.push({
|
sequence.push({
|
||||||
targets,
|
targets,
|
||||||
complete: () => targets.classList.add('animated', animation),
|
complete: () => targets.classList.add('animated', animation),
|
||||||
deltaT : '-=100'
|
overlap : 100
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -101,7 +122,7 @@ NexT.motion.middleWares = {
|
|||||||
sequence.push({
|
sequence.push({
|
||||||
targets,
|
targets,
|
||||||
complete: () => targets.classList.add('animated', post_block),
|
complete: () => targets.classList.add('animated', post_block),
|
||||||
deltaT : '-=100'
|
overlap : 100
|
||||||
});
|
});
|
||||||
animate(coll_header, targets.querySelectorAll('.collection-header'));
|
animate(coll_header, targets.querySelectorAll('.collection-header'));
|
||||||
animate(post_header, targets.querySelectorAll('.post-header'));
|
animate(post_header, targets.querySelectorAll('.post-header'));
|
||||||
@ -123,7 +144,7 @@ NexT.motion.middleWares = {
|
|||||||
sequence.push({
|
sequence.push({
|
||||||
targets,
|
targets,
|
||||||
complete: () => targets.classList.add('animated', sidebarTransition),
|
complete: () => targets.classList.add('animated', sidebarTransition),
|
||||||
deltaT : '-=100'
|
overlap : 100
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -133,7 +154,7 @@ NexT.motion.middleWares = {
|
|||||||
footer() {
|
footer() {
|
||||||
return [{
|
return [{
|
||||||
targets: document.querySelector('.footer'),
|
targets: document.querySelector('.footer'),
|
||||||
opacity: 1
|
styles : { opacity: 1 }
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -9,8 +9,7 @@ NexT.boot.registerEvents = function() {
|
|||||||
NexT.utils.updateFooterPosition();
|
NexT.utils.updateFooterPosition();
|
||||||
|
|
||||||
// Mobile top menu bar.
|
// Mobile top menu bar.
|
||||||
document.querySelector('.site-nav-toggle .toggle').addEventListener('click', event => {
|
document.querySelector('.site-nav-toggle').addEventListener('click', event => {
|
||||||
event.currentTarget.classList.toggle('toggle-close');
|
|
||||||
const siteNav = document.querySelector('.site-nav');
|
const siteNav = document.querySelector('.site-nav');
|
||||||
if (!siteNav) return;
|
if (!siteNav) return;
|
||||||
siteNav.style.setProperty('--scroll-height', siteNav.scrollHeight + 'px');
|
siteNav.style.setProperty('--scroll-height', siteNav.scrollHeight + 'px');
|
||||||
|
|||||||
@ -26,7 +26,8 @@ const pjax = new Pjax({
|
|||||||
},
|
},
|
||||||
analytics: false,
|
analytics: false,
|
||||||
cacheBust: false,
|
cacheBust: false,
|
||||||
scrollTo : !CONFIG.bookmark.enable
|
// scrollTo accepts a number (vertical offset), a number array ([x, y]), or false (do not scroll).
|
||||||
|
scrollTo : CONFIG.bookmark.enable ? false : 0
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('pjax:success', () => {
|
document.addEventListener('pjax:success', () => {
|
||||||
|
|||||||
@ -3,13 +3,17 @@
|
|||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
|
||||||
const isRight = CONFIG.sidebar.position === 'right';
|
const isRight = CONFIG.sidebar.position === 'right';
|
||||||
|
// Create the dimmer dynamically to prevent an unwanted animation before main.css is applied.
|
||||||
|
const sidebarDimmer = document.createElement('div');
|
||||||
|
sidebarDimmer.className = 'sidebar-dimmer';
|
||||||
|
document.body.appendChild(sidebarDimmer);
|
||||||
|
|
||||||
const sidebarToggleMotion = {
|
const sidebarToggleMotion = {
|
||||||
mouse: {},
|
mouse: {},
|
||||||
init() {
|
init() {
|
||||||
window.addEventListener('mousedown', this.mousedownHandler.bind(this));
|
window.addEventListener('mousedown', this.mousedownHandler.bind(this));
|
||||||
window.addEventListener('mouseup', this.mouseupHandler.bind(this));
|
window.addEventListener('mouseup', this.mouseupHandler.bind(this));
|
||||||
document.querySelector('.sidebar-dimmer').addEventListener('click', this.clickHandler.bind(this));
|
sidebarDimmer.addEventListener('click', this.clickHandler.bind(this));
|
||||||
document.querySelector('.sidebar-toggle').addEventListener('click', this.clickHandler.bind(this));
|
document.querySelector('.sidebar-toggle').addEventListener('click', this.clickHandler.bind(this));
|
||||||
window.addEventListener('sidebar:show', this.showSidebar);
|
window.addEventListener('sidebar:show', this.showSidebar);
|
||||||
window.addEventListener('sidebar:hide', this.hideSidebar);
|
window.addEventListener('sidebar:hide', this.hideSidebar);
|
||||||
|
|||||||
5
source/js/third-party/fancybox.js
vendored
5
source/js/third-party/fancybox.js
vendored
@ -31,5 +31,8 @@ document.addEventListener('page:loaded', () => {
|
|||||||
image.wrap(imageWrapLink);
|
image.wrap(imageWrapLink);
|
||||||
});
|
});
|
||||||
|
|
||||||
Fancybox.bind('[data-fancybox]');
|
// Disable hash handling to avoid conflicts with PJAX navigation.
|
||||||
|
Fancybox.bind('[data-fancybox]', {
|
||||||
|
Hash: false
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
11
source/js/third-party/math/mathjax.js
vendored
11
source/js/third-party/math/mathjax.js
vendored
@ -4,6 +4,12 @@ document.addEventListener('page:loaded', () => {
|
|||||||
if (!CONFIG.enableMath) return;
|
if (!CONFIG.enableMath) return;
|
||||||
|
|
||||||
if (typeof MathJax === 'undefined') {
|
if (typeof MathJax === 'undefined') {
|
||||||
|
const output = {
|
||||||
|
displayOverflow: CONFIG.mathjax.display_overflow
|
||||||
|
};
|
||||||
|
if (CONFIG.mathjax.font_path) {
|
||||||
|
output.fontPath = CONFIG.mathjax.font_path;
|
||||||
|
}
|
||||||
window.MathJax = {
|
window.MathJax = {
|
||||||
tex: {
|
tex: {
|
||||||
inlineMath: { '[+]': [['$', '$']] },
|
inlineMath: { '[+]': [['$', '$']] },
|
||||||
@ -12,7 +18,7 @@ document.addEventListener('page:loaded', () => {
|
|||||||
options: {
|
options: {
|
||||||
renderActions: {
|
renderActions: {
|
||||||
insertedScript: [200, () => {
|
insertedScript: [200, () => {
|
||||||
document.querySelectorAll('mjx-container').forEach(node => {
|
document.querySelectorAll('mjx-container:not([display])').forEach(node => {
|
||||||
const target = node.parentNode;
|
const target = node.parentNode;
|
||||||
if (target.nodeName.toLowerCase() === 'li') {
|
if (target.nodeName.toLowerCase() === 'li') {
|
||||||
target.parentNode.classList.add('has-jax');
|
target.parentNode.classList.add('has-jax');
|
||||||
@ -20,7 +26,8 @@ document.addEventListener('page:loaded', () => {
|
|||||||
});
|
});
|
||||||
}, '', false]
|
}, '', false]
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
output
|
||||||
};
|
};
|
||||||
NexT.utils.getScript(CONFIG.mathjax.js, {
|
NexT.utils.getScript(CONFIG.mathjax.js, {
|
||||||
attributes: {
|
attributes: {
|
||||||
|
|||||||
128
source/js/third-party/search/algolia-search.js
vendored
128
source/js/third-party/search/algolia-search.js
vendored
@ -1,6 +1,7 @@
|
|||||||
/* global CONFIG, NexT, pjax */
|
/* global CONFIG, NexT, pjax */
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
document.body.appendChild(document.querySelector('#search-popup-template').content.cloneNode(true));
|
||||||
const { indexName, appID, apiKey, hits } = CONFIG.algolia;
|
const { indexName, appID, apiKey, hits } = CONFIG.algolia;
|
||||||
const client = window['algoliasearch/lite'].liteClient(appID, apiKey);
|
const client = window['algoliasearch/lite'].liteClient(appID, apiKey);
|
||||||
|
|
||||||
@ -19,83 +20,92 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
let isSearching = false;
|
let latestSearchId = 0;
|
||||||
let pendingQuery = null;
|
|
||||||
|
|
||||||
const searchAlgolia = async (searchText, page = 0) => {
|
const searchAlgolia = async (searchText, page, searchId) => {
|
||||||
if (isSearching) {
|
if (searchId !== latestSearchId) return;
|
||||||
pendingQuery = { searchText, page };
|
container.setAttribute('aria-busy', 'true');
|
||||||
return;
|
|
||||||
}
|
|
||||||
isSearching = true;
|
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
const result = await client.search({
|
try {
|
||||||
requests: [{
|
const result = await client.search({
|
||||||
indexName,
|
requests: [{
|
||||||
page,
|
indexName,
|
||||||
query : searchText,
|
page,
|
||||||
hitsPerPage : hits.per_page || 10,
|
query : searchText,
|
||||||
attributesToRetrieve : ['permalink'],
|
hitsPerPage : hits.per_page || 10,
|
||||||
attributesToHighlight: ['title', 'excerpt', 'excerptStrip', 'contentStripTruncate'],
|
attributesToRetrieve : ['permalink'],
|
||||||
highlightPreTag : '<mark class="search-keyword">',
|
attributesToHighlight: ['title', 'excerpt', 'excerptStrip', 'contentStripTruncate'],
|
||||||
highlightPostTag : '</mark>'
|
highlightPreTag : '<mark class="search-keyword">',
|
||||||
}]
|
highlightPostTag : '</mark>'
|
||||||
});
|
}]
|
||||||
const data = result.results[0];
|
});
|
||||||
if (data.nbHits === 0) {
|
if (searchId !== latestSearchId) return;
|
||||||
container.innerHTML = '<div class="search-result-icon"><i class="far fa-frown fa-5x"></i></div>';
|
|
||||||
} else {
|
|
||||||
const stats = CONFIG.i18n.hits_time
|
|
||||||
.replace('${hits}', data.nbHits)
|
|
||||||
.replace('${time}', Date.now() - startTime);
|
|
||||||
let pagination = '';
|
|
||||||
if (data.nbPages > 1) {
|
|
||||||
pagination += '<nav class="pagination algolia-pagination">';
|
|
||||||
for (let i = 0; i < data.nbPages; i++) {
|
|
||||||
if (i === page) {
|
|
||||||
pagination += `<span class="page-number current">${i + 1}</span>`;
|
|
||||||
} else {
|
|
||||||
pagination += `<a class="page-number" href="#" data-index=${i}>${i + 1}</a>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pagination += '</nav>';
|
|
||||||
}
|
|
||||||
|
|
||||||
container.innerHTML = `<div class="search-stats">
|
const data = result.results[0];
|
||||||
|
if (data.nbHits === 0) {
|
||||||
|
container.innerHTML = '<div class="search-result-icon"><i class="far fa-frown fa-5x"></i></div>';
|
||||||
|
} else {
|
||||||
|
const stats = CONFIG.i18n.hits_time
|
||||||
|
.replace('${hits}', data.nbHits)
|
||||||
|
.replace('${time}', Date.now() - startTime);
|
||||||
|
let pagination = '';
|
||||||
|
if (data.nbPages > 1) {
|
||||||
|
pagination += '<nav class="pagination algolia-pagination">';
|
||||||
|
for (let i = 0; i < data.nbPages; i++) {
|
||||||
|
if (i === page) {
|
||||||
|
pagination += `<span class="page-number current">${i + 1}</span>`;
|
||||||
|
} else {
|
||||||
|
pagination += `<a class="page-number" href="#" data-index=${i}>${i + 1}</a>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pagination += '</nav>';
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = `<div class="search-stats">
|
||||||
<span>${stats}</span>
|
<span>${stats}</span>
|
||||||
<img src="${CONFIG.images}/logo-algolia-nebula-blue-full.svg" alt="Algolia">
|
<img src="${CONFIG.images}/logo-algolia-nebula-blue-full.svg" alt="Algolia">
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<ul class="search-result-list">${data.hits.map(formatHits).join('')}</ul>
|
<ul class="search-result-list">${data.hits.map(formatHits).join('')}</ul>
|
||||||
${pagination}`;
|
${pagination}`;
|
||||||
if (typeof pjax === 'object') pjax.refresh(container);
|
if (typeof pjax === 'object') pjax.refresh(container);
|
||||||
container.querySelectorAll('.page-number').forEach(element => {
|
container.querySelectorAll('.page-number').forEach(element => {
|
||||||
element.addEventListener('click', async event => {
|
element.addEventListener('click', async event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
await searchAlgolia(searchText, Number(element.dataset.index));
|
if (input.value.trim() !== searchText) return;
|
||||||
|
const searchId = ++latestSearchId;
|
||||||
|
await searchAlgolia(searchText, Number(element.dataset.index), searchId);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}
|
} catch (error) {
|
||||||
isSearching = false;
|
if (searchId !== latestSearchId) return;
|
||||||
if (pendingQuery !== null && (pendingQuery.searchText !== searchText || pendingQuery.page !== page)) {
|
console.warn('Algolia search failed:', error);
|
||||||
const { searchText, page } = pendingQuery;
|
container.innerHTML = '<div class="search-result-icon"><i class="far fa-frown fa-5x"></i></div>';
|
||||||
pendingQuery = null;
|
} finally {
|
||||||
searchAlgolia(searchText, page);
|
if (searchId === latestSearchId) container.removeAttribute('aria-busy');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const inputEventFunction = async () => {
|
const inputEventFunction = async searchId => {
|
||||||
|
if (searchId !== latestSearchId) return;
|
||||||
const searchText = input.value.trim();
|
const searchText = input.value.trim();
|
||||||
if (searchText === '') {
|
if (searchText === '') return;
|
||||||
container.innerHTML = '<div class="search-result-icon"><i class="fab fa-algolia fa-5x"></i></div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Algolia client will automatically cache the data for same queries
|
// Algolia client will automatically cache the data for same queries
|
||||||
await searchAlgolia(searchText, 0);
|
await searchAlgolia(searchText, 0, searchId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const debouncedSearch = NexT.utils.debounce(inputEventFunction, 500);
|
const debouncedSearch = NexT.utils.debounce(inputEventFunction, 500);
|
||||||
input.addEventListener('input', debouncedSearch);
|
input.addEventListener('input', () => {
|
||||||
|
const searchId = ++latestSearchId;
|
||||||
|
if (input.value.trim() === '') {
|
||||||
|
container.removeAttribute('aria-busy');
|
||||||
|
container.innerHTML = '<div class="search-result-icon"><i class="fab fa-algolia fa-5x"></i></div>';
|
||||||
|
} else {
|
||||||
|
container.setAttribute('aria-busy', 'true');
|
||||||
|
}
|
||||||
|
debouncedSearch(searchId);
|
||||||
|
});
|
||||||
|
|
||||||
// Handle and trigger popup window
|
// Handle and trigger popup window
|
||||||
document.querySelectorAll('.popup-trigger').forEach(element => {
|
document.querySelectorAll('.popup-trigger').forEach(element => {
|
||||||
|
|||||||
1
source/js/third-party/search/local-search.js
vendored
1
source/js/third-party/search/local-search.js
vendored
@ -6,6 +6,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
console.warn('`hexo-generator-searchdb` plugin is not installed!');
|
console.warn('`hexo-generator-searchdb` plugin is not installed!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
document.body.appendChild(document.querySelector('#search-popup-template').content.cloneNode(true));
|
||||||
const localSearch = new LocalSearch({
|
const localSearch = new LocalSearch({
|
||||||
path : CONFIG.path,
|
path : CONFIG.path,
|
||||||
top_n_per_article: CONFIG.localsearch.top_n_per_article,
|
top_n_per_article: CONFIG.localsearch.top_n_per_article,
|
||||||
|
|||||||
132
source/js/third-party/statistics/firestore.js
vendored
132
source/js/third-party/statistics/firestore.js
vendored
@ -1,57 +1,95 @@
|
|||||||
/* global CONFIG, firebase */
|
/* global CONFIG */
|
||||||
|
|
||||||
firebase.initializeApp({
|
|
||||||
apiKey : CONFIG.firestore.apiKey,
|
|
||||||
projectId: CONFIG.firestore.projectId
|
|
||||||
});
|
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
const getCount = async (doc, increaseCount) => {
|
const database = `projects/${CONFIG.firestore.projectId}/databases/(default)`;
|
||||||
// IncreaseCount will be false when not in article page
|
const documents = `${database}/documents`;
|
||||||
const d = await doc.get();
|
const api = `https://firestore.googleapis.com/v1/${documents}`;
|
||||||
// Has no data, initialize count
|
|
||||||
let count = d.exists ? d.data().count : 0;
|
const fetchFirestore = async (path, options) => {
|
||||||
// If first view this article
|
const response = await fetch(`${api}${path}`, options);
|
||||||
if (increaseCount) {
|
if (response.status === 404) return null;
|
||||||
// Increase count
|
if (!response.ok) throw new Error(`Firestore request failed with status ${response.status}`);
|
||||||
count++;
|
return response.json();
|
||||||
doc.set({
|
|
||||||
count
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const db = firebase.firestore();
|
const documentName = title => `${documents}/${CONFIG.firestore.collection}/${title}`;
|
||||||
const articles = db.collection(CONFIG.firestore.collection);
|
const getValue = value => Number(value?.integerValue ?? value?.doubleValue ?? 0);
|
||||||
|
|
||||||
|
const getCount = async (title, increaseCount) => {
|
||||||
|
if (increaseCount) {
|
||||||
|
const data = await fetchFirestore(':commit', {
|
||||||
|
method : 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
writes: [{
|
||||||
|
transform: {
|
||||||
|
document : documentName(title),
|
||||||
|
fieldTransforms: [{
|
||||||
|
fieldPath: 'count',
|
||||||
|
increment: {
|
||||||
|
integerValue: '1'
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
});
|
||||||
|
return getValue(data.writeResults[0].transformResults[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const collection = encodeURIComponent(CONFIG.firestore.collection);
|
||||||
|
const document = await fetchFirestore(`/${collection}/${encodeURIComponent(title)}`);
|
||||||
|
return getValue(document?.fields?.count);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCounts = async titles => {
|
||||||
|
if (titles.length === 0) return [];
|
||||||
|
const data = await fetchFirestore(':batchGet', {
|
||||||
|
method : 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
documents: titles.map(documentName),
|
||||||
|
mask : {
|
||||||
|
fieldPaths: ['count']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const counts = new Map(data.map(result => {
|
||||||
|
const document = result.found;
|
||||||
|
return [document?.name ?? result.missing, getValue(document?.fields?.count)];
|
||||||
|
}));
|
||||||
|
return titles.map(title => counts.get(documentName(title)) ?? 0);
|
||||||
|
};
|
||||||
|
|
||||||
document.addEventListener('page:loaded', async () => {
|
document.addEventListener('page:loaded', async () => {
|
||||||
|
try {
|
||||||
if (CONFIG.page.isPost) {
|
if (CONFIG.page.isPost) {
|
||||||
// Fix issue #118
|
// Fix issue #118
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent
|
// https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent
|
||||||
const title = document.querySelector('.post-title').textContent.trim();
|
const title = document.querySelector('.post-title').textContent.trim();
|
||||||
const doc = articles.doc(title);
|
let increaseCount = CONFIG.hostname === location.hostname;
|
||||||
let increaseCount = CONFIG.hostname === location.hostname;
|
if (localStorage.getItem(title)) {
|
||||||
if (localStorage.getItem(title)) {
|
increaseCount = false;
|
||||||
increaseCount = false;
|
} else {
|
||||||
} else {
|
// Mark as visited
|
||||||
// Mark as visited
|
localStorage.setItem(title, true);
|
||||||
localStorage.setItem(title, true);
|
}
|
||||||
|
const count = await getCount(title, increaseCount);
|
||||||
|
document.querySelector('.firestore-visitors-count').innerText = count;
|
||||||
|
} else if (CONFIG.page.isHome) {
|
||||||
|
const titles = [...document.querySelectorAll('.post-title')].map(element => element.textContent.trim());
|
||||||
|
const counts = await getCounts(titles);
|
||||||
|
const metas = document.querySelectorAll('.firestore-visitors-count');
|
||||||
|
counts.forEach((val, idx) => {
|
||||||
|
metas[idx].innerText = val;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const count = await getCount(doc, increaseCount);
|
} catch (error) {
|
||||||
document.querySelector('.firestore-visitors-count').innerText = count;
|
console.warn('Failed to load Firestore visitor count:', error);
|
||||||
} else if (CONFIG.page.isHome) {
|
|
||||||
const promises = [...document.querySelectorAll('.post-title')].map(element => {
|
|
||||||
const title = element.textContent.trim();
|
|
||||||
const doc = articles.doc(title);
|
|
||||||
return getCount(doc);
|
|
||||||
});
|
|
||||||
const counts = await Promise.all(promises);
|
|
||||||
const metas = document.querySelectorAll('.firestore-visitors-count');
|
|
||||||
counts.forEach((val, idx) => {
|
|
||||||
metas[idx].innerText = val;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
@ -19,6 +19,17 @@ HTMLElement.prototype.wrap = function(wrapper) {
|
|||||||
|
|
||||||
NexT.utils = {
|
NexT.utils = {
|
||||||
|
|
||||||
|
scrollTo(target, top) {
|
||||||
|
if (window.CSS?.supports('scroll-behavior', 'smooth')) {
|
||||||
|
target.scrollTo({
|
||||||
|
top,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
target.scrollTo(0, top);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
registerExtURL() {
|
registerExtURL() {
|
||||||
document.querySelectorAll('span.exturl').forEach(element => {
|
document.querySelectorAll('span.exturl').forEach(element => {
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
@ -193,12 +204,7 @@ NexT.utils = {
|
|||||||
}, { passive: true });
|
}, { passive: true });
|
||||||
|
|
||||||
backToTop?.addEventListener('click', () => {
|
backToTop?.addEventListener('click', () => {
|
||||||
window.anime({
|
NexT.utils.scrollTo(window, 0);
|
||||||
targets : document.scrollingElement,
|
|
||||||
duration : 500,
|
|
||||||
easing : 'linear',
|
|
||||||
scrollTop: 0
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -258,12 +264,7 @@ NexT.utils = {
|
|||||||
}, 1000);
|
}, 1000);
|
||||||
if (!CONFIG.stickytabs) return;
|
if (!CONFIG.stickytabs) return;
|
||||||
const offset = nav.parentNode.getBoundingClientRect().top + window.scrollY + 10;
|
const offset = nav.parentNode.getBoundingClientRect().top + window.scrollY + 10;
|
||||||
window.anime({
|
NexT.utils.scrollTo(window, offset);
|
||||||
targets : document.scrollingElement,
|
|
||||||
duration : 500,
|
|
||||||
easing : 'linear',
|
|
||||||
scrollTop: offset
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -311,15 +312,8 @@ NexT.utils = {
|
|||||||
element.addEventListener('click', event => {
|
element.addEventListener('click', event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const offset = target.getBoundingClientRect().top + window.scrollY;
|
const offset = target.getBoundingClientRect().top + window.scrollY;
|
||||||
window.anime({
|
NexT.utils.scrollTo(window, offset);
|
||||||
targets : document.scrollingElement,
|
history.pushState(null, document.title, element.href);
|
||||||
duration : 500,
|
|
||||||
easing : 'linear',
|
|
||||||
scrollTop: offset,
|
|
||||||
complete : () => {
|
|
||||||
history.pushState(null, document.title, element.href);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
return target;
|
return target;
|
||||||
});
|
});
|
||||||
@ -342,8 +336,6 @@ NexT.utils = {
|
|||||||
const target = navItemList[index];
|
const target = navItemList[index];
|
||||||
if (!target || target.classList.contains('active-current')) return;
|
if (!target || target.classList.contains('active-current')) return;
|
||||||
|
|
||||||
const singleHeight = navItemList[navItemList.length - 1].offsetHeight;
|
|
||||||
|
|
||||||
nav.querySelectorAll('.active').forEach(navItem => {
|
nav.querySelectorAll('.active').forEach(navItem => {
|
||||||
navItem.classList.remove('active', 'active-current');
|
navItem.classList.remove('active', 'active-current');
|
||||||
});
|
});
|
||||||
@ -356,9 +348,13 @@ NexT.utils = {
|
|||||||
if (activateEle.classList.contains('nav-item')) {
|
if (activateEle.classList.contains('nav-item')) {
|
||||||
activateEle.classList.add('active');
|
activateEle.classList.add('active');
|
||||||
} else { // .nav-child or .nav
|
} else { // .nav-child or .nav
|
||||||
// scrollHeight isn't reliable for transitioning child items.
|
// Exclude nested lists so each item's own wrapped row is counted once.
|
||||||
|
const listItemHeight = [...activateEle.children].reduce((height, navItem) => {
|
||||||
|
const navChild = [...navItem.children].find(element => element.classList.contains('nav-child'));
|
||||||
|
return height + navItem.getBoundingClientRect().height - (navChild?.getBoundingClientRect().height || 0);
|
||||||
|
}, 0);
|
||||||
// The last nav-item in a list has a margin-bottom of 5px.
|
// The last nav-item in a list has a margin-bottom of 5px.
|
||||||
navChildHeight += (singleHeight * activateEle.childElementCount) + 5;
|
navChildHeight += listItemHeight + 5;
|
||||||
activateEle.style.setProperty('--height', `${navChildHeight}px`);
|
activateEle.style.setProperty('--height', `${navChildHeight}px`);
|
||||||
}
|
}
|
||||||
activateEle = activateEle.parentElement;
|
activateEle = activateEle.parentElement;
|
||||||
@ -367,12 +363,7 @@ NexT.utils = {
|
|||||||
// Scrolling to center active TOC element if TOC content is taller then viewport.
|
// Scrolling to center active TOC element if TOC content is taller then viewport.
|
||||||
const tocElement = document.querySelector(CONFIG.scheme === 'Pisces' || CONFIG.scheme === 'Gemini' ? '.sidebar-panel-container' : '.sidebar');
|
const tocElement = document.querySelector(CONFIG.scheme === 'Pisces' || CONFIG.scheme === 'Gemini' ? '.sidebar-panel-container' : '.sidebar');
|
||||||
if (!document.querySelector('.sidebar-toc-active')) return;
|
if (!document.querySelector('.sidebar-toc-active')) return;
|
||||||
window.anime({
|
NexT.utils.scrollTo(tocElement, tocElement.scrollTop - (tocElement.offsetHeight / 2) + target.getBoundingClientRect().top - tocElement.getBoundingClientRect().top);
|
||||||
targets : tocElement,
|
|
||||||
duration : 200,
|
|
||||||
easing : 'linear',
|
|
||||||
scrollTop: tocElement.scrollTop - (tocElement.offsetHeight / 2) + target.getBoundingClientRect().top - tocElement.getBoundingClientRect().top
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
updateSidebarPosition() {
|
updateSidebarPosition() {
|
||||||
|
|||||||
73
test/helpers/engine.js
Normal file
73
test/helpers/engine.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const helpers = {};
|
||||||
|
|
||||||
|
global.hexo = {
|
||||||
|
extend: {
|
||||||
|
helper: {
|
||||||
|
register(name, helper) {
|
||||||
|
helpers[name] = helper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
require('../../scripts/helpers/engine');
|
||||||
|
delete global.hexo;
|
||||||
|
|
||||||
|
describe('engine', () => {
|
||||||
|
const context = {
|
||||||
|
next_version: '8.28.0',
|
||||||
|
theme : {
|
||||||
|
js : 'js',
|
||||||
|
vendors: {
|
||||||
|
internal : 'local',
|
||||||
|
custom_cdn_url: '',
|
||||||
|
library : {
|
||||||
|
url: 'https://cdn.example.com/library.js'
|
||||||
|
},
|
||||||
|
secure_library: {
|
||||||
|
url : 'https://cdn.example.com/secure.js',
|
||||||
|
integrity: 'sha256-example'
|
||||||
|
},
|
||||||
|
stylesheet: {
|
||||||
|
url: 'https://cdn.example.com/library.css'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
url_for(path) {
|
||||||
|
return '/' + path;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const nextJs = helpers.next_js.bind(context);
|
||||||
|
const nextVendors = helpers.next_vendors.bind(context);
|
||||||
|
|
||||||
|
it('keeps defer as the default for NexT scripts', () => {
|
||||||
|
nextJs('main.js').should.eql('<script src="/js/main.js" defer></script>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports custom loading options for NexT scripts', () => {
|
||||||
|
nextJs('main.js', { defer: false }).should.eql('<script src="/js/main.js"></script>');
|
||||||
|
nextJs('main.js', { async: true }).should.eql('<script src="/js/main.js" async></script>');
|
||||||
|
nextJs('main.js', { defer: true, async: true }).should.eql('<script src="/js/main.js" defer async></script>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves existing NexT script options', () => {
|
||||||
|
nextJs('main.js', { pjax: true, module: true }).should.eql('<script data-pjax type="module" src="/js/main.js" defer></script>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps defer as the default for vendor scripts', () => {
|
||||||
|
nextVendors('library').should.eql('<script src="https://cdn.example.com/library.js" defer></script>');
|
||||||
|
nextVendors('secure_library').should.eql('<script src="https://cdn.example.com/secure.js" integrity="sha256-example" crossorigin="anonymous" defer></script>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports custom loading options for vendor scripts', () => {
|
||||||
|
nextVendors('library', { defer: false }).should.eql('<script src="https://cdn.example.com/library.js"></script>');
|
||||||
|
nextVendors('library', { async: true }).should.eql('<script src="https://cdn.example.com/library.js" async></script>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not apply script loading options to stylesheets', () => {
|
||||||
|
nextVendors('stylesheet', { async: true }).should.eql('<link rel="stylesheet" href="https://cdn.example.com/library.css">');
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,6 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
describe('Helpers', () => {
|
describe('Helpers', () => {
|
||||||
|
require('./engine');
|
||||||
require('./font');
|
require('./font');
|
||||||
require('./next-url');
|
require('./next-url');
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user