├── .editorconfig
├── .gitattributes
├── .githooks
├── install.js
└── pre-commit
├── .github
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE
│ ├── bug-report.yml
│ ├── config.yml
│ ├── feature-request.yml
│ └── other.yml
├── PULL_REQUEST_TEMPLATE.md
├── config.yml
├── issue_label_bot.yaml
├── label-commenter-config.yml
├── labeler.yml
├── release.yml
└── workflows
│ ├── codeql.yml
│ ├── label-commenter.yml
│ ├── labeler.yml
│ ├── linter.yml
│ ├── lock.yml
│ ├── npm-publish.yml
│ └── tester.yml
├── .gitignore
├── .stylelintrc
├── LICENSE.md
├── README.md
├── _config.yml
├── _vendors.yml
├── crowdin.yml
├── docs
├── AGPL3.md
├── AUTHORS.md
├── LICENSE.txt
├── ru
│ └── README.md
└── zh-CN
│ ├── CODE_OF_CONDUCT.md
│ ├── CONTRIBUTING.md
│ └── README.md
├── eslint.config.js
├── languages
├── README.md
├── ar.yml
├── bn.yml
├── de.yml
├── default.yml
├── en.yml
├── eo.yml
├── es.yml
├── fa.yml
├── fr.yml
├── id.yml
├── it.yml
├── ja.yml
├── ko.yml
├── nl.yml
├── pt-BR.yml
├── pt.yml
├── ru.yml
├── si.yml
├── th.yml
├── tk.yml
├── tr.yml
├── uk.yml
├── vi.yml
├── zh-CN.yml
├── zh-HK.yml
└── zh-TW.yml
├── layout
├── _layout.njk
├── _macro
│ ├── post-collapse.njk
│ ├── post.njk
│ └── sidebar.njk
├── _partials
│ ├── comments.njk
│ ├── footer.njk
│ ├── head
│ │ ├── head-unique.njk
│ │ └── head.njk
│ ├── header
│ │ ├── brand.njk
│ │ ├── index.njk
│ │ ├── menu-item.njk
│ │ ├── menu.njk
│ │ └── sub-menu.njk
│ ├── languages.njk
│ ├── page
│ │ ├── breadcrumb.njk
│ │ ├── categories.njk
│ │ ├── page-header.njk
│ │ ├── schedule.njk
│ │ └── tags.njk
│ ├── pagination.njk
│ ├── post
│ │ ├── post-copyright.njk
│ │ ├── post-followme.njk
│ │ ├── post-meta.njk
│ │ ├── post-related.njk
│ │ ├── post-reward.njk
│ │ └── post-share.njk
│ ├── search
│ │ └── index.njk
│ ├── sidebar
│ │ └── site-overview.njk
│ └── widgets.njk
├── _scripts
│ ├── index.njk
│ └── vendors.njk
├── _third-party
│ ├── addtoany.njk
│ ├── analytics
│ │ ├── baidu-analytics.njk
│ │ ├── cloudflare.njk
│ │ ├── google-analytics.njk
│ │ ├── growingio.njk
│ │ ├── index.njk
│ │ ├── matomo.njk
│ │ ├── microsoft-clarity.njk
│ │ ├── plausible.njk
│ │ └── umami.njk
│ ├── chat
│ │ ├── chatra.njk
│ │ └── tidio.njk
│ ├── comments
│ │ ├── changyan.njk
│ │ ├── disqus.njk
│ │ ├── disqusjs.njk
│ │ ├── gitalk.njk
│ │ ├── isso.njk
│ │ ├── livere.njk
│ │ └── utterances.njk
│ ├── fancybox.njk
│ ├── index.njk
│ ├── math
│ │ ├── index.njk
│ │ ├── katex.njk
│ │ └── mathjax.njk
│ ├── pace.njk
│ ├── quicklink.njk
│ ├── search
│ │ ├── algolia-search.njk
│ │ └── localsearch.njk
│ ├── statistics
│ │ ├── busuanzi-counter.njk
│ │ ├── firestore.njk
│ │ ├── index.njk
│ │ └── lean-analytics.njk
│ └── tags
│ │ ├── mermaid.njk
│ │ ├── pdf.njk
│ │ └── wavedrom.njk
├── archive.njk
├── category.njk
├── index.njk
├── page.njk
├── post.njk
└── tag.njk
├── package.json
├── renovate.json
├── scripts
├── events
│ ├── index.js
│ └── lib
│ │ ├── config.js
│ │ ├── highlight.js
│ │ ├── injects.js
│ │ ├── navigation.js
│ │ ├── utils.js
│ │ └── vendors.js
├── filters
│ ├── comment
│ │ ├── changyan.js
│ │ ├── common.js
│ │ ├── default-config.js
│ │ ├── disqus.js
│ │ ├── disqusjs.js
│ │ ├── gitalk.js
│ │ ├── isso.js
│ │ ├── livere.js
│ │ └── utterances.js
│ ├── default-injects.js
│ ├── locals.js
│ ├── minify.js
│ └── post.js
├── helpers
│ ├── engine.js
│ ├── font.js
│ ├── navigation.js
│ ├── next-config.js
│ ├── next-paginator.js
│ ├── next-url.js
│ └── next-vendors.js
└── tags
│ ├── button.js
│ ├── caniuse.js
│ ├── center-quote.js
│ ├── group-pictures.js
│ ├── index.js
│ ├── label.js
│ ├── link-grid.js
│ ├── mermaid.js
│ ├── note.js
│ ├── pdf.js
│ ├── tabs.js
│ ├── video.js
│ └── wavedrom.js
├── source
├── css
│ ├── _colors.styl
│ ├── _common
│ │ ├── components
│ │ │ ├── back-to-top.styl
│ │ │ ├── index.styl
│ │ │ ├── pages
│ │ │ │ ├── breadcrumb.styl
│ │ │ │ ├── categories.styl
│ │ │ │ ├── index.styl
│ │ │ │ ├── schedule.styl
│ │ │ │ └── tag-cloud.styl
│ │ │ ├── post
│ │ │ │ ├── index.styl
│ │ │ │ ├── post-body.styl
│ │ │ │ ├── post-collapse.styl
│ │ │ │ ├── post-followme.styl
│ │ │ │ ├── post-footer.styl
│ │ │ │ ├── post-gallery.styl
│ │ │ │ ├── post-header.styl
│ │ │ │ ├── post-nav.styl
│ │ │ │ ├── post-reward.styl
│ │ │ │ └── post-widgets.styl
│ │ │ ├── reading-progress.styl
│ │ │ └── third-party
│ │ │ │ ├── disqusjs.styl
│ │ │ │ ├── gitalk.styl
│ │ │ │ ├── index.styl
│ │ │ │ ├── math.styl
│ │ │ │ ├── search.styl
│ │ │ │ └── utterances.styl
│ │ ├── outline
│ │ │ ├── footer
│ │ │ │ └── index.styl
│ │ │ ├── header
│ │ │ │ ├── bookmark.styl
│ │ │ │ ├── github-banner.styl
│ │ │ │ ├── index.styl
│ │ │ │ ├── menu.styl
│ │ │ │ ├── site-meta.styl
│ │ │ │ └── site-nav.styl
│ │ │ ├── index.styl
│ │ │ ├── mobile.styl
│ │ │ └── sidebar
│ │ │ │ ├── index.styl
│ │ │ │ ├── related-posts.styl
│ │ │ │ ├── sidebar-author-links.styl
│ │ │ │ ├── sidebar-author.styl
│ │ │ │ ├── sidebar-blogroll.styl
│ │ │ │ ├── sidebar-button.styl
│ │ │ │ ├── sidebar-copyright.styl
│ │ │ │ ├── sidebar-nav.styl
│ │ │ │ ├── sidebar-toc.styl
│ │ │ │ ├── sidebar-toggle.styl
│ │ │ │ └── site-state.styl
│ │ └── scaffolding
│ │ │ ├── base.styl
│ │ │ ├── buttons.styl
│ │ │ ├── comments.styl
│ │ │ ├── highlight
│ │ │ ├── copy-code.styl
│ │ │ ├── fold.styl
│ │ │ └── index.styl
│ │ │ ├── index.styl
│ │ │ ├── normalize.styl
│ │ │ ├── pagination.styl
│ │ │ ├── tables.styl
│ │ │ ├── tags
│ │ │ ├── blockquote-center.styl
│ │ │ ├── group-pictures.styl
│ │ │ ├── index.styl
│ │ │ ├── label.styl
│ │ │ ├── link-grid.styl
│ │ │ ├── mermaid.styl
│ │ │ ├── note.styl
│ │ │ ├── pdf.styl
│ │ │ ├── tabs.styl
│ │ │ └── wavedrom.styl
│ │ │ └── toggles.styl
│ ├── _mixins.styl
│ ├── _schemes
│ │ ├── Gemini
│ │ │ └── index.styl
│ │ ├── Mist
│ │ │ ├── _header.styl
│ │ │ ├── _layout.styl
│ │ │ ├── _menu.styl
│ │ │ ├── _posts-expand.styl
│ │ │ └── index.styl
│ │ ├── Muse
│ │ │ ├── _header.styl
│ │ │ ├── _layout.styl
│ │ │ ├── _menu.styl
│ │ │ ├── _sidebar.styl
│ │ │ ├── _sub-menu.styl
│ │ │ └── index.styl
│ │ └── Pisces
│ │ │ ├── _header.styl
│ │ │ ├── _layout.styl
│ │ │ ├── _menu.styl
│ │ │ ├── _sidebar.styl
│ │ │ ├── _sub-menu.styl
│ │ │ └── index.styl
│ ├── _variables
│ │ ├── Gemini.styl
│ │ ├── Mist.styl
│ │ ├── Muse.styl
│ │ ├── Pisces.styl
│ │ └── base.styl
│ ├── main.styl
│ └── noscript.styl
├── images
│ ├── apple-touch-icon-next.png
│ ├── avatar.gif
│ ├── favicon-16x16-next.png
│ ├── favicon-32x32-next.png
│ ├── logo-algolia-nebula-blue-full.svg
│ └── logo.svg
└── js
│ ├── bookmark.js
│ ├── comments-buttons.js
│ ├── comments.js
│ ├── config.js
│ ├── motion.js
│ ├── next-boot.js
│ ├── pjax.js
│ ├── schedule.js
│ ├── sidebar.js
│ ├── third-party
│ ├── addtoany.js
│ ├── analytics
│ │ ├── baidu-analytics.js
│ │ ├── google-analytics.js
│ │ ├── growingio.js
│ │ └── matomo.js
│ ├── chat
│ │ ├── chatra.js
│ │ └── tidio.js
│ ├── comments
│ │ ├── changyan.js
│ │ ├── disqus.js
│ │ ├── disqusjs.js
│ │ ├── gitalk.js
│ │ ├── isso.js
│ │ ├── livere.js
│ │ └── utterances.js
│ ├── fancybox.js
│ ├── math
│ │ ├── katex.js
│ │ └── mathjax.js
│ ├── pace.js
│ ├── quicklink.js
│ ├── search
│ │ ├── algolia-search.js
│ │ └── local-search.js
│ ├── statistics
│ │ ├── firestore.js
│ │ └── lean-analytics.js
│ └── tags
│ │ ├── mermaid.js
│ │ ├── pdf.js
│ │ └── wavedrom.js
│ └── utils.js
└── test
├── helpers
├── font.js
├── index.js
└── next-url.js
├── index.js
├── tags
├── button.js
├── caniuse.js
├── center-quote.js
├── group-pictures.js
├── index.js
├── label.js
├── link-grid.js
├── mermaid.js
├── note.js
├── pdf.js
├── tabs.js
└── video.js
└── validate
└── index.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 | indent_style = space
11 | indent_size = 2
12 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | test/* linguist-vendored
2 |
--------------------------------------------------------------------------------
/.githooks/install.js:
--------------------------------------------------------------------------------
1 | const { spawn } = require('child_process');
2 | const path = require('path');
3 |
4 | const subprocess = spawn('git', ['config', '--local', 'core.hooksPath', path.join(__dirname, '.githooks/')]);
5 |
6 | subprocess.on('error', () => {
7 | console.error('Failed to install git hook.');
8 | });
9 |
--------------------------------------------------------------------------------
/.githooks/pre-commit:
--------------------------------------------------------------------------------
1 | npm run eslint && npm test
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: GitHub Discussions
4 | url: https://github.com/next-theme/hexo-theme-next/discussions
5 | about: Please ask and answer questions here.
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request.yml:
--------------------------------------------------------------------------------
1 | name: Feature Request
2 | description: Suggest an idea for this project
3 | #title: ""
4 | labels:
5 | - Feature Request
6 | #assignees: ""
7 | body:
8 | - type: markdown
9 | id: overall
10 | attributes:
11 | value: |
12 | Please follow this Issue template to provide relevant information, such as source code repository, website URL and screenshots, which will help us investigate.
13 | 请按照此 Issue 模版提供相关信息,例如源码仓库、网站链接和屏幕截图,这将有助于我们进行调查。
14 | - type: checkboxes
15 | id: checklist
16 | attributes:
17 | label: Issue Checklist
18 | description: |
19 | (我确认我已经查看了)
20 | options:
21 | - label: I am using NexT version 8.0 or later.
22 | required: true
23 | - label: I have already read the [Troubleshooting page of Hexo](https://hexo.io/docs/troubleshooting) and [Troubleshooting page of NexT](https://theme-next.js.org/docs/troubleshooting.html).
24 | required: true
25 | - label: I have already searched for current [issues](https://github.com/next-theme/hexo-theme-next/issues), which does not help me.
26 | required: true
27 | - type: textarea
28 | id: expected-behavior
29 | attributes:
30 | label: Expected behavior
31 | description: "(预期行为)"
32 | validations:
33 | required: true
34 | - type: textarea
35 | id: actual-behavior
36 | attributes:
37 | label: Actual behavior
38 | description: |
39 | (实际行为)
40 | Please provide the following information (请同时提供网站链接和屏幕截图)
41 | value: |
42 | - Links to demo site with this issue:
43 | - Links to repository or source code of the blog:
44 | - Screenshots:
45 | validations:
46 | required: true
47 | - type: textarea
48 | id: reproduce
49 | attributes:
50 | label: Steps to reproduce the behavior
51 | description: "(重现步骤)"
52 | validations:
53 | required: true
54 | - type: textarea
55 | id: other-info
56 | attributes:
57 | label: Other Information
58 | description: "e.g. Browser, System"
59 | validations:
60 | required: false
61 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/other.yml:
--------------------------------------------------------------------------------
1 | name: Other
2 | description: Not a feature request or bug report
3 | #title: ""
4 | labels:
5 | - Question
6 | #assignees: ""
7 | body:
8 | - type: markdown
9 | id: overall
10 | attributes:
11 | value: |
12 | Please follow this Issue template to provide relevant information, such as source code repository, website URL and screenshots, which will help us investigate.
13 | 请按照此 Issue 模版提供相关信息,例如源码仓库、网站链接和屏幕截图,这将有助于我们进行调查。
14 | - type: checkboxes
15 | id: checklist
16 | attributes:
17 | label: Issue Checklist
18 | description: |
19 | (我确认我已经查看了)
20 | options:
21 | - label: I am using NexT version 8.0 or later.
22 | required: true
23 | - label: I have already read the [Troubleshooting page of Hexo](https://hexo.io/docs/troubleshooting) and [Troubleshooting page of NexT](https://theme-next.js.org/docs/troubleshooting.html).
24 | required: true
25 | - label: I have already searched for current [issues](https://github.com/next-theme/hexo-theme-next/issues), which does not help me.
26 | required: true
27 | - type: textarea
28 | id: other-info
29 | attributes:
30 | label: Other Information
31 | description: "e.g. Browser, System"
32 | validations:
33 | required: true
34 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
10 |
11 | ## PR Checklist
12 |
13 |
14 | - [ ] The changes have been tested (for bug fixes / features).
15 | - [ ] [Docs](https://github.com/next-theme/theme-next-docs/tree/master/source/docs) in [NexT website](https://theme-next.js.org/docs/) have been added / updated (for features).
16 |
17 |
18 | ## PR Type
19 |
20 |
21 | - [ ] Bugfix.
22 | - [ ] Feature.
23 | - [ ] Improvement.
24 | - [ ] Code style update (e.g. formatting, linting).
25 | - [ ] Refactoring (no changes to functionality and APIs).
26 | - [ ] Documentation.
27 | - [ ] Translation.
28 | - [ ] Other... Please describe:
29 |
30 | ## What is the current behavior?
31 |
32 |
33 | Issue resolved:
34 |
35 | ## What is the new behavior?
36 |
37 |
38 | - Link to demo site with this changes:
39 | - Screenshots with this changes:
40 |
41 | ### How to use?
42 |
43 | In NexT `_config.yml`:
44 | ```yml
45 |
46 | ```
47 |
--------------------------------------------------------------------------------
/.github/config.yml:
--------------------------------------------------------------------------------
1 | # =============================================================================================== #
2 | # Configuration for welcome - https://github.com/behaviorbot/welcome
3 |
4 | # Comment to be posted to on first time issues
5 | newIssueWelcomeComment: >
6 | Thanks for opening this issue, maintainers will get back to you as soon as possible!
7 |
8 | # Comment to be posted to on PRs from first time contributors in your repository
9 | newPRWelcomeComment: >
10 | Thanks so much for opening your first PR here!
11 |
12 | # Comment to be posted to on pull requests merged by a first time user
13 | firstPRMergeComment: >
14 | Congrats on merging your first pull request here! :tada: How awesome!
15 |
--------------------------------------------------------------------------------
/.github/issue_label_bot.yaml:
--------------------------------------------------------------------------------
1 | label-alias:
2 | bug: 'Bug'
3 | feature_request: 'Feature Request'
4 | question: 'Question'
5 |
--------------------------------------------------------------------------------
/.github/label-commenter-config.yml:
--------------------------------------------------------------------------------
1 | # Configuration for Label Commenter - https://github.com/peaceiris/actions-label-commenter
2 |
3 | labels:
4 | - name: Invalid
5 | labeled:
6 | issue:
7 | body: This issue has been closed because it does not seem right.
8 | action: close
9 | - name: Need More Info
10 | labeled:
11 | issue:
12 | body: We would appreciate it if you could provide us with more info about this issue!
13 | - name: 🎯 Roadmap
14 | labeled:
15 | issue:
16 | body: |
17 | This issue has been added to the latest roadmap. :tada:
18 | - name: Support
19 | labeled:
20 | issue:
21 | body: |
22 | :wave: Hey there! we use the issue tracker exclusively for bug reports and feature requests. However, this issue appears to be a support request.
23 | Please use our [support channels](https://github.com/next-theme/hexo-theme-next#feedback) to get help with the project.
24 | action: close
25 | - name: Wontfix
26 | labeled:
27 | issue:
28 | body: There wasn't enough interest from the team or community to implement this change. Thanks for contributing and we appreciate your understanding.
29 | action: close
30 | - name: Configurations
31 | labeled:
32 | pr:
33 | body: |
34 | This pull request contains changes to the configuration file. Please make sure the documentation in [NexT website](https://theme-next.js.org/docs/) is changed or added.
35 | Please edit relevant source files here: https://github.com/next-theme/theme-next-docs/tree/master/source/docs and create a pull request with the changes here: https://github.com/next-theme/theme-next-docs/pulls
36 |
--------------------------------------------------------------------------------
/.github/labeler.yml:
--------------------------------------------------------------------------------
1 | # Configuration for labeler - https://github.com/actions/labeler
2 |
3 | 📦 Dependencies:
4 | - changed-files:
5 | - any-glob-to-any-file: package.json
6 | Configurations:
7 | - changed-files:
8 | - any-glob-to-any-file: _config.yml
9 | CSS:
10 | - changed-files:
11 | - any-glob-to-any-file: source/css/**/*
12 | 📖 Docs:
13 | - changed-files:
14 | - any-glob-to-any-file: docs/**/*
15 | Layout:
16 | - changed-files:
17 | - any-glob-to-any-file: layout/**/*
18 | 🌍 i18n:
19 | - changed-files:
20 | - any-glob-to-any-file: languages/**/*
21 | Actions:
22 | - changed-files:
23 | - any-glob-to-any-file: .github/workflows/**/*
24 | 🔌 3rd Party Plugin:
25 | - changed-files:
26 | - any-glob-to-any-file: '**/*third-party/**/*'
27 |
--------------------------------------------------------------------------------
/.github/release.yml:
--------------------------------------------------------------------------------
1 | changelog:
2 | categories:
3 | - title: '💥 Breaking Changes'
4 | labels:
5 | - '💥 Breaking Change'
6 |
7 | - title: '🌟 New Features'
8 | labels:
9 | - '🌟 New Feature'
10 |
11 | - title: '⭐ Features'
12 | labels:
13 | - '⭐ Feature'
14 |
15 | - title: '🐞 Bug Fixes'
16 | labels:
17 | - '🐞 Bug Fix'
18 |
19 | - title: '🛠 Improvements'
20 | labels:
21 | - '🛠 Improvement'
22 |
23 | - title: '🌀 External Changes'
24 | labels:
25 | - '📦 Dependencies'
26 | - 'Actions'
27 |
28 | - title: '📖 Documentation'
29 | labels:
30 | - '📖 Docs'
31 |
32 | - title: '🌍 Localization'
33 | labels:
34 | - '🌍 i18n'
35 |
36 | exclude:
37 | labels:
38 | - 'Skip Release'
39 |
--------------------------------------------------------------------------------
/.github/workflows/codeql.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL"
2 |
3 | on:
4 | push:
5 | branches: [ "master" ]
6 | pull_request:
7 | branches: [ "master" ]
8 | schedule:
9 | - cron: "33 18 * * 0"
10 |
11 | jobs:
12 | analyze:
13 | name: Analyze
14 | runs-on: ubuntu-latest
15 | permissions:
16 | actions: read
17 | contents: read
18 | security-events: write
19 |
20 | strategy:
21 | fail-fast: false
22 | matrix:
23 | language: [ javascript ]
24 |
25 | steps:
26 | - name: Checkout
27 | uses: actions/checkout@v4
28 |
29 | - name: Initialize CodeQL
30 | uses: github/codeql-action/init@v3
31 | with:
32 | languages: ${{ matrix.language }}
33 | queries: +security-and-quality
34 |
35 | - name: Autobuild
36 | uses: github/codeql-action/autobuild@v3
37 |
38 | - name: Perform CodeQL Analysis
39 | uses: github/codeql-action/analyze@v3
40 | with:
41 | category: "/language:${{ matrix.language }}"
42 |
--------------------------------------------------------------------------------
/.github/workflows/label-commenter.yml:
--------------------------------------------------------------------------------
1 | name: Label Commenter
2 |
3 | on:
4 | issues:
5 | types:
6 | - labeled
7 | pull_request_target:
8 | types:
9 | - labeled
10 |
11 | jobs:
12 | comment:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v4
16 | with:
17 | ref: master
18 | - name: Label Commenter
19 | uses: peaceiris/actions-label-commenter@v1
20 |
--------------------------------------------------------------------------------
/.github/workflows/labeler.yml:
--------------------------------------------------------------------------------
1 | name: Pull Request Labeler
2 |
3 | on:
4 | - pull_request_target
5 |
6 | jobs:
7 | triage:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/labeler@v5
11 | # https://github.com/peaceiris/actions-label-commenter#work-with-other-auto-label-actions
12 | with:
13 | repo-token: "${{ secrets.GH_PAT }}"
14 |
--------------------------------------------------------------------------------
/.github/workflows/linter.yml:
--------------------------------------------------------------------------------
1 | name: Linter
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | linter:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v4
10 | - name: Use Node.js
11 | uses: actions/setup-node@v4
12 | - name: Install Dependencies
13 | run: npm install
14 | - run: npm run eslint
15 | - name: Coverage
16 | run: npx c8 --reporter=lcovonly npm test
17 | env:
18 | CI: true
19 | - name: Coveralls
20 | uses: coverallsapp/github-action@master
21 | with:
22 | github-token: ${{ secrets.GITHUB_TOKEN }}
23 | - name: Set up Python
24 | uses: actions/setup-python@v5
25 | with:
26 | python-version: '3.x'
27 | - name: Install Dependencies
28 | run: |
29 | python -m pip install --upgrade pip
30 | pip install curlylint
31 | - run: curlylint --include .njk layout
32 |
--------------------------------------------------------------------------------
/.github/workflows/lock.yml:
--------------------------------------------------------------------------------
1 | # Configuration for Lock Threads - https://github.com/dessant/lock-threads
2 | name: Lock Threads
3 |
4 | on:
5 | schedule:
6 | - cron: '0 0 * * *'
7 |
8 | jobs:
9 | lock:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: dessant/lock-threads@v5
13 | with:
14 | github-token: ${{ github.token }}
15 | issue-comment: >
16 | This thread has been automatically locked since there has not been
17 | any recent activity after it was closed. It is possible issue was
18 | solved or at least outdated. Feel free to open new for related bugs.
19 | process-only: 'issues'
20 |
--------------------------------------------------------------------------------
/.github/workflows/npm-publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish to npm
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*'
7 |
8 | jobs:
9 | npm-publish:
10 | runs-on: ubuntu-latest
11 | permissions:
12 | contents: write
13 | id-token: write
14 | steps:
15 | - uses: actions/checkout@v4
16 | - name: Use Node.js
17 | uses: actions/setup-node@v4
18 | - run: |
19 | npm install
20 | npm publish --provenance
21 | env:
22 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
23 |
--------------------------------------------------------------------------------
/.github/workflows/tester.yml:
--------------------------------------------------------------------------------
1 | name: Tester
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | tester:
7 | runs-on: ${{ matrix.os }}
8 | strategy:
9 | matrix:
10 | os: [ubuntu-latest, windows-latest, macos-latest]
11 | fail-fast: false
12 | steps:
13 | - uses: actions/checkout@v4
14 | with:
15 | repository: hexojs/hexo-starter
16 | - name: Use Node.js
17 | uses: actions/setup-node@v4
18 | - name: Install Dependencies
19 | run: npm install
20 | - name: Install hexo-tag-embed
21 | run: npm install hexo-tag-embed
22 | - uses: actions/checkout@v4
23 | with:
24 | path: themes/next
25 | - uses: actions/checkout@v4
26 | with:
27 | repository: hexojs/hexo-many-posts
28 | path: source/_posts/hexo-many-posts
29 | - run: npx hexo config theme next
30 | - uses: DamianReeves/write-file-action@master
31 | with:
32 | path: themes/next/scripts/error.js
33 | contents: |
34 | hexo.log.error = function(...params) {
35 | console.error("ERROR", ...params);
36 | process.exit(1);
37 | }
38 | - name: Test
39 | run: npx hexo g
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea/
3 | .vscode/
4 | *.log
5 | *.iml
6 | yarn.lock
7 | package-lock.json
8 | node_modules/
9 | .nyc_output/
10 | coverage/
11 |
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "stylelint-stylus"
4 | ],
5 | "extends": [
6 | "stylelint-stylus/standard"
7 | ],
8 | "rules": {
9 | "stylus/semicolon": "always",
10 | "stylus/pythonic": "never",
11 | "stylus/declaration-colon": "always",
12 | "stylus/number-leading-zero": "never",
13 | "stylus/selector-list-comma": "always",
14 | "stylus/selector-list-comma-newline-after": "never-multi-line",
15 | "stylus/media-feature-colon": "always",
16 | "stylus/single-line-comment": null,
17 | "stylus/single-line-comment-no-empty": null,
18 | "stylus/block-closing-brace-newline-after": "never-single-line"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/crowdin.yml:
--------------------------------------------------------------------------------
1 | files:
2 | - source: /languages/en.yml
3 | translation: /languages/%two_letters_code%.%file_extension%
4 | languages_mapping:
5 | two_letters_code:
6 | zh-CN: zh-CN
7 | zh-TW: zh-TW
8 | zh-HK: zh-HK
9 | pt-BR: pt-BR
10 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | const config = require("@next-theme/eslint-config");
2 |
3 | module.exports = config;
4 |
--------------------------------------------------------------------------------
/languages/README.md:
--------------------------------------------------------------------------------
1 | # Internationalization (i18n)
2 |
3 | [](https://crowdin.com/project/hexo-theme-next)
4 |
5 | You can use internationalization to present your site in different languages. The default language is set by modifying the `language` setting in Hexo `_config.yml`. You can also set multiple languages and modify the order of default languages.
6 |
7 | ```yml
8 | language: en
9 | ```
10 |
11 | ```yml
12 | language:
13 | - zh-CN
14 | - en
15 | ```
16 |
17 | ## Override Default Translations
18 |
19 | If you would like to customize the default translation, you do not need to modify the translation files in the `languages` directory. You can override all translations using [Data Files](https://hexo.io/docs/data-files).
20 |
21 | 1. Creat a `languages.yml` in `source/_data`.
22 | 2. Insert following codes: (be careful about the two-space indent)
23 |
24 | ```yml
25 | # language
26 | zh-CN:
27 | # items
28 | post:
29 | copyright:
30 | # the translation you perfer
31 | author: 本文博主
32 | en:
33 | menu:
34 | schedule: Calendar
35 | ```
36 |
37 | ## Improve Translations
38 |
39 | The files in the `language` directory are automatically generated, you do not need to modify them directly. Please submit translations via [Crowdin](https://crowdin.com/project/hexo-theme-next) if you would like to add or improve translation for NexT theme.
40 |
--------------------------------------------------------------------------------
/languages/default.yml:
--------------------------------------------------------------------------------
1 | en.yml
--------------------------------------------------------------------------------
/languages/zh-CN.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: 简体中文
3 | title:
4 | archive: 归档
5 | category: 分类
6 | tag: 标签
7 | schedule: 日程表
8 | menu:
9 | home: 首页
10 | archives: 归档
11 | categories: 分类
12 | tags: 标签
13 | about: 关于
14 | search: 搜索
15 | schedule: 日程表
16 | sitemap: 站点地图
17 | commonweal: 公益 404
18 | sidebar:
19 | overview: 站点概览
20 | toc: 文章目录
21 | links: 链接
22 | post:
23 | posted: 发表于
24 | edited: 更新于
25 | created: 创建时间
26 | modified: 修改时间
27 | edit: 编辑
28 | in: 分类于
29 | read_more: 阅读全文
30 | untitled: 未命名
31 | sticky: 置顶
32 | views: 阅读次数
33 | related_posts: 相关文章
34 | copyright:
35 | author: 本文作者
36 | link: 本文链接
37 | post_author: 原作者
38 | post_link: 原文链接
39 | license_title: 版权声明
40 | license_content: "本博客所有文章除特别声明外,均采用 %s 许可协议。转载请注明出处!"
41 | license_content_reprint: "本文章为转载文章,已获转载许可。转载请注明出处!"
42 | footer:
43 | powered: "由 %s 强力驱动"
44 | total_views: 总访问量
45 | total_visitors: 总访客量
46 | widget:
47 | github: 在 GitHub 上关注我
48 | chat: 聊天
49 | counter:
50 | tag_cloud:
51 | zero: 暂无标签
52 | one: 目前共计 1 个标签
53 | other: "目前共计 %d 个标签"
54 | categories:
55 | zero: 暂无分类
56 | one: 目前共计 1 个分类
57 | other: "目前共计 %d 个分类"
58 | archive_posts:
59 | zero: 暂无日志。
60 | one: 目前共计 1 篇日志。
61 | other: "目前共计 %d 篇日志。"
62 | state:
63 | posts: 日志
64 | tags: 标签
65 | categories: 分类
66 | search:
67 | placeholder: 搜索...
68 | empty: "没有找到任何搜索结果:%s"
69 | hits_time: "找到 %s 个搜索结果(用时 %s 毫秒)"
70 | hits: "找到 %s 个搜索结果"
71 | cheers:
72 | um: 嗯..
73 | ok: 还行
74 | nice: 不错
75 | good: 很好
76 | great: 非常好
77 | excellent: 太棒了
78 | keep_on: 继续努力。
79 | symbol:
80 | comma: ","
81 | period: "。"
82 | colon: ":"
83 | reward:
84 | donate: 赞赏
85 | wechatpay: 微信
86 | alipay: 支付宝
87 | paypal: PayPal
88 | bitcoin: 比特币
89 | comment: 请我一杯咖啡吧!
90 | follow_me:
91 | welcome: 欢迎关注我的其它发布渠道
92 | accessibility:
93 | nav_toggle: 切换导航栏
94 | prev_page: 上一页
95 | next_page: 下一页
96 | back_to_top: 返回顶部
97 | select_lang: 选择语言
98 | symbols_count_time:
99 | count: 本文字数
100 | count_total: 站点总字数
101 | time: 阅读时长
102 | time_total: 站点阅读时长
103 | time_minutes: 分钟
104 |
--------------------------------------------------------------------------------
/languages/zh-TW.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: 繁體中文
3 | title:
4 | archive: 歸檔
5 | category: 分類
6 | tag: 標籤
7 | schedule: 文章日曆
8 | menu:
9 | home: 首頁
10 | archives: 歸檔
11 | categories: 分類
12 | tags: 標籤
13 | about: 關於
14 | search: 搜尋
15 | schedule: 時間表
16 | sitemap: 網站地圖
17 | commonweal: 公益 404
18 | sidebar:
19 | overview: 本站概要
20 | toc: 文章目錄
21 | links: 連結
22 | post:
23 | posted: 發表於
24 | edited: 更新於
25 | created: 創建時間
26 | modified: 修改時間
27 | edit: 編輯
28 | in: 分類於
29 | read_more: 閱讀全文
30 | untitled: 未命名
31 | sticky: 置頂
32 | views: 閱讀次數
33 | related_posts: 相關文章
34 | copyright:
35 | author: 本文作者
36 | link: 文章連結
37 | post_author: 原作者
38 | post_link: 文章最初發表於
39 | license_title: 版權聲明
40 | license_content: "本網誌所有文章除特別聲明外,均採用 %s 許可協議。轉載請註明出處!"
41 | license_content_reprint: "本文章為轉載文章,已獲轉載許可。轉載請註明出處!"
42 | footer:
43 | powered: "由 %s 強力驅動"
44 | total_views: 總瀏覽次數
45 | total_visitors: 訪客總數
46 | widget:
47 | github: 在 GitHub 上關注我
48 | chat: 聊天
49 | counter:
50 | tag_cloud:
51 | zero: 暫無標籤
52 | one: 目前共有 1 個標籤
53 | other: "目前共有 %d 個標籤"
54 | categories:
55 | zero: 暫無分類
56 | one: 目前共有 1 個分類
57 | other: "目前共有 %d 個分類"
58 | archive_posts:
59 | zero: 沒有文章。
60 | one: 目前共有 1 篇文章。
61 | other: "目前共有 %d 篇文章。"
62 | state:
63 | posts: 文章
64 | tags: 標籤
65 | categories: 分類
66 | search:
67 | placeholder: 搜尋...
68 | empty: "我們無法找到任何有關 %s 的搜索結果"
69 | hits_time: "找到 %s 個搜索結果(用時 %s 毫秒)"
70 | hits: "找到 %s 個搜索結果"
71 | cheers:
72 | um: 嗯..
73 | ok: 還行
74 | nice: 好
75 | good: 很好
76 | great: 非常好
77 | excellent: 太棒了
78 | keep_on: 繼續努力。
79 | symbol:
80 | comma: ","
81 | period: "。"
82 | colon: ":"
83 | reward:
84 | donate: 捐贈
85 | wechatpay: 微信支付
86 | alipay: 支付寶
87 | paypal: PayPal
88 | bitcoin: 比特幣
89 | comment: 請我喝杯咖啡!
90 | follow_me:
91 | welcome: 歡迎關注我的其它發布渠道
92 | accessibility:
93 | nav_toggle: 切換導航欄
94 | prev_page: 上一頁
95 | next_page: 下一頁
96 | back_to_top: 回到頂端
97 | select_lang: 選擇語言
98 | symbols_count_time:
99 | count: 文章字數
100 | count_total: 總字數
101 | time: 所需閱讀時間
102 | time_total: 所需總閱讀時間
103 | time_minutes: 分鐘
104 |
--------------------------------------------------------------------------------
/layout/_layout.njk:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ partial('_partials/head/head.njk', {}, {cache: theme.cache.enable}) }}
5 | {%- include '_partials/head/head-unique.njk' -%}
6 | {% block title %}{% endblock %}
7 | {{ partial('_third-party/analytics/index.njk', {}, {cache: theme.cache.enable}) }}
8 | {{ partial('_scripts/index.njk', {}, {cache: theme.cache.enable}) }}
9 | {{ partial('_third-party/index.njk', {}, {cache: theme.cache.enable}) }}
10 | {{ partial('_third-party/statistics/index.njk', {}, {cache: theme.cache.enable}) }}
11 | {%- include '_third-party/math/index.njk' -%}
12 | {%- include '_third-party/quicklink.njk' -%}
13 | {{- next_inject('head') }}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 | {%- if theme.sidebar.display !== 'remove' %}
28 | {% block sidebar %}{% endblock %}
29 | {%- endif %}
30 |
31 |
32 |
33 | {%- include '_partials/header/sub-menu.njk' -%}
34 | {% block content %}{% endblock %}
35 | {%- include '_partials/comments.njk' -%}
36 |
37 |
38 |
39 |
45 |
46 | {{ partial('_partials/widgets.njk', {}, {cache: theme.cache.enable}) }}
47 |
48 | {{- next_inject('bodyEnd') }}
49 |
50 |
51 |
--------------------------------------------------------------------------------
/layout/_macro/post-collapse.njk:
--------------------------------------------------------------------------------
1 | {% macro render(posts) %}
2 | {%- set current_year = '1970' %}
3 | {%- for post in posts.toArray() %}
4 |
5 | {%- set year = date(post.date, 'YYYY') %}
6 |
7 | {%- if year !== current_year %}
8 | {%- set current_year = year %}
9 |
10 |
11 |
12 | {%- endif %}
13 |
14 |
15 |
38 |
39 |
40 | {%- endfor %}
41 | {% endmacro %}
42 |
--------------------------------------------------------------------------------
/layout/_partials/comments.njk:
--------------------------------------------------------------------------------
1 | {%- if page.comments %}
2 | {%- if theme.injects.comment.length == 1 %}
3 | {%- set inject_item = theme.injects.comment[0] %}
4 | {{ partial(inject_item.layout, inject_item.locals, inject_item.options) }}
5 | {%- elif theme.injects.comment.length > 1 %}
6 | {%- if theme.comments.style == 'buttons' %}
7 |
12 | {%- for inject_item in theme.injects.comment %}
13 |
16 | {%- endfor %}
17 | {{- next_js('comments-buttons.js', { pjax: true }) }}
18 | {%- elif theme.comments.style == 'tabs' %}
19 |
33 | {%- endif %}
34 | {%- endif %}
35 | {%- endif %}
36 |
--------------------------------------------------------------------------------
/layout/_partials/head/head-unique.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.open_graph.enable %}
2 | {%- if theme.open_graph.options %}
3 | {{ open_graph(theme.open_graph.options) }}
4 | {%- else %}
5 | {{ open_graph() }}
6 | {%- endif %}
7 | {%- endif %}
8 |
9 | {# https://github.com/theme-next/hexo-theme-next/issues/866 #}
10 | {%- set canonical = url | replace(r/index\.html$/, '') %}
11 | {%- if not config.permalink.endsWith('.html') %}
12 | {%- set canonical = canonical | replace(r/\.html$/, '') %}
13 | {%- endif %}
14 |
15 | {%- if page.noindex %}
16 |
17 | {%- endif %}
18 | {# Exports some front-matter variables to Front-End #}
19 | {# https://hexo.io/docs/variables.html #}
20 | {{ next_data('page', next_config_unique()) }}
21 |
22 | {{ next_data('calendar',
23 | theme.calendar if page.type === 'schedule' else '')
24 | }}
25 |
--------------------------------------------------------------------------------
/layout/_partials/header/brand.njk:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {%- if theme.menu %}
5 |
6 |
7 |
8 | {%- endif %}
9 |
10 |
11 |
12 |
29 |
30 |
31 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/layout/_partials/header/index.njk:
--------------------------------------------------------------------------------
1 | {{ partial('_partials/header/brand.njk') }}
2 |
3 | {{ partial('_partials/header/menu.njk', {}, {cache: theme.cache.enable}) }}
4 |
5 | {{ partial('_partials/search/index.njk', {}, {cache: theme.cache.enable}) }}
6 |
7 | {{- next_inject('header') }}
8 |
--------------------------------------------------------------------------------
/layout/_partials/header/menu-item.njk:
--------------------------------------------------------------------------------
1 | {% macro render(node) %}
2 |
3 | {%- set itemURL = node.path %}
4 |
27 |
28 | {% endmacro %}
29 |
--------------------------------------------------------------------------------
/layout/_partials/header/menu.njk:
--------------------------------------------------------------------------------
1 | {% import 'menu-item.njk' as menu_item with context %}
2 |
3 | {%- if theme.menu or theme.algolia_search.enable or theme.local_search.enable %}
4 |
5 |
6 | {%- for node in theme.main_menu %}
7 | {{- menu_item.render(node) | trim }}
8 | {%- endfor %}
9 |
10 | {%- if theme.algolia_search.enable or theme.local_search.enable %}
11 |
16 | {%- endif %}
17 |
18 |
19 | {%- endif %}
20 |
--------------------------------------------------------------------------------
/layout/_partials/header/sub-menu.njk:
--------------------------------------------------------------------------------
1 | {% import '_partials/header/menu-item.njk' as menu_item with context %}
2 |
3 | {%- if theme.menu and is_page() %}
4 | {%- set menus = next_menu(page.path) %}
5 | {%- for menu in menus %}
6 |
11 | {%- endfor %}
12 | {%- endif %}
13 |
--------------------------------------------------------------------------------
/layout/_partials/languages.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.language_switcher and languages.length > 1 %}
2 |
3 |
4 |
5 | {{ language_name(page.lang) }}
6 |
7 |
8 |
9 | {% for language in languages %}
10 |
11 | {{ language_name(language) }}
12 |
13 | {% endfor %}
14 |
15 |
16 | {%- endif %}
17 |
--------------------------------------------------------------------------------
/layout/_partials/page/breadcrumb.njk:
--------------------------------------------------------------------------------
1 | {%- set paths = page.path.split('/') %}
2 | {%- set count = paths.length %}
3 | {%- if count > 2 %}
4 | {%- set link = '/' %}
5 |
6 | {%- for path in paths %}
7 | {%- if path != 'index.html' %}
8 | {%- if loop.index == count and path.endsWith('.html') %}
9 | {%- set link = link + path %}
10 | {%- else %}
11 | {%- set link = link + path + '/' %}
12 | {%- endif %}
13 | {%- if theme.menu_map.has(link) %}
14 | {%- set name = __('menu.' + theme.menu_map.get(link).name) | replace('menu.', '') %}
15 | {%- else %}
16 | {%- set name = path %}
17 | {%- endif %}
18 | {%- if loop.index == count - 1 and paths[loop.index] == 'index.html' %}
19 | {{ name | upper }}
20 | {%- else %}
21 | {%- if path.endsWith('.html') %}
22 | {{ name | replace(r/\.html$/, '') | upper }}
23 | {%- else %}
24 | {{ name | upper }}
25 | {%- endif %}
26 | {%- endif %}
27 | {%- endif %}
28 | {%- endfor %}
29 |
30 | {%- endif %}
31 |
--------------------------------------------------------------------------------
/layout/_partials/page/categories.njk:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ _p('counter.categories', site.categories.length) }}
4 |
5 |
6 | {{ list_categories() }}
7 |
8 |
9 |
--------------------------------------------------------------------------------
/layout/_partials/page/page-header.njk:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/layout/_partials/page/schedule.njk:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{- next_js('schedule.js', { pjax: true }) }}
4 |
--------------------------------------------------------------------------------
/layout/_partials/page/tags.njk:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ _p('counter.tag_cloud', site.tags.length) }}
4 |
5 |
6 | {{ tagcloud({
7 | min_font: theme.tagcloud.min,
8 | max_font: theme.tagcloud.max,
9 | amount : theme.tagcloud.amount,
10 | orderby : theme.tagcloud.orderby,
11 | order : theme.tagcloud.order,
12 | class : 'tag-cloud'
13 | })
14 | }}
15 |
16 |
17 |
--------------------------------------------------------------------------------
/layout/_partials/pagination.njk:
--------------------------------------------------------------------------------
1 | {%- if page.prev or page.next %}
2 |
5 | {%- endif %}
6 |
--------------------------------------------------------------------------------
/layout/_partials/post/post-copyright.njk:
--------------------------------------------------------------------------------
1 | {%- set ccIcon = ' ' %}
2 | {%- set ccText = theme.creative_commons.license | upper %}
3 |
4 |
5 |
6 |
7 | {%- if page.author %}
8 | {{ __('post.copyright.post_author') + __('symbol.colon') }}
9 | {{- page.author }}
10 | {%- elif author %}
11 | {{ __('post.copyright.author') + __('symbol.colon') }}
12 | {{- author }}
13 | {%- endif %}
14 |
15 |
16 | {%- if page.post_link %}
17 | {{ __('post.copyright.post_link') + __('symbol.colon') }}
18 | {{ next_url(page.post_link, page.post_link, {title: page.title}) }}
19 | {%- else %}
20 | {{ __('post.copyright.link') + __('symbol.colon') }}
21 | {{ next_url(page.permalink, page.permalink, {title: page.title}, true) }}
22 | {%- endif %}
23 |
24 |
25 | {%- if page.copyright_reprint %}
26 | {{ __('post.copyright.license_title') + __('symbol.colon') }}
27 | {{- __('post.copyright.license_content_reprint', next_url(ccURL, ccIcon + ccText)) }}
28 | {%- else %}
29 | {{ __('post.copyright.license_title') + __('symbol.colon') }}
30 | {{- __('post.copyright.license_content', next_url(ccURL, ccIcon + ccText)) }}
31 | {%- endif %}
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/layout/_partials/post/post-followme.njk:
--------------------------------------------------------------------------------
1 |
2 |
{{ __('follow_me.welcome') }}
3 |
4 |
5 | {%- for name, value in theme.follow_me %}
6 | {%- set link = value.split('||')[0] | trim %}
7 | {%- set icon = value.split('||')[1] | trim if value.split('||')[1] else '' %}
8 |
9 |
10 | {%- if link.endsWith('.png') or link.endsWith('.jpg') %}
11 |
12 |
13 |
14 |
15 |
16 | {{ name }}
17 |
18 |
19 |
20 | {%- else %}
21 |
22 |
23 |
24 |
25 |
26 | {{ name }}
27 |
28 | {%- endif %}
29 |
30 | {%- endfor %}
31 |
32 |
33 |
--------------------------------------------------------------------------------
/layout/_partials/post/post-related.njk:
--------------------------------------------------------------------------------
1 |
2 | {%- if theme.related_posts.icon %} {% endif %}
3 | {{ __('post.related_posts') }}
4 |
5 |
6 | {%- for path in page.related_posts %}
7 | {%- set popular_post = site.posts.findOne({ path: path }) %}
8 |
9 |
10 | {%- if popular_post.date %}
11 | {{ date(popular_post.date) }}
12 |
13 | {%- endif %}
14 | {{ popular_post.title }}
15 |
16 |
17 | {%- endfor %}
18 |
19 |
--------------------------------------------------------------------------------
/layout/_partials/post/post-reward.njk:
--------------------------------------------------------------------------------
1 |
2 |
{{ __('reward.comment') }}
3 |
4 | {{ __('reward.donate') }}
5 |
6 |
7 |
8 | {%- for name, image in theme.reward %}
9 | {%- set builtin = ['wechatpay', 'alipay', 'paypal', 'bitcoin'] %}
10 | {%- set translation = __('reward.' + name) if builtin.includes(name) else name %}
11 |
12 |
13 |
{{ translation }}
14 |
15 | {%- endfor %}
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/layout/_partials/post/post-share.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.addtoany.enable %}
2 |
3 |
4 | {%- for button in theme.addtoany.buttons %}
5 |
6 | {%- endfor %}
7 |
8 | {%- endif %}
9 |
--------------------------------------------------------------------------------
/layout/_partials/search/index.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.algolia_search.enable or theme.local_search.enable %}
2 |
3 |
23 |
24 | {%- endif %}
25 |
--------------------------------------------------------------------------------
/layout/_scripts/index.njk:
--------------------------------------------------------------------------------
1 | {%- include 'vendors.njk' -%}
2 |
3 | {%- if theme.injects.comment.length > 1 %}
4 | {{- next_js('comments.js') }}
5 | {%- endif %}
6 |
7 | {{- next_js('utils.js') }}
8 | {%- if theme.motion.enable %}
9 | {{- next_js('motion.js') }}
10 | {%- endif %}
11 |
12 | {%- if theme.sidebar.display !== 'remove' %}
13 | {{- next_js('sidebar.js') }}
14 | {%- endif %}
15 |
16 | {{- next_js('next-boot.js') }}
17 | {%- if theme.bookmark.enable %}
18 | {{- next_js('bookmark.js') }}
19 | {%- endif %}
20 | {%- if theme.pjax %}
21 | {{- next_js('pjax.js') }}
22 | {%- endif %}
23 |
--------------------------------------------------------------------------------
/layout/_scripts/vendors.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.canvas_ribbon.enable %}
2 |
3 | {%- endif %}
4 |
5 | {%- for name in js_vendors() %}
6 | {{ next_vendors(name) }}
7 | {%- endfor %}
8 |
--------------------------------------------------------------------------------
/layout/_third-party/addtoany.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.addtoany.enable %}
2 | {{ next_js('third-party/addtoany.js') }}
3 | {%- endif %}
4 |
--------------------------------------------------------------------------------
/layout/_third-party/analytics/baidu-analytics.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.baidu_analytics %}
2 | {{ next_js('third-party/analytics/baidu-analytics.js') }}
3 |
4 | {%- endif %}
5 |
--------------------------------------------------------------------------------
/layout/_third-party/analytics/cloudflare.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.cloudflare_analytics %}
2 |
3 | {%- endif %}
4 |
--------------------------------------------------------------------------------
/layout/_third-party/analytics/google-analytics.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.google_analytics.tracking_id %}
2 | {%- if not theme.google_analytics.only_pageview %}
3 |
4 | {%- endif %}
5 | {{ next_data('google_analytics', theme.google_analytics) }}
6 | {{ next_js('third-party/analytics/google-analytics.js') }}
7 | {%- endif %}
8 |
--------------------------------------------------------------------------------
/layout/_third-party/analytics/growingio.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.growingio_analytics %}
2 |
3 | {{ next_data('growingio_analytics', theme.growingio_analytics) }}
4 | {{ next_js('third-party/analytics/growingio.js') }}
5 | {%- endif %}
6 |
--------------------------------------------------------------------------------
/layout/_third-party/analytics/index.njk:
--------------------------------------------------------------------------------
1 | {%- include 'google-analytics.njk' -%}
2 | {%- include 'baidu-analytics.njk' -%}
3 | {%- include 'growingio.njk' -%}
4 | {%- include 'cloudflare.njk' -%}
5 | {%- include 'microsoft-clarity.njk' -%}
6 | {%- include 'matomo.njk' -%}
7 | {%- include 'umami.njk' -%}
8 | {%- include 'plausible.njk' -%}
9 |
--------------------------------------------------------------------------------
/layout/_third-party/analytics/matomo.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.matomo.enable %}
2 | {{ next_data('matomo', theme.matomo) }}
3 | {{ next_js('third-party/analytics/matomo.js') }}
4 | {%- endif %}
5 |
--------------------------------------------------------------------------------
/layout/_third-party/analytics/microsoft-clarity.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.clarity_analytics %}
2 |
9 | {%- endif %}
10 |
--------------------------------------------------------------------------------
/layout/_third-party/analytics/plausible.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.plausible.enable %}
2 |
3 | {%- endif %}
4 |
--------------------------------------------------------------------------------
/layout/_third-party/analytics/umami.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.umami.enable %}
2 |
3 | {%- endif %}
4 |
--------------------------------------------------------------------------------
/layout/_third-party/chat/chatra.njk:
--------------------------------------------------------------------------------
1 | {{ next_data('chatra', theme.chatra) }}
2 | {{ next_js('third-party/chat/chatra.js') }}
3 |
4 |
--------------------------------------------------------------------------------
/layout/_third-party/chat/tidio.njk:
--------------------------------------------------------------------------------
1 |
2 | {{ next_js('third-party/chat/tidio.js') }}
3 |
--------------------------------------------------------------------------------
/layout/_third-party/comments/changyan.njk:
--------------------------------------------------------------------------------
1 | {{ next_data('changyan', theme.changyan) }}
2 | {{ next_js('third-party/comments/changyan.js') }}
3 |
--------------------------------------------------------------------------------
/layout/_third-party/comments/disqus.njk:
--------------------------------------------------------------------------------
1 | {{ next_data('disqus', theme.disqus, {
2 | i18n: {
3 | disqus: __('disqus')
4 | }
5 | }) }}
6 | {{ next_js('third-party/comments/disqus.js') }}
7 |
--------------------------------------------------------------------------------
/layout/_third-party/comments/disqusjs.njk:
--------------------------------------------------------------------------------
1 | {{ next_vendors('disqusjs_css') }}
2 |
3 | {{ next_data('disqusjs', theme.disqusjs, {
4 | js: theme.vendors.disqusjs_js
5 | }) }}
6 | {{ next_js('third-party/comments/disqusjs.js') }}
7 |
--------------------------------------------------------------------------------
/layout/_third-party/comments/gitalk.njk:
--------------------------------------------------------------------------------
1 | {{ next_vendors('gitalk_css') }}
2 |
3 | {{ next_data('gitalk', theme.gitalk, {
4 | js: theme.vendors.gitalk_js,
5 | path_md5: gitalk_md5(page.path)
6 | }) }}
7 | {{ next_js('third-party/comments/gitalk.js') }}
8 |
--------------------------------------------------------------------------------
/layout/_third-party/comments/isso.njk:
--------------------------------------------------------------------------------
1 | {{ next_data('isso', theme.isso) }}
2 | {{ next_js('third-party/comments/isso.js') }}
3 |
--------------------------------------------------------------------------------
/layout/_third-party/comments/livere.njk:
--------------------------------------------------------------------------------
1 | {{ next_js('third-party/comments/livere.js') }}
2 |
--------------------------------------------------------------------------------
/layout/_third-party/comments/utterances.njk:
--------------------------------------------------------------------------------
1 | {{ next_data('utterances', theme.utterances) }}
2 | {{ next_js('third-party/comments/utterances.js') }}
3 |
--------------------------------------------------------------------------------
/layout/_third-party/fancybox.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.fancybox %}
2 | {{ next_js('third-party/fancybox.js') }}
3 | {%- endif %}
4 |
--------------------------------------------------------------------------------
/layout/_third-party/index.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.algolia_search.enable %}
2 | {%- include 'search/algolia-search.njk' -%}
3 | {%- elif theme.local_search.enable %}
4 | {%- include 'search/localsearch.njk' -%}
5 | {%- endif %}
6 |
7 | {%- if theme.chatra.enable %}
8 | {%- include 'chat/chatra.njk' -%}
9 | {%- elif theme.tidio.enable %}
10 | {%- include 'chat/tidio.njk' -%}
11 | {%- endif %}
12 |
13 | {%- include 'tags/pdf.njk' -%}
14 | {%- include 'tags/mermaid.njk' -%}
15 | {%- include 'tags/wavedrom.njk' -%}
16 |
17 | {%- include 'fancybox.njk' -%}
18 | {%- include 'pace.njk' -%}
19 | {%- include 'addtoany.njk' -%}
20 |
--------------------------------------------------------------------------------
/layout/_third-party/math/index.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.math.mathjax.enable or theme.math.katex.enable %}
2 | {%- set enable_math = false %}
3 |
4 | {%- set is_index_has_math = false %}
5 |
6 | {# At home, check if there has `mathjax: true` post #}
7 | {%- if is_home() and not theme.math.every_page %}
8 | {%- for post in page.posts.toArray() %}
9 | {%- if post.mathjax and not is_index_has_math %}
10 | {%- set is_index_has_math = true %}
11 | {%- endif %}
12 | {%- endfor %}
13 | {%- endif %}
14 |
15 | {%- if theme.math.every_page or is_index_has_math or page.mathjax %}
16 | {%- set enable_math = true %}
17 | {%- endif %}
18 |
19 | {{ next_data('enableMath', enable_math) }}
20 |
21 | {%- if theme.math.mathjax.enable %}
22 | {%- include '_third-party/math/mathjax.njk' -%}
23 | {% elif theme.math.katex.enable %}
24 | {%- include '_third-party/math/katex.njk' -%}
25 | {%- endif %}
26 | {%- endif %}
27 |
--------------------------------------------------------------------------------
/layout/_third-party/math/katex.njk:
--------------------------------------------------------------------------------
1 | {{ next_vendors('katex') }}
2 | {%- if theme.math.katex.copy_tex %}
3 | {{ next_data('katex', {
4 | copy_tex_js: theme.vendors.copy_tex_js
5 | }) }}
6 | {{ next_js('third-party/math/katex.js') }}
7 | {%- endif %}
8 |
--------------------------------------------------------------------------------
/layout/_third-party/math/mathjax.njk:
--------------------------------------------------------------------------------
1 | {{ next_data('mathjax', theme.math.mathjax, {
2 | js: theme.vendors.mathjax
3 | }) }}
4 | {{ next_js('third-party/math/mathjax.js') }}
5 |
--------------------------------------------------------------------------------
/layout/_third-party/pace.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.pace.enable %}
2 | {{ next_js('third-party/pace.js') }}
3 | {%- endif %}
4 |
--------------------------------------------------------------------------------
/layout/_third-party/quicklink.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.quicklink.enable %}
2 | {{ next_vendors('quicklink') }}
3 | {{ next_data('quicklink', page.quicklink, {
4 | url: url | replace(r/index\.html$/, '')
5 | }) }}
6 | {{ next_js('third-party/quicklink.js') }}
7 | {%- endif %}
8 |
--------------------------------------------------------------------------------
/layout/_third-party/search/algolia-search.njk:
--------------------------------------------------------------------------------
1 | {{ next_vendors('algolia_search') }}
2 |
3 | {{- next_js('third-party/search/algolia-search.js') }}
4 |
--------------------------------------------------------------------------------
/layout/_third-party/search/localsearch.njk:
--------------------------------------------------------------------------------
1 | {{ next_vendors('local_search') }}
2 | {{ next_js('third-party/search/local-search.js') }}
3 |
--------------------------------------------------------------------------------
/layout/_third-party/statistics/busuanzi-counter.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.busuanzi_count.enable %}
2 | {%- if theme.vendors.busuanzi %}
3 |
4 | {%- else %}
5 |
6 | {%- endif %}
7 | {%- endif %}
8 |
--------------------------------------------------------------------------------
/layout/_third-party/statistics/firestore.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.firestore.enable %}
2 | {{ next_vendors('firebase_app') }}
3 | {{ next_vendors('firebase_firestore') }}
4 | {{ next_data('firestore', theme.firestore) }}
5 | {{ next_js('third-party/statistics/firestore.js') }}
6 | {%- endif %}
7 |
--------------------------------------------------------------------------------
/layout/_third-party/statistics/index.njk:
--------------------------------------------------------------------------------
1 | {%- include 'busuanzi-counter.njk' -%}
2 | {%- include 'firestore.njk' -%}
3 | {%- include 'lean-analytics.njk' -%}
4 |
--------------------------------------------------------------------------------
/layout/_third-party/statistics/lean-analytics.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.leancloud_visitors.enable %}
2 | {{ next_data('leancloud_visitors', theme.leancloud_visitors) }}
3 | {{ next_js('third-party/statistics/lean-analytics.js') }}
4 | {%- endif %}
5 |
--------------------------------------------------------------------------------
/layout/_third-party/tags/mermaid.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.mermaid.enable %}
2 | {{ next_data('mermaid', theme.mermaid, {
3 | js: theme.vendors.mermaid
4 | }) }}
5 | {{ next_js('third-party/tags/mermaid.js') }}
6 | {%- endif %}
7 |
--------------------------------------------------------------------------------
/layout/_third-party/tags/pdf.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.pdf.enable %}
2 | {{ next_data('pdf', {
3 | object_url: theme.vendors.pdfobject,
4 | url: url_for('lib/pdf/web/viewer.html')
5 | }) }}
6 | {{ next_js('third-party/tags/pdf.js') }}
7 | {%- endif %}
8 |
--------------------------------------------------------------------------------
/layout/_third-party/tags/wavedrom.njk:
--------------------------------------------------------------------------------
1 | {%- if theme.wavedrom.enable %}
2 | {{ next_data('wavedrom', theme.wavedrom, {
3 | js: theme.vendors.wavedrom
4 | }) }}
5 | {{ next_data('wavedrom_skin', theme.wavedrom, {
6 | js: theme.vendors.wavedrom_skin
7 | }) }}
8 | {{ next_js('third-party/tags/wavedrom.js') }}
9 | {%- endif %}
10 |
--------------------------------------------------------------------------------
/layout/archive.njk:
--------------------------------------------------------------------------------
1 | {% extends '_layout.njk' %}
2 | {% import '_macro/post-collapse.njk' as post_template with context %}
3 | {% import '_macro/sidebar.njk' as sidebar_template with context %}
4 |
5 | {% block title %}{{ __('title.archive') }} | {{ title }}{% endblock %}
6 |
7 | {% block class %}archive posts-collapse{% endblock %}
8 |
9 | {% block content %}
10 |
11 | {#####################}
12 | {### ARCHIVE BLOCK ###}
13 | {#####################}
14 |
15 |
16 |
17 | {%- set posts_length = site.posts.length %}
18 | {%- if posts_length > 210 %}
19 | {%- set cheers = 'excellent' %}
20 | {% elif posts_length > 130 %}
21 | {%- set cheers = 'great' %}
22 | {% elif posts_length > 80 %}
23 | {%- set cheers = 'good' %}
24 | {% elif posts_length > 50 %}
25 | {%- set cheers = 'nice' %}
26 | {% elif posts_length > 30 %}
27 | {%- set cheers = 'ok' %}
28 | {% else %}
29 | {%- set cheers = 'um' %}
30 | {%- endif %}
31 |
32 |
33 |
34 | {{ post_template.render(page.posts) }}
35 |
36 |
37 |
38 | {#########################}
39 | {### END ARCHIVE BLOCK ###}
40 | {#########################}
41 |
42 | {%- include '_partials/pagination.njk' -%}
43 |
44 | {% endblock %}
45 |
46 | {% block sidebar %}
47 | {{ sidebar_template.render(false) }}
48 | {% endblock %}
49 |
--------------------------------------------------------------------------------
/layout/category.njk:
--------------------------------------------------------------------------------
1 | {% extends '_layout.njk' %}
2 | {% import '_macro/post-collapse.njk' as post_template with context %}
3 | {% import '_macro/sidebar.njk' as sidebar_template with context %}
4 |
5 | {% block title %}{{ __('title.category') }}: {{ page.category }} | {{ title }}{% endblock %}
6 |
7 | {% block class %}category posts-collapse{% endblock %}
8 |
9 | {% block content %}
10 |
11 | {######################}
12 | {### CATEGORY BLOCK ###}
13 | {######################}
14 |
15 |
16 |
17 |
21 |
22 |
23 | {{ post_template.render(page.posts) }}
24 |
25 |
26 | {##########################}
27 | {### END CATEGORY BLOCK ###}
28 | {##########################}
29 |
30 | {%- include '_partials/pagination.njk' -%}
31 |
32 | {% endblock %}
33 |
34 | {% block sidebar %}
35 | {{ sidebar_template.render(false) }}
36 | {% endblock %}
37 |
--------------------------------------------------------------------------------
/layout/index.njk:
--------------------------------------------------------------------------------
1 | {% extends '_layout.njk' %}
2 | {% import '_macro/sidebar.njk' as sidebar_template with context %}
3 |
4 | {% block title %}{{ title }}{% if theme.index_with_subtitle and subtitle %} - {{ subtitle }}{% endif %}{% endblock %}
5 |
6 | {% block class %}index posts-expand{% endblock %}
7 |
8 | {% block content %}
9 |
10 | {%- for post in page.posts.toArray() %}
11 | {{ partial('_macro/post.njk', {post: post, is_index: true}) }}
12 | {%- endfor %}
13 |
14 | {%- include '_partials/pagination.njk' -%}
15 |
16 | {% endblock %}
17 |
18 | {% block sidebar %}
19 | {{ sidebar_template.render(false) }}
20 | {% endblock %}
21 |
--------------------------------------------------------------------------------
/layout/page.njk:
--------------------------------------------------------------------------------
1 | {% extends '_layout.njk' %}
2 | {% import '_macro/sidebar.njk' as sidebar_template with context %}
3 |
4 | {% block title %}
5 | {%- set page_title_suffix = ' | ' + title %}
6 |
7 | {%- if page.type === 'categories' and not page.title %}
8 | {{- __('title.category') + page_title_suffix }}
9 | {%- elif page.type === 'tags' and not page.title %}
10 | {{- __('title.tag') + page_title_suffix }}
11 | {%- elif page.type === 'schedule' and not page.title %}
12 | {{- __('title.schedule') + page_title_suffix }}
13 | {%- else %}
14 | {{- page.title + page_title_suffix }}
15 | {%- endif %}
16 | {% endblock %}
17 |
18 | {% block class %}page posts-expand{% endblock %}
19 |
20 | {% block content %}
21 |
22 | {##################}
23 | {### PAGE BLOCK ###}
24 | {##################}
25 |
26 | {%- if page.header !== false %}
27 | {%- include '_partials/page/page-header.njk' -%}
28 | {%- endif %}
29 | {#################}
30 | {### PAGE BODY ###}
31 | {#################}
32 |
33 | {%- if page.type === 'tags' %}
34 | {%- include '_partials/page/tags.njk' -%}
35 | {% elif page.type === 'categories' %}
36 | {%- include '_partials/page/categories.njk' -%}
37 | {% elif page.type === 'schedule' %}
38 | {%- include '_partials/page/schedule.njk' -%}
39 | {% else %}
40 | {{ page.content }}
41 | {%- endif %}
42 |
43 | {#####################}
44 | {### END PAGE BODY ###}
45 | {#####################}
46 |
47 | {%- include '_partials/page/breadcrumb.njk' -%}
48 | {######################}
49 | {### END PAGE BLOCK ###}
50 | {######################}
51 |
52 | {% endblock %}
53 |
54 | {% block sidebar %}
55 | {{ sidebar_template.render(page.toc.enable) }}
56 | {% endblock %}
57 |
--------------------------------------------------------------------------------
/layout/post.njk:
--------------------------------------------------------------------------------
1 | {% extends '_layout.njk' %}
2 | {% import '_macro/sidebar.njk' as sidebar_template with context %}
3 |
4 | {% block title %}{{ page.title }} | {{ title }}{% endblock %}
5 |
6 | {% block class %}post posts-expand{% endblock %}
7 |
8 | {% block content %}
9 |
10 | {{ partial('_macro/post.njk', {post: page}) }}
11 |
12 | {% endblock %}
13 |
14 | {% block sidebar %}
15 | {{ sidebar_template.render(page.toc.enable) }}
16 | {% endblock %}
17 |
--------------------------------------------------------------------------------
/layout/tag.njk:
--------------------------------------------------------------------------------
1 | {% extends '_layout.njk' %}
2 | {% import '_macro/post-collapse.njk' as post_template with context %}
3 | {% import '_macro/sidebar.njk' as sidebar_template with context %}
4 |
5 | {% block title %}{{ __('title.tag') }}: {{ page.tag }} | {{ title }}{% endblock %}
6 |
7 | {% block class %}tag posts-collapse{% endblock %}
8 |
9 | {% block content %}
10 |
11 | {#################}
12 | {### TAG BLOCK ###}
13 | {#################}
14 |
15 |
16 |
17 |
21 |
22 |
23 | {{ post_template.render(page.posts) }}
24 |
25 |
26 | {#####################}
27 | {### END TAG BLOCK ###}
28 | {#####################}
29 |
30 | {%- include '_partials/pagination.njk' -%}
31 |
32 | {% endblock %}
33 |
34 | {% block sidebar %}
35 | {{ sidebar_template.render(false) }}
36 | {% endblock %}
37 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hexo-theme-next",
3 | "version": "8.23.0",
4 | "description": "Elegant and powerful theme for Hexo.",
5 | "main": "package.json",
6 | "files": [
7 | "docs",
8 | "languages",
9 | "layout",
10 | "scripts",
11 | "source",
12 | "_config.yml",
13 | "_vendors.yml"
14 | ],
15 | "scripts": {
16 | "eslint": "eslint scripts/ source/js test/",
17 | "prepare": "node .githooks/install.js",
18 | "stylint": "stylelint source/css/ --ip source/css/_common/scaffolding/highlight/index.styl",
19 | "test": "mocha test/index.js",
20 | "test-cov": "c8 npm test"
21 | },
22 | "repository": {
23 | "type": "git",
24 | "url": "git+https://github.com/next-theme/hexo-theme-next.git"
25 | },
26 | "keywords": [
27 | "hexo",
28 | "theme",
29 | "next"
30 | ],
31 | "author": "NexT (https://theme-next.js.org)",
32 | "license": "AGPL-3.0-only",
33 | "bugs": {
34 | "url": "https://github.com/next-theme/hexo-theme-next/issues"
35 | },
36 | "homepage": "https://theme-next.js.org",
37 | "devDependencies": {
38 | "@next-theme/eslint-config": "0.0.4",
39 | "c8": "10.1.3",
40 | "chai": "5.2.0",
41 | "eslint": "9.23.0",
42 | "hexo": "7.3.0",
43 | "hexo-renderer-marked": "7.0.1",
44 | "js-yaml": "4.1.0",
45 | "mocha": "11.1.0",
46 | "stylelint": "16.17.0",
47 | "stylelint-stylus": "1.0.0"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/scripts/events/index.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict';
4 |
5 | hexo.extend.filter.register('before_generate', () => {
6 | // Merge config
7 | require('./lib/config')(hexo);
8 | // Set vendors
9 | require('./lib/vendors')(hexo);
10 | // Add filter type `theme_inject`
11 | require('./lib/injects')(hexo);
12 | // Highlight
13 | require('./lib/highlight')(hexo);
14 | // Menu and sub menu
15 | require('./lib/navigation')(hexo);
16 | }, 0);
17 |
18 | hexo.on('ready', () => {
19 | if (!/^(g|s)/.test(hexo.env.cmd) || process.argv.includes('--next-disable-banner')) return;
20 | const { version } = require('../../package.json');
21 | hexo.log.info(`==================================
22 | ███╗ ██╗███████╗██╗ ██╗████████╗
23 | ████╗ ██║██╔════╝╚██╗██╔╝╚══██╔══╝
24 | ██╔██╗ ██║█████╗ ╚███╔╝ ██║
25 | ██║╚██╗██║██╔══╝ ██╔██╗ ██║
26 | ██║ ╚████║███████╗██╔╝ ██╗ ██║
27 | ╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝ ╚═╝
28 | ========================================
29 | NexT version ${version}
30 | Documentation: https://theme-next.js.org
31 | ========================================`);
32 | });
33 |
--------------------------------------------------------------------------------
/scripts/events/lib/config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { deepMerge } = require('hexo-util');
4 |
5 | module.exports = hexo => {
6 | const data = hexo.locals.get('data');
7 |
8 | if (data.next) {
9 | hexo.log.warn('`next.yml` is deprecated. Please upgrade to Hexo 5 or later and use `_config.next.yml` instead.');
10 | hexo.log.warn('Documentation: https://theme-next.js.org/docs/getting-started/configuration.html');
11 | }
12 |
13 | const { cache, language_switcher } = hexo.theme.config;
14 | const warning = function(...args) {
15 | hexo.log.warn(`Since ${args[0]} is turned on, the ${args[1]} is disabled to avoid potential hazards.`);
16 | };
17 |
18 | if (cache?.enable && language_switcher) {
19 | warning('language_switcher', 'caching');
20 | cache.enable = false;
21 | }
22 | if (cache?.enable && hexo.config.relative_link) {
23 | warning('caching', '`relative_link` option in Hexo `_config.yml`');
24 | hexo.config.relative_link = false;
25 | }
26 |
27 | // Custom languages support. Introduced in NexT v6.3.0.
28 | if (data.languages) {
29 | const { language } = hexo.config;
30 | const { i18n } = hexo.theme;
31 |
32 | const mergeLang = lang => {
33 | if (data.languages[lang]) i18n.set(lang, deepMerge(i18n.get([lang]), data.languages[lang]));
34 | };
35 |
36 | if (Array.isArray(language)) {
37 | for (const lang of language) {
38 | mergeLang(lang);
39 | }
40 | } else {
41 | mergeLang(language);
42 | }
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/scripts/events/lib/highlight.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const { resolve, highlightTheme } = require('./utils');
5 |
6 | function prismTheme(name) {
7 | let file = resolve('prismjs', `themes/${name}.css`);
8 | if (!fs.existsSync(file)) file = resolve('prism-themes', `themes/${name}.css`);
9 | return file;
10 | }
11 |
12 | module.exports = hexo => {
13 | const { config } = hexo;
14 | const theme = hexo.theme.config;
15 | config.highlight.hljs = false;
16 | theme.highlight = {
17 | enable: config.syntax_highlighter === 'highlight.js' || config.highlight.enable,
18 | light : highlightTheme(theme.codeblock.theme.light),
19 | dark : highlightTheme(theme.codeblock.theme.dark)
20 | };
21 | theme.prism = {
22 | enable: config.syntax_highlighter === 'prismjs' || config.prismjs.enable,
23 | light : prismTheme(theme.codeblock.prism.light),
24 | dark : prismTheme(theme.codeblock.prism.dark),
25 | number: resolve('prismjs', 'plugins/line-numbers/prism-line-numbers.css')
26 | };
27 | };
28 |
--------------------------------------------------------------------------------
/scripts/events/lib/navigation.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { join } = require('path').posix;
4 |
5 | class TreeNode {
6 | constructor(parent, path, name, icon) {
7 | if (parent && !path.startsWith('http')) {
8 | path = join(parent.path, path);
9 | }
10 | this.parent = parent;
11 | this.children = [];
12 | this.path = path;
13 | this.name = name;
14 | this.icon = icon;
15 | }
16 |
17 | append(child) {
18 | this.children.push(child);
19 | }
20 | }
21 |
22 | module.exports = hexo => {
23 | const menu_map = new Map();
24 | const main_menu = [];
25 | hexo.theme.config.menu_map = menu_map;
26 | hexo.theme.config.main_menu = main_menu;
27 |
28 | function parse(menu, parent) {
29 | if (!menu) return;
30 | Object.entries(menu).forEach(([name, value]) => {
31 | if (name.toLowerCase() === 'default') return;
32 | let node;
33 | if (typeof value === 'string') {
34 | const [path, icon] = value.split('||').map(v => v.trim());
35 | node = new TreeNode(parent, path, name, icon);
36 | } else if (typeof value === 'object') {
37 | if (typeof value.default !== 'string') {
38 | hexo.log.warn('Missing default entry for menu item:', name);
39 | return;
40 | }
41 | const [path, icon] = value.default.split('||').map(v => v.trim());
42 | node = new TreeNode(parent, path, name, icon);
43 | parse(value, node);
44 | }
45 | if (node) {
46 | menu_map.set(node.path, node);
47 | if (parent) {
48 | parent.append(node);
49 | } else {
50 | main_menu.push(node);
51 | }
52 | }
53 | });
54 | }
55 |
56 | parse(hexo.theme.config.menu);
57 | };
58 |
--------------------------------------------------------------------------------
/scripts/events/lib/vendors.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const path = require('path');
5 | const yaml = require('js-yaml');
6 | const { url_for } = require('hexo-util');
7 | const { getVendors } = require('./utils');
8 |
9 | let internal;
10 | try {
11 | internal = require('@next-theme/plugins');
12 | } catch {
13 | }
14 | const vendorsFile = fs.readFileSync(path.join(__dirname, '../../../_vendors.yml'));
15 | const dependencies = yaml.load(vendorsFile);
16 |
17 | module.exports = hexo => {
18 | const { vendors, creative_commons, pace } = hexo.theme.config;
19 | if (typeof internal === 'function') {
20 | internal(hexo, dependencies);
21 | }
22 | let { plugins = 'cdnjs' } = vendors;
23 | if (plugins === 'local' && typeof internal === 'undefined') {
24 | hexo.log.warn('Dependencies for `plugins: local` not found. The default CDN provider CDNJS is used instead.');
25 | hexo.log.warn('Run `npm install @next-theme/plugins` in Hexo site root directory to install the plugin.');
26 | plugins = 'cdnjs';
27 | }
28 | for (const [key, value] of Object.entries(dependencies)) {
29 | // This script will be executed repeatedly when Hexo listens file changes
30 | // But the variable vendors[key] only needs to be modified once
31 | if (vendors[key] && typeof vendors[key] === 'string') {
32 | vendors[key] = {
33 | url: url_for.call(hexo, vendors[key])
34 | };
35 | continue;
36 | }
37 | if (key === 'creative_commons') {
38 | value.file = `${value.dir}/${creative_commons.size}/${creative_commons.license.replace(/-/g, '_')}.svg`;
39 | }
40 | if (key === 'pace_css') {
41 | value.file = `${value.dir}/${pace.color}/pace-theme-${pace.theme}.css`;
42 | }
43 | const { name, file } = value;
44 | const links = getVendors({
45 | ...value,
46 | minified: file,
47 | local : url_for.call(hexo, `lib/${name}/${file}`),
48 | custom : vendors.custom_cdn_url
49 | });
50 | vendors[key] = {
51 | url : links[plugins] || links.cdnjs,
52 | integrity: value.integrity
53 | };
54 | }
55 | };
56 |
--------------------------------------------------------------------------------
/scripts/filters/comment/changyan.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict';
4 |
5 | const path = require('path');
6 | const { iconText } = require('./common');
7 |
8 | // Add comment
9 | hexo.extend.filter.register('theme_inject', injects => {
10 | const config = hexo.theme.config.changyan;
11 | if (!config.enable || !config.appid || !config.appkey) return;
12 |
13 | injects.comment.raw('changyan', '', {}, {});
14 |
15 | injects.bodyEnd.file('changyan', path.join(hexo.theme_dir, 'layout/_third-party/comments/changyan.njk'));
16 |
17 | });
18 |
19 | // Add post_meta
20 | hexo.extend.filter.register('theme_inject', injects => {
21 | const config = hexo.theme.config.changyan;
22 | if (!config.enable || !config.count || !config.appid || !config.appkey) return;
23 |
24 | injects.postMeta.raw('changyan', `
25 | {% if post.comments %}
26 |
27 | ${iconText('far fa-comment', 'changyan')}
28 |
29 |
30 |
31 |
32 | {% endif %}
33 | `, {}, {});
34 |
35 | });
36 |
--------------------------------------------------------------------------------
/scripts/filters/comment/common.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function capitalize(input) {
4 | return input.toString().charAt(0).toUpperCase() + input.toString().substring(1);
5 | }
6 |
7 | module.exports = {
8 | iconText(icon, key, defaultValue) {
9 | if (!defaultValue) {
10 | defaultValue = capitalize(key);
11 | }
12 | return `
13 |
14 |
15 |
16 | {%- set post_meta_comment = __('post.comments.${key}') %}
17 | {%- if post_meta_comment == 'post.comments.${key}' %}
18 | {%- set post_meta_comment = '${defaultValue}' %}
19 | {%- endif %}
20 | {{ post_meta_comment + __('symbol.colon') }}
21 | `;
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/scripts/filters/comment/default-config.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict';
4 |
5 | const path = require('path');
6 |
7 | hexo.extend.filter.register('theme_inject', injects => {
8 | injects.comment.raws.forEach(element => {
9 | // Set default button content
10 | const injectName = path.basename(element.name, path.extname(element.name));
11 | element.args[0] = Object.assign({
12 | configKey: injectName,
13 | class : injectName,
14 | button : injectName
15 | }, element.args[0]);
16 | // Get locals and config
17 | const locals = element.args[0];
18 | const config = hexo.theme.config.comments;
19 | // Set activeClass
20 | if (config.active === locals.configKey) {
21 | config.activeClass = locals.class;
22 | }
23 | // Set custom button content
24 | if (config.nav) {
25 | const nav = config.nav[locals.configKey] || {};
26 | if (nav.order) {
27 | element.args[2] = nav.order;
28 | }
29 | if (nav.text) {
30 | locals.button = nav.text;
31 | }
32 | }
33 | });
34 | }, 99999);
35 |
--------------------------------------------------------------------------------
/scripts/filters/comment/disqus.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict';
4 |
5 | const path = require('path');
6 | const { iconText } = require('./common');
7 |
8 | // Add comment
9 | hexo.extend.filter.register('theme_inject', injects => {
10 | const config = hexo.theme.config.disqus;
11 | if (!config.enable || !config.shortname) return;
12 |
13 | injects.comment.raw('disqus', `
14 |
17 | `, {}, { cache: true });
18 |
19 | injects.bodyEnd.file('disqus', path.join(hexo.theme_dir, 'layout/_third-party/comments/disqus.njk'));
20 |
21 | });
22 |
23 | // Add post_meta
24 | hexo.extend.filter.register('theme_inject', injects => {
25 | const config = hexo.theme.config.disqus;
26 | if (!config.enable || !config.shortname || !config.count) return;
27 |
28 | injects.postMeta.raw('disqus', `
29 | {% if post.comments %}
30 |
31 | ${iconText('far fa-comment', 'disqus')}
32 |
33 |
34 |
35 |
36 | {% endif %}
37 | `, {}, {});
38 |
39 | });
40 |
--------------------------------------------------------------------------------
/scripts/filters/comment/disqusjs.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict';
4 |
5 | const path = require('path');
6 |
7 | // Add comment
8 | hexo.extend.filter.register('theme_inject', injects => {
9 | const config = hexo.theme.config.disqusjs;
10 | if (!config.enable || !config.shortname || !config.apikey) return;
11 |
12 | injects.comment.raw('disqusjs', `
13 |
16 | `, {}, { cache: true });
17 |
18 | injects.bodyEnd.file('disqusjs', path.join(hexo.theme_dir, 'layout/_third-party/comments/disqusjs.njk'));
19 |
20 | });
21 |
--------------------------------------------------------------------------------
/scripts/filters/comment/gitalk.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict';
4 |
5 | const path = require('path');
6 |
7 | // Add comment
8 | hexo.extend.filter.register('theme_inject', injects => {
9 | const theme = hexo.theme.config;
10 | if (!theme.gitalk.enable) return;
11 |
12 | injects.comment.raw('gitalk', '', {}, { cache: true });
13 |
14 | injects.bodyEnd.file('gitalk', path.join(hexo.theme_dir, 'layout/_third-party/comments/gitalk.njk'));
15 |
16 | });
17 |
--------------------------------------------------------------------------------
/scripts/filters/comment/isso.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict';
4 |
5 | const path = require('path');
6 |
7 | // Add comment
8 | hexo.extend.filter.register('theme_inject', injects => {
9 | const theme = hexo.theme.config;
10 | if (!theme.isso) return;
11 |
12 | injects.comment.raw('isso', '', {}, { cache: true });
13 |
14 | injects.bodyEnd.file('isso', path.join(hexo.theme_dir, 'layout/_third-party/comments/isso.njk'));
15 |
16 | });
17 |
--------------------------------------------------------------------------------
/scripts/filters/comment/livere.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict';
4 |
5 | const path = require('path');
6 |
7 | // Add comment
8 | hexo.extend.filter.register('theme_inject', injects => {
9 | const theme = hexo.theme.config;
10 | if (!theme.livere_uid) return;
11 |
12 | injects.comment.raw('livere', '', {}, { cache: true });
13 |
14 | injects.bodyEnd.file('livere', path.join(hexo.theme_dir, 'layout/_third-party/comments/livere.njk'));
15 |
16 | });
17 |
--------------------------------------------------------------------------------
/scripts/filters/comment/utterances.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict';
4 |
5 | const path = require('path');
6 |
7 | // Add comment
8 | hexo.extend.filter.register('theme_inject', injects => {
9 | const config = hexo.theme.config.utterances;
10 | if (!config.enable) return;
11 |
12 | if (!config.repo) {
13 | hexo.log.warn('utterances.repo can\'t be null.');
14 | return;
15 | }
16 |
17 | injects.comment.raw('utterances', '', {}, { cache: true });
18 |
19 | injects.bodyEnd.file('utterances', path.join(hexo.theme_dir, 'layout/_third-party/comments/utterances.njk'));
20 |
21 | });
22 |
--------------------------------------------------------------------------------
/scripts/filters/default-injects.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict';
4 |
5 | const { points } = require('../events/lib/utils');
6 |
7 | hexo.extend.filter.register('theme_inject', injects => {
8 | const filePath = hexo.theme.config.custom_file_path;
9 |
10 | if (!filePath) return;
11 |
12 | points.views.forEach(key => {
13 | if (filePath[key]) {
14 | injects[key].file('custom', filePath[key]);
15 | }
16 | });
17 |
18 | points.styles.forEach(key => {
19 | if (filePath[key]) {
20 | injects[key].push(filePath[key]);
21 | }
22 | });
23 |
24 | }, 99);
25 |
--------------------------------------------------------------------------------
/scripts/filters/locals.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict';
4 |
5 | const keys = ['toc', 'reward_settings', 'quicklink'];
6 |
7 | hexo.extend.filter.register('template_locals', locals => {
8 | const { config } = hexo;
9 | const { __, theme, page } = locals;
10 | const { i18n } = hexo.theme;
11 | // Hexo & NexT version
12 | locals.next_version = require('../../package.json').version;
13 | // Language & Config
14 | locals.title = __('title') !== 'title' ? __('title') : config.title;
15 | locals.subtitle = __('subtitle') !== 'subtitle' ? __('subtitle') : config.subtitle;
16 | locals.author = __('author') !== 'author' ? __('author') : config.author;
17 | locals.description = __('description') !== 'description' ? __('description') : config.description;
18 | locals.languages = [...i18n.languages];
19 | locals.languages.splice(locals.languages.indexOf('default'), 1);
20 | // See https://github.com/hexojs/hexo/pull/4614
21 | page.lang = page.lang || page.language;
22 | // Creative Commons
23 | locals.ccURL = 'https://creativecommons.org/' + (theme.creative_commons.license === 'cc-zero' ? 'publicdomain/zero/1.0/' : 'licenses/' + theme.creative_commons.license + '/4.0/') + (theme.creative_commons.language || '');
24 | // PJAX
25 | locals.pjax = theme.pjax ? ' data-pjax' : '';
26 | // Front-matter
27 | keys.forEach(key => {
28 | page[key] = { ...theme[key], ...page[key] };
29 | });
30 | // Set home or archive quicklink
31 | if (page.__index) {
32 | page.quicklink.enable = theme.quicklink.home;
33 | }
34 | if (page.archive) {
35 | page.quicklink.enable = theme.quicklink.archive;
36 | }
37 | });
38 |
--------------------------------------------------------------------------------
/scripts/filters/post.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict';
4 |
5 | const { parse } = require('url');
6 | const { unescapeHTML } = require('hexo-util');
7 |
8 | hexo.extend.filter.register('after_post_render', data => {
9 | const { config } = hexo;
10 | const theme = hexo.theme.config;
11 | if (!theme.exturl && !theme.lazyload) return;
12 | if (theme.lazyload) {
13 | data.content = data.content.replace(/( ]*)\ssrc=/ig, '$1 data-src=');
14 | }
15 | if (theme.exturl) {
16 | const siteHost = parse(config.url).hostname || config.url;
17 | // External URL icon
18 | const exturlIcon = theme.exturl_icon ? ' ' : '';
19 | data.content = data.content.replace(/]*\shref="([^"]+)"[^>]*>([^<]+)<\/a>/ig, (match, href, html) => {
20 | // Exit if the href attribute doesn't exist.
21 | if (!href) return match;
22 |
23 | // Exit if the url has same host with `config.url`, which means it's an internal link.
24 | const link = parse(href);
25 | if (!link.protocol || link.hostname === siteHost) return match;
26 |
27 | // Return encrypted URL with title.
28 | const title = match.match(/title="([^"]+)"/);
29 | const encoded = Buffer.from(unescapeHTML(href)).toString('base64');
30 | if (title) return `${html}${exturlIcon} `;
31 |
32 | return `${html}${exturlIcon} `;
33 | });
34 | }
35 |
36 | }, 0);
37 |
--------------------------------------------------------------------------------
/scripts/helpers/font.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // https://developers.google.com/fonts/docs/getting_started
4 | module.exports = function() {
5 | const config = this.theme.font;
6 |
7 | if (!config || !config.enable) return '';
8 |
9 | const fontStyles = ':ital,wght@0,300;0,400;0,700;1,300;1,400;1,700';
10 | const fontHost = config.host || 'https://fonts.googleapis.com';
11 |
12 | // Get a font list from config
13 | let fontFamilies = [];
14 | ['global', 'title', 'headings', 'posts', 'codes'].forEach(item => {
15 | if (config[item]?.family && config[item].external) {
16 | fontFamilies = fontFamilies.concat(config[item].family.split(','));
17 | }
18 | });
19 |
20 | fontFamilies = fontFamilies.map(name => name.trim().replace(/\s/g, '+') + fontStyles);
21 | fontFamilies = [...new Set(fontFamilies)].map(name => 'family=' + name).join('&');
22 |
23 | // Merge extra parameters to the final processed font string
24 | return fontFamilies ? ` ` : '';
25 | };
26 |
--------------------------------------------------------------------------------
/scripts/helpers/navigation.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict';
4 |
5 | hexo.extend.helper.register('next_menu', function(path) {
6 | path = ('/' + path).replace(/index\.html$/, '');
7 | const { menu_map } = this.theme;
8 | if (!menu_map.has(path)) return;
9 | let node = menu_map.get(path);
10 | const menus = [];
11 | if (node.children.length) {
12 | menus.unshift(node.children);
13 | }
14 | while (node.parent) {
15 | menus.unshift(node.parent.children);
16 | node = node.parent;
17 | }
18 | return menus;
19 | });
20 |
--------------------------------------------------------------------------------
/scripts/helpers/next-config.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict';
4 |
5 | const { parse } = require('url');
6 |
7 | /**
8 | * Export theme config
9 | */
10 | hexo.extend.helper.register('next_config', function() {
11 | const { config, theme, url_for, __ } = this;
12 | const exportConfig = {
13 | hostname : parse(config.url).hostname || config.url,
14 | root : config.root,
15 | images : url_for(theme.images),
16 | scheme : theme.scheme,
17 | darkmode : theme.darkmode,
18 | version : this.next_version,
19 | exturl : theme.exturl,
20 | sidebar : theme.sidebar,
21 | hljswrap : theme.highlight.enable && config.highlight.wrap,
22 | codeblock : theme.codeblock,
23 | bookmark : theme.bookmark,
24 | mediumzoom: theme.mediumzoom,
25 | lazyload : theme.lazyload,
26 | pangu : theme.pangu,
27 | comments : theme.comments,
28 | stickytabs: theme.tabs.sticky,
29 | motion : theme.motion,
30 | prism : theme.prism.enable && !config.prismjs.preprocess,
31 | i18n : {
32 | placeholder: __('search.placeholder'),
33 | empty : __('search.empty', '${query}'),
34 | hits_time : __('search.hits_time', '${hits}', '${time}'),
35 | hits : __('search.hits', '${hits}')
36 | }
37 | };
38 | if (config.algolia && theme.algolia_search?.enable) {
39 | exportConfig.algolia = {
40 | appID : config.algolia.applicationID || config.algolia.appId,
41 | apiKey : config.algolia.apiKey,
42 | indexName: config.algolia.indexName,
43 | hits : theme.algolia_search.hits
44 | };
45 | }
46 | if (config.search && theme.local_search?.enable) {
47 | exportConfig.path = url_for(config.search.path);
48 | exportConfig.localsearch = theme.local_search;
49 | }
50 | return exportConfig;
51 | });
52 |
53 | hexo.extend.helper.register('next_config_unique', function() {
54 | const { page, is_home, is_post } = this;
55 | return {
56 | sidebar : page.sidebar || '',
57 | isHome : is_home(),
58 | isPost : is_post(),
59 | lang : page.lang,
60 | comments : page.comments || '',
61 | permalink: page.permalink || '',
62 | path : page.path || '',
63 | title : page.title || ''
64 | };
65 | });
66 |
--------------------------------------------------------------------------------
/scripts/helpers/next-paginator.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict';
4 |
5 | hexo.extend.helper.register('next_paginator', function() {
6 | const prev = this.__('accessibility.prev_page');
7 | const next = this.__('accessibility.next_page');
8 | let paginator = this.paginator({
9 | prev_text: ' ',
10 | next_text: ' ',
11 | mid_size : 1,
12 | escape : false
13 | });
14 | paginator = paginator
15 | .replace('rel="prev"', `rel="prev" title="${prev}" aria-label="${prev}"`)
16 | .replace('rel="next"', `rel="next" title="${next}" aria-label="${next}"`);
17 | return paginator;
18 | });
19 |
--------------------------------------------------------------------------------
/scripts/helpers/next-url.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { htmlTag } = require('hexo-util');
4 | const { parse } = require('url');
5 |
6 | module.exports = function(path, text, options = {}, decode = false) {
7 | const { config, theme } = this;
8 | const data = parse(path);
9 | const siteHost = parse(config.url).hostname || config.url;
10 |
11 | let exturl = '';
12 | let tag = 'a';
13 | let attrs = { href: this.url_for(path) };
14 |
15 | // If `exturl` enabled, set spanned links only on external links.
16 | if (theme.exturl && data.protocol && data.hostname !== siteHost) {
17 | tag = 'span';
18 | exturl = 'exturl';
19 | const encoded = Buffer.from(path).toString('base64');
20 | attrs = {
21 | class : exturl,
22 | 'data-url': encoded
23 | };
24 | }
25 |
26 | for (const key in options) {
27 |
28 | /**
29 | * If option have `class` attribute, add it to
30 | * 'exturl' class if `exturl` option enabled.
31 | */
32 | if (exturl !== '' && key === 'class') {
33 | attrs[key] += ' ' + options[key];
34 | } else {
35 | attrs[key] = options[key];
36 | }
37 | }
38 |
39 | // If it's external link, rewrite attributes.
40 | if (data.protocol && data.hostname !== siteHost) {
41 | attrs.external = null;
42 |
43 | if (!theme.exturl) {
44 | // Only for simple link need to rewrite/add attributes.
45 | attrs.rel = attrs.rel || 'noopener';
46 | attrs.target = '_blank';
47 | } else {
48 | // Remove rel attributes for `exturl` in main menu.
49 | attrs.rel = null;
50 | }
51 | }
52 |
53 | return htmlTag(tag, attrs, decode ? decodeURI(text) : text, false);
54 | };
55 |
--------------------------------------------------------------------------------
/scripts/helpers/next-vendors.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict';
4 |
5 | hexo.extend.helper.register('js_vendors', function() {
6 | const { config, theme } = this;
7 | const vendors = ['anime'];
8 | if (theme.prism.enable && !config.prismjs.preprocess) {
9 | vendors.push('prism', 'prism_autoloader');
10 | if (config.prismjs.line_number) {
11 | vendors.push('prism_line_numbers');
12 | }
13 | }
14 | if (theme.pjax) {
15 | vendors.push('pjax');
16 | }
17 | if (theme.fancybox) {
18 | vendors.push('fancybox_js');
19 | }
20 | if (theme.mediumzoom) {
21 | vendors.push('mediumzoom');
22 | }
23 | if (theme.lazyload) {
24 | vendors.push('lazyload');
25 | }
26 | if (theme.pangu) {
27 | vendors.push('pangu');
28 | }
29 | return vendors;
30 | });
31 |
--------------------------------------------------------------------------------
/scripts/tags/button.js:
--------------------------------------------------------------------------------
1 | /**
2 | * button.js | https://theme-next.js.org/docs/tag-plugins/button
3 | */
4 |
5 | 'use strict';
6 |
7 | module.exports = ctx => function(args) {
8 | args = args.join(' ').split(',');
9 | const url = args[0];
10 | const text = (args[1] || '').trim();
11 | let icon = (args[2] || '').trim();
12 | const title = (args[3] || '').trim();
13 |
14 | if (!url) {
15 | ctx.log.warn('URL can NOT be empty.');
16 | }
17 | if (icon.length > 0) {
18 | if (!icon.startsWith('fa')) icon = 'fa fa-' + icon;
19 | icon = ` `;
20 | }
21 |
22 | return ` 0 ? ` title="${title}"` : ''}>${icon}${text} `;
23 | };
24 |
--------------------------------------------------------------------------------
/scripts/tags/caniuse.js:
--------------------------------------------------------------------------------
1 | /**
2 | * caniuse.js | https://theme-next.js.org/docs/tag-plugins/caniuse
3 | */
4 |
5 | 'use strict';
6 |
7 | module.exports = ctx => function(args) {
8 | const [feature, periods = 'current'] = args.join('').split('@');
9 |
10 | if (!feature) {
11 | ctx.log.warn('Caniuse feature can NOT be empty.');
12 | return '';
13 | }
14 |
15 | return ``;
16 | };
17 |
--------------------------------------------------------------------------------
/scripts/tags/center-quote.js:
--------------------------------------------------------------------------------
1 | /**
2 | * center-quote.js | https://theme-next.js.org/docs/tag-plugins/
3 | */
4 |
5 | 'use strict';
6 |
7 | module.exports = ctx => function(args, content) {
8 | return `
9 | ${ctx.render.renderSync({ text: content, engine: 'markdown' })}
10 | `;
11 | };
12 |
--------------------------------------------------------------------------------
/scripts/tags/index.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict';
4 |
5 | const postButton = require('./button')(hexo);
6 |
7 | hexo.extend.tag.register('button', postButton);
8 | hexo.extend.tag.register('btn', postButton);
9 |
10 | const caniUse = require('./caniuse')(hexo);
11 |
12 | hexo.extend.tag.register('caniuse', caniUse);
13 | hexo.extend.tag.register('can', caniUse);
14 |
15 | const centerQuote = require('./center-quote')(hexo);
16 |
17 | hexo.extend.tag.register('centerquote', centerQuote, true);
18 | hexo.extend.tag.register('cq', centerQuote, true);
19 |
20 | const groupPicture = require('./group-pictures')(hexo);
21 |
22 | hexo.extend.tag.register('grouppicture', groupPicture, true);
23 | hexo.extend.tag.register('gp', groupPicture, true);
24 |
25 | const postLabel = require('./label')(hexo);
26 |
27 | hexo.extend.tag.register('label', postLabel);
28 |
29 | const linkGrid = require('./link-grid');
30 |
31 | hexo.extend.tag.register('linkgrid', linkGrid, true);
32 | hexo.extend.tag.register('lg', linkGrid, true);
33 |
34 | const mermaid = require('./mermaid');
35 |
36 | hexo.extend.tag.register('mermaid', mermaid, true);
37 |
38 | const wavedrom = require('./wavedrom');
39 |
40 | hexo.extend.tag.register('wavedrom', wavedrom, true);
41 |
42 | const postNote = require('./note')(hexo);
43 |
44 | hexo.extend.tag.register('note', postNote, true);
45 | hexo.extend.tag.register('subnote', postNote, true);
46 |
47 | const pdf = require('./pdf')(hexo);
48 |
49 | hexo.extend.tag.register('pdf', pdf);
50 |
51 | const postTabs = require('./tabs')(hexo);
52 |
53 | hexo.extend.tag.register('tabs', postTabs, true);
54 | hexo.extend.tag.register('subtabs', postTabs, true);
55 | hexo.extend.tag.register('subsubtabs', postTabs, true);
56 |
57 | const postVideo = require('./video');
58 |
59 | hexo.extend.tag.register('video', postVideo);
60 |
--------------------------------------------------------------------------------
/scripts/tags/label.js:
--------------------------------------------------------------------------------
1 | /**
2 | * label.js | https://theme-next.js.org/docs/tag-plugins/label
3 | */
4 |
5 | 'use strict';
6 |
7 | module.exports = ctx => function(args) {
8 | const [classes = 'default', text = ''] = args.join(' ').split('@');
9 |
10 | if (!text) ctx.log.warn('Label text must be defined!');
11 |
12 | return `${text} `;
13 | };
14 |
--------------------------------------------------------------------------------
/scripts/tags/link-grid.js:
--------------------------------------------------------------------------------
1 | /**
2 | * link-grid.js | https://theme-next.js.org/docs/tag-plugins/link-grid
3 | */
4 |
5 | 'use strict';
6 |
7 | module.exports = function([image = '/images/avatar.gif', delimiter = '|', comment = '%'], content) {
8 | const links = content.split('\n').filter(line => line.trim() !== '').map(line => {
9 | const item = line.split(delimiter).map(arg => arg.trim());
10 | if (item[0][0] === comment) return '';
11 | const imageSource = item[3] || image;
12 | const hasExtension = /\.[^/]+$/.test(imageSource);
13 | return `
14 |
15 |
${item[0]}
${item[2] || item[1]}
16 |
17 |
`;
18 | });
19 | return `${links.join('')}
`;
20 | };
21 |
--------------------------------------------------------------------------------
/scripts/tags/mermaid.js:
--------------------------------------------------------------------------------
1 | /**
2 | * mermaid.js | https://theme-next.js.org/docs/tag-plugins/mermaid
3 | */
4 |
5 | 'use strict';
6 |
7 | const { escapeHTML } = require('hexo-util');
8 |
9 | module.exports = function(args, content) {
10 | // Support mermaid inside backtick code block
11 | // Keep the same HTML structure
12 | // Fix issue #347 #797
13 | return `
14 |
15 | ${args.join(' ')}
16 | ${escapeHTML(content)}
17 |
18 | `;
19 | };
20 |
--------------------------------------------------------------------------------
/scripts/tags/note.js:
--------------------------------------------------------------------------------
1 | /**
2 | * note.js | https://theme-next.js.org/docs/tag-plugins/note
3 | */
4 |
5 | 'use strict';
6 |
7 | module.exports = ctx => function(args, content) {
8 | const keywords = ['default', 'primary', 'info', 'success', 'warning', 'danger', 'no-icon'];
9 | const className = [];
10 | for (let i = 0; i < 2; i++) {
11 | if (keywords.includes(args[0])) {
12 | className.push(args.shift());
13 | } else {
14 | break;
15 | }
16 | }
17 |
18 | content = ctx.render.renderSync({ text: content, engine: 'markdown' });
19 | if (args.length === 0) {
20 | return `${content}
`;
21 | }
22 | return `${ctx.render.renderSync({ text: args.join(' '), engine: 'markdown' })}
23 | ${content}
24 | `;
25 | };
26 |
--------------------------------------------------------------------------------
/scripts/tags/pdf.js:
--------------------------------------------------------------------------------
1 | /**
2 | * pdf.js | https://theme-next.js.org/docs/tag-plugins/pdf
3 | */
4 |
5 | 'use strict';
6 |
7 | module.exports = ctx => function(args) {
8 | const theme = ctx.theme.config;
9 | return `
`;
10 | };
11 |
--------------------------------------------------------------------------------
/scripts/tags/tabs.js:
--------------------------------------------------------------------------------
1 | /**
2 | * tabs.js | https://theme-next.js.org/docs/tag-plugins/tabs
3 | */
4 |
5 | 'use strict';
6 |
7 | module.exports = ctx => function(args, content = '') {
8 | const tabBlock = /\n([\w\W\s\S]*?)/g;
9 |
10 | args = args.join(' ').split(',');
11 | const tabName = args[0];
12 | const tabActive = Number(args[1]) || 0;
13 |
14 | let tabId = 0;
15 | let tabNav = '';
16 | let tabContent = '';
17 |
18 | if (!tabName) ctx.log.warn('Tabs block must have unique name!');
19 | const matches = content.matchAll(tabBlock);
20 |
21 | for (const match of matches) {
22 | let [caption = '', icon = ''] = match[1].split('@');
23 | let postContent = match[2];
24 |
25 | postContent = ctx.render.renderSync({ text: postContent, engine: 'markdown' }).trim();
26 |
27 | const abbr = tabName + ' ' + ++tabId;
28 | const href = abbr.toLowerCase().split(' ').join('-');
29 |
30 | icon = icon.trim();
31 | if (icon.length > 0) {
32 | if (!icon.startsWith('fa')) icon = 'fa fa-' + icon;
33 | icon = ` `;
34 | }
35 |
36 | caption = icon + caption.trim();
37 |
38 | const isActive = (tabActive > 0 && tabActive === tabId) || (tabActive === 0 && tabId === 1) ? ' active' : '';
39 | tabNav += `${caption || abbr} `;
40 | tabContent += `${postContent}
`;
41 | }
42 |
43 | tabNav = ``;
44 | tabContent = `${tabContent}
`;
45 |
46 | return `${tabNav + tabContent}
`;
47 | };
48 |
--------------------------------------------------------------------------------
/scripts/tags/video.js:
--------------------------------------------------------------------------------
1 | /**
2 | * video.js | https://theme-next.js.org/docs/tag-plugins/
3 | */
4 |
5 | 'use strict';
6 |
7 | module.exports = function(args) {
8 | return ` `;
9 | };
10 |
--------------------------------------------------------------------------------
/scripts/tags/wavedrom.js:
--------------------------------------------------------------------------------
1 | /**
2 | * wavedrom.js | https://theme-next.js.org/docs/tag-plugins/wavedrom
3 | */
4 |
5 | 'use strict';
6 |
7 | module.exports = function(args, content) {
8 | return `
`;
11 | };
12 |
--------------------------------------------------------------------------------
/source/css/_common/components/back-to-top.styl:
--------------------------------------------------------------------------------
1 | if (hexo-config('back2top.enable')) {
2 | .back-to-top {
3 | font-size: $b2t-font-size;
4 |
5 | span {
6 | margin-right: 8px;
7 | if (not hexo-config('back2top.scrollpercent')) {
8 | display: none;
9 | }
10 | }
11 |
12 | .fa {
13 | text-align: center;
14 | width: $sidebar-toggle-size;
15 | }
16 |
17 | if (hexo-config('back2top.sidebar')) {
18 | margin: 20px - $sidebar-offset -10px -20px;
19 | // FIXME: opacity override by motion
20 | opacity: 0;
21 | transition: opacity $transition-ease;
22 |
23 | &.back-to-top-on {
24 | cursor: pointer;
25 | opacity: $b2t-opacity;
26 |
27 | &:hover {
28 | opacity: $b2t-opacity-hover;
29 | }
30 | }
31 | } else {
32 | align-items: center;
33 | bottom: $b2t-position-bottom;
34 | color: $b2t-color;
35 | display: flex;
36 | height: $sidebar-toggle-size;
37 | transition: $transition-ease;
38 | // Override in Pisces
39 | transition-property: bottom;
40 | sidebar-toggle();
41 |
42 | &:hover {
43 | color: $sidebar-highlight;
44 | }
45 |
46 | &.back-to-top-on {
47 | bottom: $b2t-position-bottom-on;
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/source/css/_common/components/index.styl:
--------------------------------------------------------------------------------
1 | @import 'back-to-top';
2 | @import 'reading-progress';
3 |
4 | @import 'post';
5 | @import 'pages';
6 | @import 'third-party';
7 |
--------------------------------------------------------------------------------
/source/css/_common/components/pages/breadcrumb.styl:
--------------------------------------------------------------------------------
1 | ul.breadcrumb {
2 | font-size: $font-size-smallest;
3 | list-style: none;
4 | margin: 1em 0;
5 | padding: 0 2em;
6 | text-align: center;
7 |
8 | li {
9 | display: inline;
10 | }
11 |
12 | li:not(:first-child)::before {
13 | content: '/\00a0';
14 | font-weight: normal;
15 | padding: .5em;
16 | }
17 |
18 | li:last-child {
19 | font-weight: bold;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/source/css/_common/components/pages/categories.styl:
--------------------------------------------------------------------------------
1 | .category-all-page {
2 | .category-all-title {
3 | text-align: center;
4 | }
5 |
6 | .category-all {
7 | margin-top: 20px;
8 | }
9 |
10 | .category-list {
11 | list-style: none;
12 | margin: 0;
13 | padding: 0;
14 | }
15 |
16 | .category-list-item {
17 | margin: 5px 10px;
18 | }
19 |
20 | .category-list-count {
21 | font-size: $font-size-smallest;
22 | badge();
23 | }
24 |
25 | .category-list-child {
26 | padding-left: 10px;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/source/css/_common/components/pages/index.styl:
--------------------------------------------------------------------------------
1 | // Page specific styles
2 | @import 'categories';
3 | @import 'schedule';
4 | @import 'breadcrumb';
5 | @import 'tag-cloud';
6 |
--------------------------------------------------------------------------------
/source/css/_common/components/pages/tag-cloud.styl:
--------------------------------------------------------------------------------
1 | .tag-cloud {
2 | text-align: center;
3 |
4 | a {
5 | display: inline-block;
6 | margin: 10px;
7 | }
8 | }
9 |
10 | for $tag-cloud in (0 .. 10) {
11 | $tag-cloud-color = mix($tag-cloud-end, $tag-cloud-start, $tag-cloud * 10);
12 | .tag-cloud-{$tag-cloud} {
13 | border-bottom-color: $tag-cloud-color;
14 | color: $tag-cloud-color;
15 | }
16 | }
17 |
18 | if (hexo-config('darkmode')) {
19 | @media (prefers-color-scheme: dark) {
20 | for $tag-cloud in (0 .. 10) {
21 | $tag-cloud-color = mix($tag-cloud-end-dark, $tag-cloud-start-dark, $tag-cloud * 10);
22 | .tag-cloud-{$tag-cloud} {
23 | border-bottom-color: $tag-cloud-color;
24 | color: $tag-cloud-color;
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/source/css/_common/components/post/index.styl:
--------------------------------------------------------------------------------
1 | .rtl {
2 | &.post-body {
3 | p, a, h1, h2, h3, h4, h5, h6, li, ul, ol {
4 | direction: rtl;
5 | font-family: UKIJ Ekran;
6 | }
7 | }
8 |
9 | &.post-title {
10 | font-family: UKIJ Ekran;
11 | }
12 | }
13 |
14 | .post-button {
15 | margin-top: 40px;
16 | text-align: $scheme-text-align;
17 | }
18 |
19 | .use-motion {
20 | if (hexo-config('motion.transition.post_block')) {
21 | .post-block, .pagination, .comments {
22 | visibility: hidden;
23 | }
24 | }
25 |
26 | if (hexo-config('motion.transition.post_header')) {
27 | .post-header {
28 | visibility: hidden;
29 | }
30 | }
31 |
32 | if (hexo-config('motion.transition.post_body')) {
33 | .post-body {
34 | visibility: hidden;
35 | }
36 | }
37 |
38 | if (hexo-config('motion.transition.coll_header')) {
39 | .collection-header {
40 | visibility: hidden;
41 | }
42 | }
43 | }
44 |
45 | @import 'post-collapse';
46 | @import 'post-body';
47 | @import 'post-gallery';
48 | @import 'post-header';
49 | @import 'post-nav';
50 | @import 'post-footer';
51 | @import 'post-widgets';
52 | @import 'post-reward';
53 | @import 'post-followme';
54 |
--------------------------------------------------------------------------------
/source/css/_common/components/post/post-body.styl:
--------------------------------------------------------------------------------
1 | .post-body {
2 | font-family: $font-family-posts;
3 | word-wrap();
4 |
5 | +desktop-large() {
6 | font-size: $font-size-large;
7 | }
8 |
9 | +desktop() {
10 | text-align: unquote(hexo-config('text_align.desktop'));
11 | }
12 |
13 | +tablet-mobile() {
14 | text-align: unquote(hexo-config('text_align.mobile'));
15 | }
16 |
17 | h1, h2, h3, h4, h5, h6 {
18 | // Supported plugins: hexo-renderer-markdown-it hexo-renderer-marked
19 | .header-anchor, .headerlink {
20 | border-bottom-style: none;
21 | color: inherit;
22 | float: right;
23 | font-size: $font-size-small;
24 | margin-left: 10px;
25 | opacity: 0;
26 |
27 | &::before {
28 | font-family-icons('\f0c1');
29 | }
30 | }
31 |
32 | &:hover {
33 | .header-anchor, .headerlink {
34 | opacity: .5;
35 |
36 | &:hover {
37 | opacity: 1;
38 | }
39 | }
40 | }
41 | }
42 |
43 | .exturl .fa {
44 | font-size: $font-size-small;
45 | margin-left: 4px;
46 | }
47 |
48 | // https://github.com/hexojs/hexo-renderer-marked/pull/264
49 | // https://github.com/next-theme/hexo-next-exif
50 | figure:not(.highlight) figcaption {
51 | color: $grey-dark;
52 | font-size: $font-size-small;
53 | font-weight: bold;
54 | line-height: 1;
55 | margin: -15px auto 15px;
56 | text-align: center;
57 | }
58 |
59 | iframe, img, video, embed {
60 | margin-bottom: 20px;
61 | }
62 |
63 | .video-container {
64 | height: 0;
65 | margin-bottom: 20px;
66 | overflow: hidden;
67 | padding-top: 75%;
68 | position: relative;
69 | width: 100%;
70 |
71 | iframe, object, embed {
72 | height: 100%;
73 | left: 0;
74 | margin: 0;
75 | position: absolute;
76 | top: 0;
77 | width: 100%;
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/source/css/_common/components/post/post-followme.styl:
--------------------------------------------------------------------------------
1 | if (hexo-config('follow_me')) {
2 | .followme {
3 | color: $grey;
4 | padding: 1em 1.5em;
5 | text-align: center;
6 | post-card();
7 |
8 | .social-list {
9 | flex-wrap();
10 |
11 | .social-item {
12 | margin: .5em 2em;
13 | position: relative;
14 |
15 | +tablet-mobile() {
16 | margin: .5em .75em;
17 | }
18 | }
19 |
20 | .social-link {
21 | border: 0;
22 | // Make the hit area continious
23 | display: block;
24 |
25 | .icon {
26 | font-size: 1.75em;
27 | }
28 |
29 | .label {
30 | display: block;
31 | font-size: 14px;
32 | }
33 |
34 | &:hover + .social-item-img {
35 | display: block;
36 | }
37 | }
38 |
39 | span.social-link {
40 | color: var(--link-color);
41 |
42 | &:hover {
43 | color: var(--link-hover-color);
44 | }
45 | }
46 |
47 | .social-item-img {
48 | display: none;
49 | left: 50%;
50 | max-width: $post-followme-img-width;
51 | position: absolute;
52 | transform: translate(-50%, 20px);
53 | z-index: 1;
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/source/css/_common/components/post/post-footer.styl:
--------------------------------------------------------------------------------
1 | // Flexbox layout makes it possible to reorder the child
2 | // elements of .post-footer through the `order` CSS property
3 | // Fix issue #16
4 | // To do: use `gap` instead of `margin`
5 | // See https://caniuse.com/flexbox-gap
6 | .post-footer {
7 | flex-column();
8 | }
9 |
10 | .post-eof {
11 | background: $grey-light;
12 | height: 1px;
13 | margin: $post-eof-margin-top auto $post-eof-margin-bottom;
14 | width: 8%;
15 |
16 | .post-block:last-of-type & {
17 | display: none;
18 | }
19 | }
20 |
21 | if (hexo-config('creative_commons.post')) {
22 | .post-copyright ul {
23 | list-style: none;
24 | overflow: hidden;
25 | padding: .5em 1em;
26 | position: relative;
27 | post-card();
28 |
29 | &::after {
30 | content: '\f25e';
31 | font-family: 'Font Awesome 6 Brands';
32 | font-size: 200px;
33 | opacity: $watermark-opacity;
34 | position: absolute;
35 | right: -50px;
36 | top: -150px;
37 | }
38 | }
39 | }
40 |
41 | .post-tags {
42 | margin-top: 40px;
43 | text-align: $scheme-text-align;
44 |
45 | a {
46 | display: inline-block;
47 | font-size: $font-size-smaller;
48 |
49 | &:not(:last-child) {
50 | margin-right: 10px;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/source/css/_common/components/post/post-gallery.styl:
--------------------------------------------------------------------------------
1 | .post-gallery {
2 | display: flex;
3 | min-height: 200px;
4 |
5 | .post-gallery-image {
6 | flex: 1;
7 |
8 | &:not(:first-child) {
9 | clip-path: polygon(40px 0, 100% 0, 100% 100%, 0 100%);
10 | margin-left: -20px;
11 | }
12 |
13 | &:not(:last-child) {
14 | margin-right: -20px;
15 | }
16 |
17 | img {
18 | height: 100%;
19 | object-fit: cover;
20 | // Override darkmode image opacity.
21 | opacity: 1;
22 | width: 100%;
23 | }
24 | }
25 | }
26 |
27 | .posts-expand .post-gallery {
28 | margin-bottom: 60px;
29 | }
30 |
31 | .posts-collapse .post-gallery {
32 | margin: 15px 0;
33 | }
34 |
--------------------------------------------------------------------------------
/source/css/_common/components/post/post-nav.styl:
--------------------------------------------------------------------------------
1 | .post-nav {
2 | border-top: 1px solid $gainsboro;
3 | display: flex;
4 | gap: 30px;
5 | justify-content: space-between;
6 | margin-top: 1em;
7 | padding: 10px 5px 0;
8 | }
9 |
10 | .post-nav-item {
11 | flex: 1;
12 |
13 | a {
14 | border-bottom: 0;
15 | display: block;
16 | font-size: $font-size-small;
17 | line-height: 1.6;
18 |
19 | &:active {
20 | top: 2px;
21 | }
22 | }
23 |
24 | .fa {
25 | font-size: $font-size-smallest;
26 | }
27 |
28 | &:first-child {
29 | .fa {
30 | margin-right: 5px;
31 | }
32 | }
33 |
34 | &:last-child {
35 | text-align: right;
36 |
37 | .fa {
38 | margin-left: 5px;
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/source/css/_common/components/post/post-reward.styl:
--------------------------------------------------------------------------------
1 | .reward-container {
2 | margin: $post-card-margin;
3 | padding: 1em 0;
4 | text-align: center;
5 |
6 | button {
7 | button($sidebar-highlight);
8 | border: 2px solid $sidebar-highlight;
9 | border-radius: 2px;
10 | outline: 0;
11 | vertical-align: text-top;
12 | }
13 | }
14 |
15 | .post-reward {
16 | display: none;
17 | padding-top: 20px;
18 |
19 | &.active {
20 | display: block;
21 | }
22 |
23 | div {
24 | display: inline-block;
25 |
26 | span {
27 | display: block;
28 | }
29 |
30 | if (hexo-config('reward_settings.animation')) {
31 | &:hover span {
32 | animation: next-roll .1s infinite linear;
33 | // The animation may affect :hover of img in dark mode
34 | pointer-events: none;
35 | }
36 | }
37 | }
38 |
39 | img {
40 | display: inline-block;
41 | margin: .8em 2em 0;
42 | max-width: 100%;
43 | width: $post-reward-img-width;
44 | }
45 | }
46 |
47 | @keyframes next-roll {
48 | from {
49 | transform: rotateZ(30deg);
50 | }
51 |
52 | to {
53 | transform: rotateZ(-30deg);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/source/css/_common/components/post/post-widgets.styl:
--------------------------------------------------------------------------------
1 | .social-like {
2 | border-top: 1px solid $gainsboro;
3 | font-size: $font-size-small;
4 | margin-top: 1em;
5 | padding-top: 1em;
6 | flex-wrap();
7 |
8 | a {
9 | border-bottom: none;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/source/css/_common/components/reading-progress.styl:
--------------------------------------------------------------------------------
1 | if (hexo-config('reading_progress.enable')) {
2 | .reading-progress-bar {
3 | --progress: 0;
4 | background: convert(hexo-config('reading_progress.color'));
5 | height: convert(hexo-config('reading_progress.height'));
6 | position: fixed;
7 | z-index: $zindex-5;
8 |
9 | if (hexo-config('reading_progress.reversed')) {
10 | width: calc(100% - var(--progress));
11 | } else {
12 | width: var(--progress);
13 | }
14 |
15 | if (hexo-config('reading_progress.start_at') == 'right') {
16 | right: 0;
17 | } else {
18 | left: 0;
19 | }
20 |
21 | if (hexo-config('reading_progress.position') == 'bottom') {
22 | bottom: 0;
23 | } else {
24 | top: 0;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/source/css/_common/components/third-party/disqusjs.styl:
--------------------------------------------------------------------------------
1 | if (hexo-config('disqusjs.enable') and hexo-config('darkmode')) {
2 | @media (prefers-color-scheme:dark) {
3 | html #dsqjs a {
4 | color: var(--link-color);
5 | }
6 |
7 | html #dsqjs a:focus,html #dsqjs a:hover {
8 | color: var(--link-hover-color);
9 | }
10 |
11 | html #dsqjs .dsqjs-nav,html #dsqjs footer {
12 | border-color: var(--card-bg-color);
13 | }
14 |
15 | html #dsqjs .dsqjs-load-more,html #dsqjs .dsqjs-load-more:hover,html #dsqjs .dsqjs-nav-tab,html #dsqjs .dsqjs-no-comment,html #dsqjs .dsqjs-post-content {
16 | color: var(--text-color);
17 | }
18 |
19 | html #dsqjs .dsqjs-order-label {
20 | background-color: #3e4b5e;
21 | }
22 |
23 | html #dsqjs .dsqjs-order-radio:checked+.dsqjs-order-label {
24 | background-color: var(--content-bg-color);
25 | }
26 |
27 | html #dsqjs .dsqjs-tab-active>span:after {
28 | background-color: #2e9fff!important;
29 | }
30 |
31 | html #dsqjs .dsqjs-footer,html #dsqjs .dsqjs-meta {
32 | color: var(--text-color);
33 | }
34 |
35 | html #dsqjs .dsqjs-post-body blockquote {
36 | border-color: var(--content-bg-color);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/source/css/_common/components/third-party/gitalk.styl:
--------------------------------------------------------------------------------
1 | if (hexo-config('gitalk.enable')) {
2 | .gt-header a, .gt-comments a, .gt-popup a {
3 | border-bottom: 0;
4 | }
5 |
6 | .gt-container .gt-popup .gt-action.is--active::before {
7 | top: .7em;
8 | }
9 |
10 | if (hexo-config('darkmode')) {
11 | @media (prefers-color-scheme: dark) {
12 | .gt-container .gt-header-textarea {
13 | background-color: var(--card-bg-color) !important;
14 | }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/source/css/_common/components/third-party/index.styl:
--------------------------------------------------------------------------------
1 | @import 'disqusjs';
2 | @import 'gitalk';
3 | @import 'utterances';
4 | @import 'search';
5 | @import 'math';
6 |
7 | .use-motion .animated {
8 | // Fix issue #48 #55
9 | animation-fill-mode: none;
10 | // Fix issue #46 .animated in .sidebar
11 | visibility: inherit;
12 | }
13 |
14 | .use-motion .sidebar .animated {
15 | animation-fill-mode: both;
16 | }
17 |
--------------------------------------------------------------------------------
/source/css/_common/components/third-party/math.styl:
--------------------------------------------------------------------------------
1 | if (hexo-config('math.mathjax.enable')) {
2 | mjx-container[jax='CHTML'][display='true'], .has-jax {
3 | overflow: auto hidden;
4 | }
5 |
6 | mjx-container[display='true'] + br {
7 | display: none;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/source/css/_common/components/third-party/utterances.styl:
--------------------------------------------------------------------------------
1 | if (hexo-config('utterances.enable')) {
2 | .utterances {
3 | max-width: unset;
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/source/css/_common/outline/header/bookmark.styl:
--------------------------------------------------------------------------------
1 | if (hexo-config('bookmark.enable')) {
2 | .book-mark-link {
3 | border-bottom: 0;
4 | position: fixed;
5 | top: -10px;
6 | transition: top .3s;
7 | sidebar-toggle-position(true);
8 |
9 | +tablet-mobile() {
10 | display: none;
11 | }
12 |
13 | &::before {
14 | color: convert(hexo-config('bookmark.color'));
15 | font-size: 32px;
16 | line-height: 1;
17 | font-family-icons('\f02e');
18 | }
19 | }
20 |
21 | .book-mark-link:hover, .book-mark-link-fixed {
22 | top: -2px;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/source/css/_common/outline/header/github-banner.styl:
--------------------------------------------------------------------------------
1 | if (hexo-config('github_banner.enable')) {
2 | @keyframes octocat-wave {
3 | 0%, 100% {
4 | transform: rotate(0);
5 | }
6 |
7 | 20%, 60% {
8 | transform: rotate(-25deg);
9 | }
10 |
11 | 40%, 80% {
12 | transform: rotate(10deg);
13 | }
14 | }
15 |
16 | .github-corner {
17 | :hover .octo-arm {
18 | animation: octocat-wave 560ms ease-in-out;
19 | }
20 |
21 | svg {
22 | color: white;
23 | fill: var(--theme-color);
24 | position: absolute;
25 | right: 0;
26 | top: 0;
27 | z-index: $zindex-0;
28 | }
29 |
30 | +tablet-mobile() {
31 | if (hexo-config('local_search.enable') or hexo-config('algolia_search.enable')) {
32 | display: none;
33 | }
34 |
35 | svg {
36 | if (($scheme == 'Pisces') or ($scheme == 'Gemini')) {
37 | color: var(--theme-color);
38 | fill: white;
39 | }
40 | }
41 |
42 | .github-corner:hover .octo-arm {
43 | animation: none;
44 | }
45 |
46 | .github-corner .octo-arm {
47 | animation: octocat-wave 560ms ease-in-out;
48 | }
49 | }
50 |
51 | if ($scheme == 'Mist') {
52 | +mobile() {
53 | svg {
54 | top: inherit;
55 | }
56 | }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/source/css/_common/outline/header/index.styl:
--------------------------------------------------------------------------------
1 | // Header Section
2 | // --------------------------------------------------
3 | .headband {
4 | background: $headband-bg;
5 | height: $headband-height;
6 |
7 | +tablet-mobile() {
8 | display: none;
9 | }
10 | }
11 |
12 | .site-brand-container {
13 | display: flex;
14 | flex-shrink: 0;
15 | padding: 0 10px;
16 | }
17 |
18 | .use-motion {
19 | .column, .site-brand-container .toggle {
20 | opacity: 0;
21 | }
22 | }
23 |
24 | @import 'site-meta';
25 | @import 'site-nav';
26 | @import 'menu';
27 |
28 | @import 'bookmark';
29 | @import 'github-banner';
30 |
--------------------------------------------------------------------------------
/source/css/_common/outline/header/menu.styl:
--------------------------------------------------------------------------------
1 | // Menu
2 | // --------------------------------------------------
3 | .menu {
4 | margin: 0;
5 | padding: 1em 0;
6 | text-align: center;
7 | }
8 |
9 | .menu-item {
10 | display: inline-block;
11 | list-style: none;
12 | margin: 0 10px;
13 |
14 | +mobile() {
15 | display: block;
16 | margin-top: 10px;
17 |
18 | &.menu-item-search {
19 | display: none;
20 | }
21 | }
22 |
23 | a {
24 | border-bottom: 0;
25 | display: block;
26 | font-size: $font-size-smaller;
27 | transition: border-color $transition-ease;
28 |
29 | &:hover, &.menu-item-active {
30 | background: var(--menu-item-bg-color);
31 | }
32 | }
33 |
34 | i[class^='fa'] {
35 | margin-right: 8px;
36 | }
37 |
38 | .badge {
39 | badge();
40 | }
41 | }
42 |
43 | if (hexo-config('motion.transition.menu_item')) {
44 | .use-motion .menu-item {
45 | visibility: hidden;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/source/css/_common/outline/header/site-meta.styl:
--------------------------------------------------------------------------------
1 | .site-meta {
2 | flex-grow: 1;
3 | text-align: center;
4 |
5 | +mobile() {
6 | text-align: center;
7 | }
8 | }
9 |
10 | .custom-logo-image {
11 | margin-top: 20px;
12 |
13 | +tablet-mobile() {
14 | display: none;
15 | }
16 | }
17 |
18 | .brand {
19 | border-bottom: 0;
20 | color: var(--brand-color);
21 | display: inline-block;
22 | padding: $brand-padding;
23 |
24 | &:hover {
25 | color: var(--brand-hover-color);
26 | }
27 | }
28 |
29 | .site-title {
30 | font-family: $font-family-logo;
31 | font-size: $font-size-title;
32 | font-weight: normal;
33 | line-height: 1.5;
34 | margin: 0;
35 | }
36 |
37 | .site-subtitle {
38 | color: $subtitle-color;
39 | font-size: $font-size-subtitle;
40 | margin: $site-subtitle-margin;
41 | }
42 |
43 | .use-motion {
44 | .site-title, .site-subtitle, .custom-logo-image {
45 | opacity: 0;
46 | position: relative;
47 | top: -10px;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/source/css/_common/outline/header/site-nav.styl:
--------------------------------------------------------------------------------
1 | .site-nav-toggle, .site-nav-right {
2 | display: none;
3 |
4 | +mobile() {
5 | flex-column();
6 | }
7 |
8 | .toggle {
9 | color: var(--text-color);
10 | padding: 10px;
11 | width: 22px;
12 |
13 | .toggle-line {
14 | background: var(--text-color);
15 | border-radius: 1px;
16 | }
17 | }
18 | }
19 |
20 | .site-nav {
21 | +mobile() {
22 | site-nav-hide-by-default();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/source/css/_common/outline/index.styl:
--------------------------------------------------------------------------------
1 | @import 'header';
2 | @import 'sidebar';
3 | @import 'footer';
4 |
5 | @import 'mobile';
6 |
--------------------------------------------------------------------------------
/source/css/_common/outline/mobile.styl:
--------------------------------------------------------------------------------
1 | if (hexo-config('mobile_layout_economy')) {
2 | +mobile-small() {
3 | // For Pisces & Gemini schemes only wider width (remove main blocks in Gemini).
4 | .main-inner {
5 | padding: initial !important;
6 | }
7 |
8 | // For all schemes wider width.
9 | .posts-expand {
10 | .post-header {
11 | margin-bottom: 10px !important;
12 | }
13 | }
14 |
15 | .post-block {
16 | margin-top: initial !important;
17 | // Inside posts blocks content padding (default 40px).
18 | padding: $content-mobile-padding 18px $content-mobile-padding !important;
19 | }
20 |
21 | .post-body {
22 | // For headers narrow width.
23 | h1, h2, h3, h4, h5, h6 {
24 | margin: 20px 0 8px;
25 | }
26 |
27 | // Rewrite paddings & margins inside tags.
28 | .note, .tabs .tab-content .tab-pane {
29 | h1, h2, h3, h4, h5, h6 {
30 | margin: 0 5px;
31 | }
32 | }
33 |
34 | // For paragraphs narrow width.
35 | > p {
36 | margin: 0 0 10px;
37 | }
38 |
39 | // Rewrite paddings & margins inside tags.
40 | .note > p, .tabs .tab-content .tab-pane > p {
41 | padding: 0 5px;
42 | }
43 |
44 | img, video {
45 | margin-bottom: 10px !important;
46 | }
47 |
48 | // Fix issue #641
49 | figure:not(.highlight) figcaption {
50 | margin: -5px auto 15px !important;
51 | }
52 |
53 | .note {
54 | margin-bottom: 10px !important;
55 | padding: 10px !important;
56 |
57 | if (hexo-config('note.icons')) {
58 | &:not(.no-icon) {
59 | padding-left: 35px !important;
60 | }
61 | }
62 | }
63 |
64 | .tabs .tab-content .tab-pane {
65 | padding: 10px 10px 0 !important;
66 | }
67 | }
68 |
69 | .post-eof {
70 | margin: 40px auto 20px !important;
71 | }
72 |
73 | .pagination {
74 | margin-top: 40px;
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/source/css/_common/outline/sidebar/related-posts.styl:
--------------------------------------------------------------------------------
1 | if (hexo-config('related_posts.enable')) {
2 | .sidebar-post-related {
3 | font-size: $font-size-smaller;
4 | padding: $sidebar-padding 0 0 0;
5 | }
6 |
7 | .popular-posts {
8 | margin: 0;
9 | padding: 1em 0;
10 | text-align: left;
11 |
12 | .popular-posts-item {
13 | display: block;
14 |
15 | .popular-posts-link {
16 | border-bottom: 0;
17 | display: block;
18 | padding: 5px 20px;
19 | transition: background .2s ease-in-out;
20 |
21 | &:hover {
22 | background: var(--menu-item-bg-color);
23 | }
24 | }
25 |
26 | .popular-posts-time {
27 | color: $site-state-item-name-color;
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/source/css/_common/outline/sidebar/sidebar-author-links.styl:
--------------------------------------------------------------------------------
1 | .links-of-author {
2 | a {
3 | font-size: $font-size-smaller;
4 | }
5 |
6 | if (not hexo-config('social_icons.icons_only')) {
7 | i[class^='fa'] {
8 | margin-right: 2px;
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/source/css/_common/outline/sidebar/sidebar-author.styl:
--------------------------------------------------------------------------------
1 | .site-author-image {
2 | border: $site-author-image-border-width solid $site-author-image-border-color;
3 | max-width: $site-author-image-width;
4 | padding: 2px;
5 |
6 | if (hexo-config('avatar.rounded')) {
7 | border-radius: 50%;
8 | }
9 |
10 | if (hexo-config('avatar.rotated')) {
11 | transition: transform 1s ease-out;
12 |
13 | &:hover {
14 | transform: rotateZ(360deg);
15 | }
16 | }
17 | }
18 |
19 | .site-author-name {
20 | color: $site-author-name-color;
21 | font-weight: $site-author-name-weight;
22 | margin: $site-author-name-margin;
23 | }
24 |
25 | .site-description {
26 | color: $site-description-color;
27 | font-size: $site-description-font-size;
28 | margin-top: $site-description-margin-top;
29 | }
30 |
--------------------------------------------------------------------------------
/source/css/_common/outline/sidebar/sidebar-blogroll.styl:
--------------------------------------------------------------------------------
1 | .links-of-blogroll {
2 | font-size: $font-size-smaller;
3 | }
4 |
5 | .links-of-blogroll-title {
6 | font-size: $font-size-small;
7 | font-weight: 600;
8 | }
9 |
10 | .links-of-blogroll-list {
11 | list-style: none;
12 | gap: 5px;
13 | margin: 5px 0 0;
14 | padding: 0;
15 | flex-wrap();
16 |
17 | if (hexo-config('links_settings.layout') == 'block') {
18 | flex-direction: column;
19 | }
20 | }
21 |
22 | .links-of-blogroll-item {
23 | max-width: 100%;
24 |
25 | a {
26 | sidebar-inline-links-item();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/source/css/_common/outline/sidebar/sidebar-button.styl:
--------------------------------------------------------------------------------
1 | .sidebar .sidebar-button {
2 | &:not(:first-child) {
3 | margin-top: 15px;
4 | }
5 |
6 | button {
7 | button($orange);
8 | border: 1px solid $orange;
9 | border-radius: 4px;
10 |
11 | i[class^='fa'] {
12 | margin-right: 5px;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/source/css/_common/outline/sidebar/sidebar-copyright.styl:
--------------------------------------------------------------------------------
1 | .cc-license {
2 | .cc-opacity {
3 | border-bottom: 0;
4 | opacity: .7;
5 |
6 | &:hover {
7 | opacity: .9;
8 | }
9 | }
10 |
11 | img {
12 | display: inline-block;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/source/css/_common/outline/sidebar/sidebar-toc.styl:
--------------------------------------------------------------------------------
1 | if (hexo-config('toc.enable')) {
2 | .post-toc {
3 | font-size: $font-size-small;
4 |
5 | ol {
6 | list-style: none;
7 | margin: 0;
8 | padding: 0 2px 0 10px;
9 | text-align: left;
10 |
11 | > :last-child {
12 | margin-bottom: 5px;
13 | }
14 |
15 | > ol {
16 | padding-left: 0;
17 | }
18 |
19 | a {
20 | transition: all $transition-ease;
21 | }
22 | }
23 |
24 | .nav-item {
25 | line-height: 1.8;
26 | overflow: hidden;
27 | text-overflow: ellipsis;
28 |
29 | if (not hexo-config('toc.wrap')) {
30 | white-space: nowrap;
31 | }
32 | }
33 |
34 | .nav {
35 | if (not hexo-config('toc.expand_all')) {
36 | .nav-child {
37 | --height: 0;
38 | height: 0;
39 | opacity: 0;
40 | overflow: hidden;
41 | transition-property: height, opacity, visibility;
42 | transition: $transition-ease;
43 | visibility: hidden;
44 | }
45 |
46 | .active > .nav-child {
47 | height: var(--height, auto);
48 | opacity: 1;
49 | visibility: unset;
50 | }
51 | }
52 |
53 | .active > a {
54 | border-bottom-color: $sidebar-highlight;
55 | color: $sidebar-highlight;
56 | }
57 |
58 | .active-current > a {
59 | color: $sidebar-highlight;
60 |
61 | &:hover {
62 | color: $sidebar-highlight;
63 | }
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/source/css/_common/outline/sidebar/sidebar-toggle.styl:
--------------------------------------------------------------------------------
1 | .sidebar-toggle {
2 | bottom: $b2t-position-bottom-on + $sidebar-toggle-size + 5px;
3 | height: $sidebar-toggle-inner-size;
4 | padding: $sidebar-toggle-padding;
5 | width: $sidebar-toggle-inner-size;
6 | sidebar-toggle();
7 | }
8 |
9 | .sidebar-toggle:hover .toggle-line {
10 | background: $sidebar-highlight;
11 | }
12 |
13 | @media (any-hover: hover) {
14 | body:not(.sidebar-active) .sidebar-toggle:hover {
15 | toggle-arrow($sidebar-toggle-alignment);
16 | }
17 | }
18 |
19 | .sidebar-active .sidebar-toggle {
20 | toggle-close($sidebar-toggle-alignment);
21 | }
22 |
--------------------------------------------------------------------------------
/source/css/_common/outline/sidebar/site-state.styl:
--------------------------------------------------------------------------------
1 | if (hexo-config('site_state')) {
2 | .site-state {
3 | flex-wrap();
4 | line-height: 1.4;
5 | }
6 |
7 | .site-state-item {
8 | // Fix issue #103
9 | // The click area of the link becomes smaller
10 | padding: 0 15px;
11 |
12 | a {
13 | border-bottom: 0;
14 | display: block;
15 | }
16 | }
17 |
18 | .site-state-item-count {
19 | display: block;
20 | font-size: $site-state-item-count-font-size;
21 | font-weight: 600;
22 | }
23 |
24 | .site-state-item-name {
25 | color: $site-state-item-name-color;
26 | font-size: $site-state-item-name-font-size;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/source/css/_common/scaffolding/base.styl:
--------------------------------------------------------------------------------
1 | ::selection {
2 | background: var(--selection-bg);
3 | color: var(--selection-color);
4 | }
5 |
6 | html, body {
7 | height: 100%;
8 | }
9 |
10 | body {
11 | background: var(--body-bg-color);
12 | box-sizing: border-box;
13 | color: var(--text-color);
14 | font-family: $font-family-base;
15 | font-size: $font-size-base;
16 | line-height: $line-height-base;
17 | min-height: 100%;
18 | position: relative;
19 | transition: padding $transition-ease;
20 |
21 | if (hexo-config('body_scrollbar.overlay')) {
22 | overflow-x: hidden;
23 | @supports (overflow-x: clip) {
24 | overflow-x: clip;
25 | }
26 | width: 100vw;
27 | }
28 |
29 | if (hexo-config('body_scrollbar.stable')) {
30 | // https://caniuse.com/mdn-css_properties_scrollbar-gutter
31 | scrollbar-gutter: stable;
32 | }
33 | }
34 |
35 | h1, h2, h3, h4, h5, h6 {
36 | font-family: $font-family-headings;
37 | font-weight: bold;
38 | line-height: 1.5;
39 | margin: 30px 0 15px;
40 | }
41 |
42 | for $headline in (1 .. 6) {
43 | h{$headline} {
44 | font-size: $font-size-headings-base - $font-size-headings-step * $headline;
45 | }
46 | }
47 |
48 | a {
49 | border-bottom: 1px solid $link-decoration-color;
50 | color: var(--link-color);
51 | // For a:not(:any-link)
52 | cursor: pointer;
53 | outline: 0;
54 | text-decoration: none;
55 | word-wrap();
56 |
57 | &:hover {
58 | border-bottom-color: var(--link-hover-color);
59 | color: var(--link-hover-color);
60 | }
61 | }
62 |
63 | iframe, img, video, embed {
64 | display: block;
65 | margin-left: auto;
66 | margin-right: auto;
67 | max-width: 100%;
68 | }
69 |
70 | hr {
71 | background-image: repeating-linear-gradient(-45deg, $grey-lighter, $grey-lighter 4px, transparent 4px, transparent 8px);
72 | border: 0;
73 | height: 3px;
74 | margin: 40px 0;
75 | }
76 |
77 | blockquote {
78 | border-left: 4px solid $grey-lighter;
79 | color: var(--blockquote-color);
80 | margin: 0;
81 | padding: 0 15px;
82 |
83 | cite::before {
84 | content: '-';
85 | padding: 0 5px;
86 | }
87 | }
88 |
89 | dt {
90 | font-weight: bold;
91 | }
92 |
93 | dd {
94 | margin: 0;
95 | padding: 0;
96 | }
97 |
--------------------------------------------------------------------------------
/source/css/_common/scaffolding/buttons.styl:
--------------------------------------------------------------------------------
1 | .btn {
2 | background: var(--btn-default-bg);
3 | border: 2px solid var(--btn-default-border-color);
4 | border-radius: $btn-default-radius;
5 | color: var(--btn-default-color);
6 | display: inline-block;
7 | font-size: $font-size-small;
8 | line-height: 2;
9 | padding: 0 20px;
10 | transition: background-color $transition-ease;
11 |
12 | &:hover {
13 | background: var(--btn-default-hover-bg);
14 | border-color: var(--btn-default-hover-border-color);
15 | color: var(--btn-default-hover-color);
16 | }
17 |
18 | + .btn {
19 | margin: 0 0 8px 8px;
20 | }
21 |
22 | .fa-fw {
23 | text-align: left;
24 | width: (18em / 14);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/source/css/_common/scaffolding/comments.styl:
--------------------------------------------------------------------------------
1 | .comments {
2 | margin-top: 60px;
3 | overflow: hidden;
4 | }
5 |
6 | .comment-button-group {
7 | display: flex;
8 | flex-wrap: wrap-reverse;
9 | justify-content: center;
10 | margin: 1em 0;
11 |
12 | .comment-button {
13 | margin: .1em .2em;
14 |
15 | &.active {
16 | background: var(--btn-default-hover-bg);
17 | border-color: var(--btn-default-hover-border-color);
18 | color: var(--btn-default-hover-color);
19 | }
20 | }
21 | }
22 |
23 | .comment-position {
24 | display: none;
25 |
26 | &.active {
27 | display: block;
28 | }
29 | }
30 |
31 | .tabs-comment {
32 | margin-top: 4em;
33 | padding-top: 0;
34 |
35 | .comments {
36 | margin-top: 0;
37 | padding-top: 0;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/source/css/_common/scaffolding/highlight/copy-code.styl:
--------------------------------------------------------------------------------
1 | .highlight:hover .copy-btn, .code-container:hover .copy-btn {
2 | opacity: 1;
3 | }
4 |
5 | .code-container {
6 | position: relative;
7 | }
8 |
9 | .code-lang {
10 | font-size: 40px;
11 | line-height: 1;
12 | opacity: $watermark-opacity;
13 | pointer-events: none;
14 | position: absolute;
15 | right: 5px;
16 | }
17 |
18 | .copy-btn {
19 | color: $black-dim;
20 | cursor: pointer;
21 | line-height: 1.6;
22 | opacity: 0;
23 | padding: 2px 6px;
24 | position: absolute;
25 | transition: opacity $transition-ease;
26 |
27 | if (hexo-config('codeblock.copy_button.style') == 'flat') {
28 | background: white;
29 | border: 0;
30 | font-size: $font-size-smaller;
31 | right: 0;
32 | top: 0;
33 | } else if (hexo-config('codeblock.copy_button.style') == 'mac') {
34 | color: var(--highlight-foreground);
35 | font-size: 14px;
36 | right: 0;
37 | top: 2px;
38 | } else {
39 | background-color: $gainsboro;
40 | background-image: linear-gradient(#fcfcfc, $gainsboro);
41 | border: 1px solid #d5d5d5;
42 | border-radius: 3px;
43 | font-size: $font-size-smaller;
44 | right: 4px;
45 | top: 8px;
46 | }
47 | }
48 |
49 | if (hexo-config('codeblock.copy_button.style') == 'mac') {
50 | figure.highlight {
51 | border-radius: 5px;
52 | box-shadow: 0 10px 30px 0 rgba(0, 0, 0, .4);
53 | padding-top: 30px;
54 |
55 | .table-container {
56 | border-radius: 0 0 5px 5px;
57 | }
58 |
59 | &::before {
60 | background: #fc625d;
61 | box-shadow: 20px 0 #fdbc40, 40px 0 #35cd4b;
62 | left: 12px;
63 | margin-top: -20px;
64 | position: absolute;
65 | round-icon(12px);
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/source/css/_common/scaffolding/highlight/fold.styl:
--------------------------------------------------------------------------------
1 | .expand-btn {
2 | bottom: 0;
3 | color: var(--highlight-foreground);
4 | cursor: pointer;
5 | display: none;
6 | left: 0;
7 | right: 0;
8 | position: absolute;
9 | text-align: center;
10 | }
11 |
12 | .fold-cover {
13 | background-image: linear-gradient(to top, var(--highlight-background) 0, rgba(0, 0, 0, 0) 100%);
14 | bottom: 0;
15 | display: none;
16 | height: 50px;
17 | left: 0;
18 | right: 0;
19 | position: absolute;
20 | }
21 |
22 | .highlight-fold {
23 | max-height: unit(hexo-config('codeblock.fold.height'), 'px');
24 | overflow-y: hidden !important;
25 |
26 | .expand-btn, .fold-cover {
27 | display: block;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/source/css/_common/scaffolding/index.styl:
--------------------------------------------------------------------------------
1 | //
2 | // Scaffolding
3 | // ==================================================
4 | @import 'normalize';
5 | @import 'base';
6 | @import 'tables';
7 | @import 'buttons';
8 | @import 'toggles';
9 | @import 'highlight';
10 | @import 'tags';
11 | @import 'pagination';
12 | @import 'comments';
13 |
--------------------------------------------------------------------------------
/source/css/_common/scaffolding/pagination.styl:
--------------------------------------------------------------------------------
1 | $page-number-basic {
2 | display: inline-block;
3 | margin: -1px 10px 0;
4 | padding: 0 10px;
5 |
6 | +mobile() {
7 | margin: 0 5px;
8 | }
9 | }
10 |
11 | $page-number-current {
12 | background: $pagination-active-bg;
13 | border-color: $pagination-active-border;
14 | color: $pagination-active-color;
15 | }
16 |
17 | .pagination {
18 | border-top: 1px solid $pagination-border;
19 | margin: 120px 0 0;
20 | text-align: $scheme-text-align;
21 |
22 | .prev, .next, .page-number {
23 | @extend $page-number-basic;
24 | border-bottom: 0;
25 | border-top: 1px solid $pagination-link-border;
26 | transition: border-color $transition-ease;
27 |
28 | &:hover {
29 | border-top-color: $pagination-link-hover-border;
30 | }
31 | }
32 |
33 | +mobile() {
34 | border-top: 0;
35 |
36 | .prev, .next, .page-number {
37 | border-bottom: 1px solid $pagination-link-border;
38 | border-top: 0;
39 |
40 | &:hover {
41 | border-bottom-color: $pagination-link-hover-border;
42 | }
43 | }
44 | }
45 |
46 | .space {
47 | @extend $page-number-basic;
48 | margin: 0;
49 | padding: 0;
50 | }
51 |
52 | .page-number.current {
53 | @extend $page-number-current;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/source/css/_common/scaffolding/tables.styl:
--------------------------------------------------------------------------------
1 | .table-container {
2 | overflow: auto;
3 | }
4 |
5 | table {
6 | border-collapse: collapse;
7 | border-spacing: 0;
8 | font-size: $table-font-size;
9 | margin: 0 0 20px;
10 | width: 100%;
11 | }
12 |
13 | tbody tr {
14 | &:nth-of-type(odd) {
15 | background: var(--table-row-odd-bg-color);
16 | }
17 |
18 | &:hover {
19 | background: var(--table-row-hover-bg-color);
20 | }
21 | }
22 |
23 | caption, th, td {
24 | padding: 8px;
25 | }
26 |
27 | th, td {
28 | border: 1px solid $table-border-color;
29 | border-bottom: 3px solid $table-cell-border-bottom-color;
30 | }
31 |
32 | th {
33 | font-weight: 700;
34 | padding-bottom: 10px;
35 | }
36 |
37 | td {
38 | border-bottom-width: 1px;
39 | }
40 |
--------------------------------------------------------------------------------
/source/css/_common/scaffolding/tags/blockquote-center.styl:
--------------------------------------------------------------------------------
1 | // Blockquote with all children centered.
2 | .blockquote-center {
3 | border-left: 0;
4 | margin: 40px 0;
5 | padding: 0;
6 | position: relative;
7 | text-align: center;
8 |
9 | &::before, &::after {
10 | left: 0;
11 | line-height: 1;
12 | opacity: .6;
13 | position: absolute;
14 | width: 100%;
15 | }
16 |
17 | &::before {
18 | border-top: 1px solid $grey-light;
19 | text-align: left;
20 | top: -20px;
21 | font-family-icons('\f10d');
22 | }
23 |
24 | &::after {
25 | border-bottom: 1px solid $grey-light;
26 | bottom: -20px;
27 | text-align: right;
28 | font-family-icons('\f10e');
29 | }
30 |
31 | p, div {
32 | text-align: center;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/source/css/_common/scaffolding/tags/group-pictures.styl:
--------------------------------------------------------------------------------
1 | .group-picture {
2 | margin-bottom: 20px;
3 |
4 | .group-picture-row {
5 | display: flex;
6 | gap: 3px;
7 | margin-bottom: 3px;
8 | }
9 |
10 | .group-picture-column {
11 | flex: 1;
12 |
13 | img {
14 | height: 100%;
15 | margin: 0;
16 | object-fit: cover;
17 | width: 100%;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/source/css/_common/scaffolding/tags/index.styl:
--------------------------------------------------------------------------------
1 | @import 'blockquote-center';
2 | @import 'group-pictures';
3 | @import 'label';
4 | @import 'link-grid';
5 | @import 'mermaid';
6 | @import 'wavedrom';
7 | @import 'note';
8 | @import 'pdf';
9 | @import 'tabs';
10 |
--------------------------------------------------------------------------------
/source/css/_common/scaffolding/tags/label.styl:
--------------------------------------------------------------------------------
1 | .post-body .label {
2 | color: $text-color;
3 | padding: 0 2px;
4 |
5 | for $type in $note-types {
6 | &.{$type} {
7 | background: $label[$type];
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/source/css/_common/scaffolding/tags/mermaid.styl:
--------------------------------------------------------------------------------
1 | if (hexo-config('mermaid.enable')) {
2 | .mermaid {
3 | margin-bottom: 20px;
4 | text-align: center;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/source/css/_common/scaffolding/tags/pdf.styl:
--------------------------------------------------------------------------------
1 | if (hexo-config('pdf.enable')) {
2 | .pdfobject-container {
3 | iframe, embed {
4 | height: convert(hexo-config('pdf.height'));
5 | width: 100%;
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/source/css/_common/scaffolding/tags/wavedrom.styl:
--------------------------------------------------------------------------------
1 | if (hexo-config('wavedrom.enable')) {
2 | .wavedrom {
3 | margin-bottom: 20px;
4 | text-align: center;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/source/css/_common/scaffolding/toggles.styl:
--------------------------------------------------------------------------------
1 | .toggle {
2 | line-height: 0;
3 |
4 | .toggle-line {
5 | background: white;
6 | display: block;
7 | height: 2px;
8 | left: 0;
9 | position: relative;
10 | top: 0;
11 | transition: .4s;
12 | transition-property: left, opacity, top, transform, width;
13 | width: 100%;
14 |
15 | &:first-child {
16 | margin-top: 1px;
17 | }
18 |
19 | &:not(:first-child) {
20 | margin-top: 4px;
21 | }
22 | }
23 | }
24 |
25 | .toggle.toggle-arrow {
26 | toggle-arrow($sidebar-toggle-alignment);
27 | }
28 |
29 | .toggle.toggle-close {
30 | toggle-close($sidebar-toggle-alignment);
31 | }
32 |
--------------------------------------------------------------------------------
/source/css/_schemes/Mist/_header.styl:
--------------------------------------------------------------------------------
1 | // Header
2 | // --------------------------------------------------
3 | .column {
4 | background: var(--content-bg-color);
5 | }
6 |
7 | header.header {
8 | align-items: center;
9 | display: flex;
10 | padding: 20px 0;
11 |
12 | +mobile() {
13 | display: block;
14 | padding: 10px 0;
15 | }
16 | }
17 |
18 | .site-meta {
19 | line-height: normal;
20 |
21 | .brand {
22 | +mobile() {
23 | display: block;
24 | }
25 | }
26 |
27 | .site-title {
28 | font-weight: bolder;
29 | }
30 | }
31 |
32 | .logo-line {
33 | background: var(--brand-color);
34 | display: block;
35 | height: 2px;
36 | margin: 0 auto;
37 | width: 75%;
38 |
39 | +mobile() {
40 | display: none;
41 | }
42 | }
43 |
44 | .use-motion {
45 | .logo-line:first-of-type {
46 | transform: scaleX(0);
47 | transform-origin: left;
48 | }
49 |
50 | .logo-line:last-of-type {
51 | transform: scaleX(0);
52 | transform-origin: right;
53 | }
54 | }
55 |
56 | .site-subtitle {
57 | display: none;
58 | }
59 |
--------------------------------------------------------------------------------
/source/css/_schemes/Mist/_layout.styl:
--------------------------------------------------------------------------------
1 | // Tags
2 | // --------------------------------------------------
3 | hr {
4 | height: 2px;
5 | margin: 20px 0;
6 | }
7 |
8 | // Components
9 | // --------------------------------------------------
10 | .btn {
11 | padding: 0 10px;
12 | }
13 |
14 | .headband {
15 | display: none;
16 | }
17 |
18 | // Pagination
19 | // --------------------------------------------------
20 | .pagination {
21 | +mobile() {
22 | margin: 80px 0 0;
23 | text-align: center;
24 | }
25 | }
26 |
27 | // Footer
28 | // --------------------------------------------------
29 | .footer {
30 | background: var(--content-bg-color);
31 | color: var(--text-color);
32 | padding: 10px 0;
33 | }
34 |
35 | .footer-inner {
36 | +mobile() {
37 | text-align: center;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/source/css/_schemes/Mist/_menu.styl:
--------------------------------------------------------------------------------
1 | // Menu
2 | // --------------------------------------------------
3 | .site-nav {
4 | flex-grow: 1;
5 |
6 | +mobile() {
7 | padding: 0 10px 0;
8 | }
9 | }
10 |
11 | .main-menu {
12 | +mobile() {
13 | padding-top: 10px;
14 | }
15 | }
16 |
17 | .menu {
18 | padding: 0;
19 |
20 | .menu-item {
21 | margin: 0;
22 |
23 | +mobile() {
24 | margin-top: 5px;
25 | }
26 |
27 | a {
28 | border-radius: 2px;
29 | padding: 0 10px;
30 | transition-property: background;
31 |
32 | +mobile() {
33 | text-align: left;
34 | menu-item-row();
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/source/css/_schemes/Mist/_posts-expand.styl:
--------------------------------------------------------------------------------
1 | // Post Expand
2 | // --------------------------------------------------
3 | .posts-expand {
4 | &.index {
5 | .post-header {
6 | text-align: $scheme-text-align;
7 |
8 | +mobile() {
9 | text-align: center;
10 | }
11 | }
12 |
13 | .post-meta-container {
14 | margin-top: 5px;
15 | }
16 |
17 | .post-meta {
18 | justify-content: flex-start;
19 |
20 | +mobile() {
21 | justify-content: center;
22 | }
23 | }
24 | }
25 |
26 | .post-eof {
27 | display: none;
28 | }
29 |
30 | .post-block:not(:first-of-type) {
31 | margin-top: 120px;
32 | }
33 |
34 | .post-header {
35 | margin-bottom: 20px;
36 | }
37 |
38 | .post-tags {
39 | a {
40 | background: var(--content-bg-color);
41 | border-bottom: 0;
42 | padding: 1px 5px;
43 |
44 | &:hover {
45 | background: var(--menu-item-bg-color);
46 | }
47 | }
48 | }
49 |
50 | .post-nav {
51 | margin-top: 40px;
52 | }
53 | }
54 |
55 | .post-button {
56 | margin-top: 20px;
57 |
58 | .btn {
59 | background: none;
60 | border: 0;
61 | border-bottom: 2px solid var(--btn-default-border-color);
62 | padding: 0;
63 | transition-property: border;
64 |
65 | &:hover {
66 | border-bottom-color: var(--btn-default-hover-border-color);
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/source/css/_schemes/Mist/index.styl:
--------------------------------------------------------------------------------
1 | //
2 | // Mist scheme
3 | // ==================================================
4 | @import '_layout';
5 | @import '_header';
6 | @import '_menu';
7 | @import '_posts-expand';
8 | @import '../Muse/_layout';
9 | @import '../Muse/_sidebar';
10 | @import '../Muse/_sub-menu';
11 |
--------------------------------------------------------------------------------
/source/css/_schemes/Muse/_header.styl:
--------------------------------------------------------------------------------
1 | .custom-logo-image {
2 | background: white;
3 | margin: 0 auto 10px;
4 | max-width: 150px;
5 | padding: 5px;
6 | }
7 |
8 | .brand {
9 | background: var(--btn-default-bg);
10 | }
11 |
12 | header.header {
13 | padding-top: 100px;
14 |
15 | +mobile() {
16 | padding-top: 50px;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/source/css/_schemes/Muse/_layout.styl:
--------------------------------------------------------------------------------
1 | header.header {
2 | main-container();
3 | }
4 |
5 | .main-inner {
6 | main-container();
7 | padding-bottom: $content-padding-bottom;
8 |
9 | +mobile() {
10 | padding-left: 20px;
11 | padding-right: 20px;
12 | }
13 | }
14 |
15 | // Page - Container
16 | // --------------------------------------------------
17 | .post-block:first-of-type {
18 | padding-top: $posts-first-padding;
19 |
20 | +mobile() {
21 | padding-top: $posts-first-padding-mobile;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/source/css/_schemes/Muse/_menu.styl:
--------------------------------------------------------------------------------
1 | .site-nav {
2 | +mobile() {
3 | padding-top: 30px;
4 | }
5 | }
6 |
7 | .main-menu {
8 | +mobile() {
9 | border-bottom: 1px solid $grey-lighter;
10 | border-top: 1px solid $grey-lighter;
11 | }
12 | }
13 |
14 | .menu {
15 | +mobile() {
16 | text-align: left;
17 | }
18 | }
19 |
20 | .menu .menu-item {
21 | +mobile() {
22 | margin: 0 10px;
23 | }
24 |
25 | a {
26 | border-bottom: 1px solid transparent;
27 |
28 | +mobile() {
29 | padding: 5px 10px;
30 | menu-item-row();
31 | }
32 |
33 | &:hover, &.menu-item-active {
34 | background: transparent;
35 | border-bottom: 1px solid var(--link-hover-color);
36 |
37 | +mobile() {
38 | border-bottom: 1px dotted $grey-lighter;
39 | }
40 | }
41 | }
42 |
43 | i[class^='fa'] {
44 | +tablet-desktop() {
45 | display: block;
46 | line-height: 2;
47 | margin-right: 0;
48 | width: 100%;
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/source/css/_schemes/Muse/_sidebar.styl:
--------------------------------------------------------------------------------
1 | +desktop-large() {
2 | .sidebar-dimmer {
3 | display: none;
4 | }
5 |
6 | .sidebar-active {
7 | // Note: $sidebar-width-expanded + $content-desktop-large should be less than desktop-large threshold
8 | // Otherwise a horizontal scrollbar will appear
9 | if ($sidebar-toggle-alignment == 'right') {
10 | padding-right: $sidebar-width-expanded;
11 |
12 | .footer-fixed {
13 | right: $sidebar-width-expanded;
14 | }
15 | } else {
16 | padding-left: $sidebar-width-expanded;
17 |
18 | .footer-fixed {
19 | left: $sidebar-width-expanded;
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/source/css/_schemes/Muse/_sub-menu.styl:
--------------------------------------------------------------------------------
1 | .sub-menu {
2 | margin: 10px 0;
3 |
4 | .menu-item {
5 | display: inline-block;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/source/css/_schemes/Muse/index.styl:
--------------------------------------------------------------------------------
1 | @import '_layout';
2 | @import '_header';
3 | @import '_menu';
4 | @import '_sub-menu';
5 | @import '_sidebar';
6 |
--------------------------------------------------------------------------------
/source/css/_schemes/Pisces/_header.styl:
--------------------------------------------------------------------------------
1 | .column {
2 | width: $sidebar-width-dual-column;
3 |
4 | +tablet-mobile() {
5 | width: auto;
6 | }
7 | }
8 |
9 | .site-brand-container {
10 | background: var(--theme-color);
11 |
12 | .site-nav-on & {
13 | +tablet-mobile() {
14 | box-shadow: 0 0 16px rgba(0, 0, 0, .5);
15 | }
16 | }
17 | }
18 |
19 | .site-meta {
20 | padding: 20px 0;
21 | }
22 |
23 | .site-nav-toggle, .site-nav-right {
24 | +tablet() {
25 | flex-column();
26 | }
27 |
28 | .toggle {
29 | color: white;
30 |
31 | .toggle-line {
32 | background: white;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/source/css/_schemes/Pisces/_layout.styl:
--------------------------------------------------------------------------------
1 | header.header {
2 | gemini-block();
3 |
4 | +tablet-mobile() {
5 | border-radius: initial;
6 | }
7 | }
8 |
9 | .main {
10 | // Make sure that .header and .main-inner are the same height
11 | // Required for .sidebar `position: sticky;`
12 | align-items: stretch;
13 | display: flex;
14 | justify-content: space-between;
15 | main-container();
16 |
17 | if ($sidebar-toggle-alignment == 'right') {
18 | flex-direction: row-reverse;
19 | }
20 |
21 | +tablet-mobile() {
22 | display: block;
23 | width: auto;
24 | }
25 | }
26 |
27 | .main-inner {
28 | border-radius: $border-radius-inner;
29 | box-sizing: border-box;
30 | width: $content-wrap;
31 |
32 | +tablet-mobile() {
33 | border-radius: initial;
34 | width: 100%;
35 | }
36 | }
37 |
38 | .footer-inner {
39 | if ($sidebar-toggle-alignment == 'right') {
40 | padding-right: $sidebar-width-dual-column + $sidebar-offset;
41 | } else {
42 | padding-left: $sidebar-width-dual-column + $sidebar-offset;
43 | }
44 |
45 | +tablet-mobile() {
46 | padding-left: 0;
47 | padding-right: 0;
48 | width: auto;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/source/css/_schemes/Pisces/_menu.styl:
--------------------------------------------------------------------------------
1 | .site-nav {
2 | +tablet() {
3 | site-nav-hide-by-default();
4 | }
5 | }
6 |
7 | .menu .menu-item {
8 | display: block;
9 | margin: 0;
10 |
11 | a {
12 | padding: 5px 20px;
13 | // For .menu-item-active::after
14 | position: relative;
15 | transition-property: background-color;
16 | menu-item-row();
17 | }
18 |
19 | +tablet-mobile() {
20 | &.menu-item-search {
21 | display: none;
22 | }
23 | }
24 | }
25 |
26 | if (not hexo-config('menu_settings.badges')) {
27 | // Only apply to the main menu
28 | // Fix issue #850
29 | .main-menu .menu-item-active::after {
30 | background: $grey;
31 | border-radius: 50%;
32 | content: ' ';
33 | height: 6px;
34 | margin-top: -3px;
35 | position: absolute;
36 | right: 15px;
37 | top: 50%;
38 | width: 6px;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/source/css/_schemes/Pisces/_sidebar.styl:
--------------------------------------------------------------------------------
1 | +desktop() {
2 | .sidebar {
3 | // https://caniuse.com/css-sticky
4 | position: sticky;
5 | top: $sidebar-offset;
6 | }
7 |
8 | .sidebar-toggle {
9 | display: none;
10 | }
11 |
12 | .sidebar-inner {
13 | background: var(--content-bg-color);
14 | border-radius: $border-radius;
15 | box-shadow: $box-shadow;
16 | box-sizing: border-box;
17 | color: var(--text-color);
18 | margin-top: $sidebar-offset;
19 | max-height: 'calc(100vh - %s)' % unit($sidebar-offset * 2, 'px');
20 |
21 | if (hexo-config('motion.enable') and hexo-config('motion.transition.sidebar')) {
22 | visibility: hidden;
23 | }
24 | }
25 |
26 | .site-state-item {
27 | padding: 0 10px;
28 | }
29 |
30 | .sidebar .sidebar-button {
31 | border-bottom: 1px dotted $grey-light;
32 | border-top: 1px dotted $grey-light;
33 |
34 | button {
35 | border: 0;
36 | color: $orange;
37 | display: block;
38 | width: 100%;
39 |
40 | &:hover {
41 | background: none;
42 | border: 0;
43 | color: darken($orange, 20%);
44 | }
45 | }
46 | }
47 |
48 | .links-of-author {
49 | flex-wrap();
50 | }
51 |
52 | .links-of-author-item {
53 | margin: 5px 0 0;
54 |
55 | if (not hexo-config('social_icons.icons_only')) {
56 | width: 50%;
57 | }
58 |
59 | a {
60 | border-bottom: 0;
61 | border-radius: 4px;
62 | display: block;
63 | padding: 0 5px;
64 | sidebar-inline-links-item();
65 |
66 | &:hover {
67 | background: var(--body-bg-color);
68 | }
69 | }
70 | }
71 |
72 | .links-of-blogroll-item a {
73 | padding: 0 5px;
74 | }
75 |
76 | if (hexo-config('back2top.sidebar')) {
77 | // Only when back2top.sidebar is true, apply the following styles
78 | .back-to-top {
79 | background: var(--body-bg-color);
80 | margin: 8px - $sidebar-offset -10px -18px;
81 | transition-property: bottom, margin-top;
82 |
83 | &.back-to-top-on {
84 | margin-top: 16px;
85 | }
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/source/css/_schemes/Pisces/_sub-menu.styl:
--------------------------------------------------------------------------------
1 | .sub-menu {
2 | margin: 0;
3 | padding: 6px 0;
4 |
5 | .menu-item {
6 | display: inline-block;
7 |
8 | a {
9 | background: transparent;
10 | margin: 5px 10px;
11 | padding: initial;
12 |
13 | &:hover {
14 | background: transparent;
15 | color: $sidebar-highlight;
16 | }
17 | }
18 | }
19 |
20 | .menu-item-active {
21 | border-bottom-color: $sidebar-highlight;
22 | color: $sidebar-highlight;
23 |
24 | &:hover {
25 | border-bottom-color: $sidebar-highlight;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/source/css/_schemes/Pisces/index.styl:
--------------------------------------------------------------------------------
1 | @import '_layout';
2 | @import '_header';
3 | @import '_menu';
4 | @import '_sub-menu';
5 | @import '_sidebar';
6 |
7 | .main-inner {
8 | background: var(--content-bg-color);
9 | box-shadow: $box-shadow-inner;
10 | padding: $content-desktop-padding;
11 |
12 | +tablet-mobile() {
13 | padding: 20px;
14 | }
15 | }
16 |
17 | // Sub-menu(s).
18 | .sub-menu {
19 | border-bottom: 1px solid $table-border-color;
20 | }
21 |
22 | .post-block:first-of-type {
23 | padding-top: 40px;
24 | }
25 |
26 | .pagination {
27 | +mobile() {
28 | margin-bottom: 10px;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/source/css/_variables/Gemini.styl:
--------------------------------------------------------------------------------
1 | // Variables of Gemini scheme
2 | // ==================================================
3 |
4 | @import 'Pisces';
5 |
6 | // Settings for some of the most global styles.
7 | // --------------------------------------------------
8 | $body-bg-color = #eee;
9 |
10 | // Borders.
11 | // --------------------------------------------------
12 | $box-shadow-inner = 0 2px 2px 0 rgba(0, 0, 0, .12), 0 3px 1px -2px rgba(0, 0, 0, .06), 0 1px 5px 0 rgba(0, 0, 0, .12);
13 | $box-shadow = 0 2px 2px 0 rgba(0, 0, 0, .12), 0 3px 1px -2px rgba(0, 0, 0, .06), 0 1px 5px 0 rgba(0, 0, 0, .12), 0 -1px .5px 0 rgba(0, 0, 0, .09);
14 |
15 | $border-radius-inner = initial;
16 | $border-radius = initial;
17 | // $border-radius-inner = 0 0 3px 3px;
18 | // $border-radius = 3px;
19 |
--------------------------------------------------------------------------------
/source/css/_variables/Mist.styl:
--------------------------------------------------------------------------------
1 | // Variables of Mist scheme
2 | // ==================================================
3 |
4 | @import 'Muse';
5 |
6 | $scheme-text-align = left;
7 |
8 | $content-padding-bottom = 80px;
9 | $posts-first-padding = 80px;
10 | $posts-first-padding-mobile = 60px;
11 |
12 | $link-decoration-color = $grey-light;
13 | $content-bg-color = $whitesmoke;
14 | $menu-item-bg-color = $grey-lighter;
15 |
16 | $brand-color = $black-deep;
17 | $brand-hover-color = $brand-color;
18 | $brand-padding = 2px 1px;
19 |
20 | $posts-collapse-left = 0;
21 |
22 | $btn-default-bg = transparent;
23 | $btn-default-color = var(--link-color);
24 | $btn-default-hover-bg = transparent;
25 | $btn-default-border-color = var(--link-color);
26 | $btn-default-hover-color = var(--link-hover-color);
27 | $btn-default-hover-border-color = var(--link-hover-color);
28 |
29 | $badge-background = white;
30 | $badge-border-radius = 10px;
31 | $badge-text-shadow = 1px 1px 0 rgba(0, 0, 0, .1);
32 |
--------------------------------------------------------------------------------
/source/css/_variables/Muse.styl:
--------------------------------------------------------------------------------
1 | // Variables of Muse scheme
2 | // ==================================================
3 |
4 | $content-padding-bottom = 60px;
5 | $posts-first-padding = 70px;
6 | $posts-first-padding-mobile = 35px;
7 |
--------------------------------------------------------------------------------
/source/css/main.styl:
--------------------------------------------------------------------------------
1 | // CSS Style Guide: https://codeguide.co/#css
2 |
3 | // https://stylus-lang.com/docs/keyframes.html
4 | vendors = official;
5 |
6 | $scheme = hexo-config('scheme') ? hexo-config('scheme') : 'Muse';
7 |
8 |
9 | // Variables Layer
10 | // --------------------------------------------------
11 | @import '_variables/base';
12 | @import '_variables/' + $scheme;
13 | for $inject_variable in hexo-config('injects.variable')
14 | @import $inject_variable;
15 |
16 | // Mixins Layer
17 | // --------------------------------------------------
18 | @import '_mixins';
19 | for $inject_mixin in hexo-config('injects.mixin')
20 | @import $inject_mixin;
21 |
22 | // Dark mode colors
23 | // --------------------------------------------------
24 | @import '_colors';
25 |
26 | // Common Layer
27 | // --------------------------------------------------
28 |
29 | // Scaffolding
30 | @import '_common/scaffolding';
31 |
32 | // Layout
33 | @import '_common/outline';
34 |
35 | // Components
36 | @import '_common/components';
37 |
38 |
39 | // Schemes Layer
40 | // --------------------------------------------------
41 | @import '_schemes/' + $scheme;
42 |
43 |
44 | // Custom Layer
45 | // --------------------------------------------------
46 | for $inject_style in hexo-config('injects.style')
47 | @import $inject_style;
48 |
--------------------------------------------------------------------------------
/source/css/noscript.styl:
--------------------------------------------------------------------------------
1 | @import '_variables/base';
2 |
3 | body { margin-top: 2rem; }
4 |
5 | .use-motion .menu-item,
6 | .use-motion .sidebar,
7 | .use-motion .sidebar-inner,
8 | .use-motion .post-block,
9 | .use-motion .pagination,
10 | .use-motion .comments,
11 | .use-motion .post-header,
12 | .use-motion .post-body,
13 | .use-motion .collection-header {
14 | visibility: visible;
15 | }
16 |
17 | .use-motion .column,
18 | .use-motion .site-brand-container .toggle,
19 | .use-motion .footer { opacity: initial; }
20 |
21 | .use-motion .site-title,
22 | .use-motion .site-subtitle,
23 | .use-motion .custom-logo-image {
24 | opacity: initial;
25 | top: initial;
26 | }
27 |
28 | .use-motion .logo-line {
29 | transform: scaleX(1);
30 | }
31 |
32 | .search-pop-overlay, .sidebar-nav { display: none; }
33 | .sidebar-panel { display: block; }
34 |
35 | .noscript-warning {
36 | background-color: lighten($red, 20%);
37 | color: white;
38 | font-family: sans-serif;
39 | font-size: 1rem;
40 | font-weight: bold;
41 | left: 0;
42 | position: fixed;
43 | text-align: center;
44 | top: 0;
45 | width: 100%;
46 | z-index: $zindex-5;
47 | }
48 |
--------------------------------------------------------------------------------
/source/images/apple-touch-icon-next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/next-theme/hexo-theme-next/2c02824edb508482ab61c187ef95d089232918b4/source/images/apple-touch-icon-next.png
--------------------------------------------------------------------------------
/source/images/avatar.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/next-theme/hexo-theme-next/2c02824edb508482ab61c187ef95d089232918b4/source/images/avatar.gif
--------------------------------------------------------------------------------
/source/images/favicon-16x16-next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/next-theme/hexo-theme-next/2c02824edb508482ab61c187ef95d089232918b4/source/images/favicon-16x16-next.png
--------------------------------------------------------------------------------
/source/images/favicon-32x32-next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/next-theme/hexo-theme-next/2c02824edb508482ab61c187ef95d089232918b4/source/images/favicon-32x32-next.png
--------------------------------------------------------------------------------
/source/images/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/source/js/bookmark.js:
--------------------------------------------------------------------------------
1 | /* global CONFIG */
2 |
3 | document.addEventListener('DOMContentLoaded', () => {
4 | 'use strict';
5 |
6 | const doSaveScroll = () => {
7 | localStorage.setItem('bookmark' + location.pathname, window.scrollY);
8 | };
9 |
10 | const scrollToMark = () => {
11 | let top = localStorage.getItem('bookmark' + location.pathname);
12 | top = Number(top);
13 | // If the page opens with a specific hash, just jump out
14 | if (!isNaN(top) && location.hash === '') {
15 | // Auto scroll to the position
16 | window.anime({
17 | targets : document.scrollingElement,
18 | duration : 200,
19 | easing : 'linear',
20 | scrollTop: top
21 | });
22 | }
23 | };
24 | // Register everything
25 | const init = function(trigger) {
26 | // Create a link element
27 | const link = document.querySelector('.book-mark-link');
28 | // Scroll event
29 | window.addEventListener('scroll', () => link.classList.toggle('book-mark-link-fixed', window.scrollY === 0), { passive: true });
30 | // Register beforeunload event when the trigger is auto
31 | if (trigger === 'auto') {
32 | // Register beforeunload event
33 | window.addEventListener('beforeunload', doSaveScroll);
34 | document.addEventListener('pjax:send', doSaveScroll);
35 | }
36 | // Save the position by clicking the icon
37 | link.addEventListener('click', () => {
38 | doSaveScroll();
39 | window.anime({
40 | targets : link,
41 | duration: 200,
42 | easing : 'linear',
43 | top : -30,
44 | complete: () => {
45 | setTimeout(() => {
46 | link.style.top = '';
47 | }, 400);
48 | }
49 | });
50 | });
51 | scrollToMark();
52 | document.addEventListener('pjax:success', scrollToMark);
53 | };
54 |
55 | init(CONFIG.bookmark.save);
56 | });
57 |
--------------------------------------------------------------------------------
/source/js/comments-buttons.js:
--------------------------------------------------------------------------------
1 | /* global CONFIG */
2 |
3 | (function() {
4 | const commentButton = document.querySelectorAll('.comment-button');
5 | commentButton.forEach(element => {
6 | const commentClass = element.classList[2];
7 | element.addEventListener('click', () => {
8 | commentButton.forEach(active => active.classList.toggle('active', active === element));
9 | document.querySelectorAll('.comment-position').forEach(active => active.classList.toggle('active', active.classList.contains(commentClass)));
10 | if (CONFIG.comments.storage) {
11 | localStorage.setItem('comments_active', commentClass);
12 | }
13 | });
14 | });
15 | let { activeClass } = CONFIG.comments;
16 | if (CONFIG.comments.storage) {
17 | activeClass = localStorage.getItem('comments_active') || activeClass;
18 | }
19 | if (activeClass) {
20 | const activeButton = document.querySelector(`.comment-button.${activeClass}`);
21 | if (activeButton) {
22 | activeButton.click();
23 | }
24 | }
25 | })();
26 |
--------------------------------------------------------------------------------
/source/js/comments.js:
--------------------------------------------------------------------------------
1 | /* global CONFIG */
2 |
3 | window.addEventListener('tabs:register', () => {
4 | let { activeClass } = CONFIG.comments;
5 | if (CONFIG.comments.storage) {
6 | activeClass = localStorage.getItem('comments_active') || activeClass;
7 | }
8 | if (activeClass) {
9 | const activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
10 | if (activeTab) {
11 | activeTab.click();
12 | }
13 | }
14 | });
15 | if (CONFIG.comments.storage) {
16 | window.addEventListener('tabs:click', event => {
17 | if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
18 | const commentClass = event.target.classList[1];
19 | localStorage.setItem('comments_active', commentClass);
20 | });
21 | }
22 |
--------------------------------------------------------------------------------
/source/js/config.js:
--------------------------------------------------------------------------------
1 | if (!window.NexT) window.NexT = {};
2 |
3 | (function() {
4 | const className = 'next-config';
5 |
6 | const staticConfig = {};
7 | let variableConfig = {};
8 |
9 | const parse = text => JSON.parse(text || '{}');
10 |
11 | const update = name => {
12 | const targetEle = document.querySelector(`.${className}[data-name="${name}"]`);
13 | if (!targetEle) return;
14 | const parsedConfig = parse(targetEle.text);
15 | if (name === 'main') {
16 | Object.assign(staticConfig, parsedConfig);
17 | } else {
18 | variableConfig[name] = parsedConfig;
19 | }
20 | };
21 |
22 | update('main');
23 |
24 | window.CONFIG = new Proxy({}, {
25 | get(overrideConfig, name) {
26 | let existing;
27 | if (name in staticConfig) {
28 | existing = staticConfig[name];
29 | } else {
30 | if (!(name in variableConfig)) update(name);
31 | existing = variableConfig[name];
32 | }
33 |
34 | // For unset override and mixable existing
35 | if (!(name in overrideConfig) && typeof existing === 'object') {
36 | // Get ready to mix.
37 | overrideConfig[name] = {};
38 | }
39 |
40 | if (name in overrideConfig) {
41 | const override = overrideConfig[name];
42 |
43 | // When mixable
44 | if (typeof override === 'object' && typeof existing === 'object') {
45 | // Mix, proxy changes to the override.
46 | return new Proxy({ ...existing, ...override }, {
47 | set(target, prop, value) {
48 | target[prop] = value;
49 | override[prop] = value;
50 | return true;
51 | }
52 | });
53 | }
54 |
55 | return override;
56 | }
57 |
58 | // Only when not mixable and override hasn't been set.
59 | return existing;
60 | }
61 | });
62 |
63 | document.addEventListener('pjax:success', () => {
64 | variableConfig = {};
65 | });
66 | })();
67 |
--------------------------------------------------------------------------------
/source/js/pjax.js:
--------------------------------------------------------------------------------
1 | /* global NexT, CONFIG, Pjax */
2 |
3 | const pjax = new Pjax({
4 | selectors: [
5 | 'head title',
6 | 'meta[property="og:title"]',
7 | 'script[type="application/json"]',
8 | // Precede .main-inner to prevent placeholder TOC changes asap
9 | '.post-toc-wrap',
10 | '.main-inner',
11 | '.languages',
12 | '.pjax'
13 | ],
14 | switches: {
15 | '.post-toc-wrap'(oldWrap, newWrap) {
16 | if (newWrap.querySelector('.post-toc')) {
17 | Pjax.switches.outerHTML.call(this, oldWrap, newWrap);
18 | } else {
19 | const curTOC = oldWrap.querySelector('.post-toc');
20 | if (curTOC) {
21 | curTOC.classList.add('placeholder-toc');
22 | }
23 | this.onSwitch();
24 | }
25 | }
26 | },
27 | analytics: false,
28 | cacheBust: false,
29 | scrollTo : !CONFIG.bookmark.enable
30 | });
31 |
32 | document.addEventListener('pjax:success', () => {
33 | pjax.executeScripts(document.querySelectorAll('script[data-pjax]'));
34 | NexT.boot.refresh();
35 | // Define Motion Sequence & Bootstrap Motion.
36 | if (CONFIG.motion.enable) {
37 | NexT.motion.integrator
38 | .init()
39 | .add(NexT.motion.middleWares.subMenu)
40 | // Add sidebar-post-related transition.
41 | .add(NexT.motion.middleWares.sidebar)
42 | .add(NexT.motion.middleWares.postList)
43 | .bootstrap();
44 | }
45 | if (CONFIG.sidebar.display !== 'remove') {
46 | const hasTOC = document.querySelector('.post-toc:not(.placeholder-toc)');
47 | document.querySelector('.sidebar-inner').classList.toggle('sidebar-nav-active', hasTOC);
48 | NexT.utils.activateSidebarPanel(hasTOC ? 0 : 1);
49 | NexT.utils.updateSidebarPosition();
50 | }
51 | });
52 |
53 | if (!window.pjax) window.pjax = pjax;
54 |
--------------------------------------------------------------------------------
/source/js/sidebar.js:
--------------------------------------------------------------------------------
1 | /* global CONFIG */
2 |
3 | document.addEventListener('DOMContentLoaded', () => {
4 |
5 | const isRight = CONFIG.sidebar.position === 'right';
6 |
7 | const sidebarToggleMotion = {
8 | mouse: {},
9 | init() {
10 | window.addEventListener('mousedown', this.mousedownHandler.bind(this));
11 | window.addEventListener('mouseup', this.mouseupHandler.bind(this));
12 | document.querySelector('.sidebar-dimmer').addEventListener('click', this.clickHandler.bind(this));
13 | document.querySelector('.sidebar-toggle').addEventListener('click', this.clickHandler.bind(this));
14 | window.addEventListener('sidebar:show', this.showSidebar);
15 | window.addEventListener('sidebar:hide', this.hideSidebar);
16 | },
17 | mousedownHandler(event) {
18 | this.mouse.X = event.pageX;
19 | this.mouse.Y = event.pageY;
20 | },
21 | mouseupHandler(event) {
22 | const deltaX = event.pageX - this.mouse.X;
23 | const deltaY = event.pageY - this.mouse.Y;
24 | const clickingBlankPart = Math.hypot(deltaX, deltaY) < 20 && event.target.matches('.main');
25 | // Fancybox has z-index property, but medium-zoom does not, so the sidebar will overlay the zoomed image.
26 | if (clickingBlankPart || event.target.matches('img.medium-zoom-image')) {
27 | this.hideSidebar();
28 | }
29 | },
30 | clickHandler() {
31 | document.body.classList.contains('sidebar-active') ? this.hideSidebar() : this.showSidebar();
32 | },
33 | showSidebar() {
34 | document.body.classList.add('sidebar-active');
35 | const animateAction = isRight ? 'fadeInRight' : 'fadeInLeft';
36 | document.querySelectorAll('.sidebar .animated').forEach((element, index) => {
37 | element.style.animationDelay = (100 * index) + 'ms';
38 | element.classList.remove(animateAction);
39 | setTimeout(() => {
40 | // Trigger a DOM reflow
41 | element.classList.add(animateAction);
42 | });
43 | });
44 | },
45 | hideSidebar() {
46 | document.body.classList.remove('sidebar-active');
47 | }
48 | };
49 | sidebarToggleMotion.init();
50 | });
51 |
--------------------------------------------------------------------------------
/source/js/third-party/addtoany.js:
--------------------------------------------------------------------------------
1 | /* global NexT */
2 |
3 | document.addEventListener('page:loaded', () => {
4 | NexT.utils.getScript('https://static.addtoany.com/menu/page.js', { condition: window.a2a })
5 | .then(() => {
6 | window.a2a.init();
7 | });
8 | });
9 |
--------------------------------------------------------------------------------
/source/js/third-party/analytics/baidu-analytics.js:
--------------------------------------------------------------------------------
1 | /* global _hmt */
2 |
3 | if (!window._hmt) window._hmt = [];
4 |
5 | document.addEventListener('pjax:success', () => {
6 | _hmt.push(['_trackPageview', location.pathname]);
7 | });
8 |
--------------------------------------------------------------------------------
/source/js/third-party/analytics/google-analytics.js:
--------------------------------------------------------------------------------
1 | /* global CONFIG, dataLayer, gtag */
2 |
3 | if (!CONFIG.google_analytics.only_pageview) {
4 | if (CONFIG.hostname === location.hostname) {
5 | window.dataLayer = window.dataLayer || [];
6 | window.gtag = function() {
7 | dataLayer.push(arguments);
8 | };
9 | gtag('js', new Date());
10 | gtag('config', CONFIG.google_analytics.tracking_id);
11 |
12 | document.addEventListener('pjax:success', () => {
13 | gtag('event', 'page_view', {
14 | page_location: location.href,
15 | page_path : location.pathname,
16 | page_title : document.title
17 | });
18 | });
19 | }
20 | } else {
21 | const sendPageView = () => {
22 | if (CONFIG.hostname !== location.hostname) return;
23 | const uid = localStorage.getItem('uid') || (Math.random() + '.' + Math.random());
24 | localStorage.setItem('uid', uid);
25 | fetch(
26 | 'https://www.google-analytics.com/mp/collect?' + new URLSearchParams({
27 | api_secret : CONFIG.google_analytics.measure_protocol_api_secret,
28 | measurement_id: CONFIG.google_analytics.tracking_id
29 | }),
30 | {
31 | method : 'POST',
32 | headers: {
33 | 'Content-Type': 'application/json'
34 | },
35 | body: JSON.stringify({
36 | client_id: uid,
37 | events : [
38 | {
39 | name : 'page_view',
40 | params: {
41 | page_location: location.href,
42 | page_title : document.title
43 | }
44 | }
45 | ]
46 | }),
47 | mode: 'no-cors'
48 | }
49 | );
50 | };
51 | document.addEventListener('pjax:complete', sendPageView);
52 | sendPageView();
53 | }
54 |
--------------------------------------------------------------------------------
/source/js/third-party/analytics/growingio.js:
--------------------------------------------------------------------------------
1 | /* global CONFIG, gio */
2 |
3 | if (!window.gio) {
4 | window.gio = function() {
5 | (window.gio.q = window.gio.q || []).push(arguments);
6 | };
7 | }
8 |
9 | gio('init', `${CONFIG.growingio_analytics}`, {});
10 | gio('send');
11 |
--------------------------------------------------------------------------------
/source/js/third-party/analytics/matomo.js:
--------------------------------------------------------------------------------
1 | /* global CONFIG */
2 |
3 | if (CONFIG.matomo.enable) {
4 | window._paq = window._paq || [];
5 | const _paq = window._paq;
6 |
7 | /* tracker methods like "setCustomDimension" should be called before "trackPageView" */
8 | _paq.push(['trackPageView']);
9 | _paq.push(['enableLinkTracking']);
10 | const u = CONFIG.matomo.server_url;
11 | _paq.push(['setTrackerUrl', u + 'matomo.php']);
12 | _paq.push(['setSiteId', CONFIG.matomo.site_id]);
13 | const d = document;
14 | const g = d.createElement('script');
15 | const s = d.getElementsByTagName('script')[0];
16 | g.async = true;
17 | g.src = u + 'matomo.js';
18 | s.parentNode.insertBefore(g, s);
19 | }
20 |
--------------------------------------------------------------------------------
/source/js/third-party/chat/chatra.js:
--------------------------------------------------------------------------------
1 | /* global CONFIG, Chatra */
2 |
3 | (function() {
4 | if (CONFIG.chatra.embed) {
5 | window.ChatraSetup = {
6 | mode : 'frame',
7 | injectTo: CONFIG.chatra.embed
8 | };
9 | }
10 |
11 | window.ChatraID = CONFIG.chatra.id;
12 |
13 | const chatButton = document.querySelector('.sidebar-button button');
14 | if (chatButton) {
15 | chatButton.addEventListener('click', () => {
16 | Chatra('openChat', true);
17 | });
18 | }
19 | })();
20 |
--------------------------------------------------------------------------------
/source/js/third-party/chat/tidio.js:
--------------------------------------------------------------------------------
1 | /* global tidioChatApi */
2 |
3 | (function() {
4 | const chatButton = document.querySelector('.sidebar-button button');
5 | if (chatButton) {
6 | chatButton.addEventListener('click', () => {
7 | tidioChatApi.open();
8 | });
9 | }
10 | })();
11 |
--------------------------------------------------------------------------------
/source/js/third-party/comments/changyan.js:
--------------------------------------------------------------------------------
1 | /* global NexT, CONFIG */
2 |
3 | document.addEventListener('page:loaded', () => {
4 | const { appid, appkey } = CONFIG.changyan;
5 | const mainJs = 'https://cy-cdn.kuaizhan.com/upload/changyan.js';
6 | const countJs = `https://cy-cdn.kuaizhan.com/upload/plugins/plugins.list.count.js?clientId=${appid}`;
7 |
8 | // Get the number of comments
9 | setTimeout(() => {
10 | return NexT.utils.getScript(countJs, {
11 | attributes: {
12 | async: true,
13 | id : 'cy_cmt_num'
14 | }
15 | });
16 | }, 0);
17 |
18 | // When scroll to comment section
19 | if (CONFIG.page.comments && !CONFIG.page.isHome) {
20 | NexT.utils.loadComments('#SOHUCS')
21 | .then(() => {
22 | return NexT.utils.getScript(mainJs, {
23 | attributes: {
24 | async: true
25 | }
26 | });
27 | })
28 | .then(() => {
29 | window.changyan.api.config({
30 | appid,
31 | conf: appkey
32 | });
33 | })
34 | .catch(error => {
35 | // eslint-disable-next-line no-console
36 | console.error('Failed to load Changyan', error);
37 | });
38 | }
39 | });
40 |
--------------------------------------------------------------------------------
/source/js/third-party/comments/disqus.js:
--------------------------------------------------------------------------------
1 | /* global NexT, CONFIG, DISQUS */
2 |
3 | document.addEventListener('page:loaded', () => {
4 |
5 | if (CONFIG.disqus.count) {
6 | if (window.DISQUSWIDGETS) {
7 | window.DISQUSWIDGETS.getCount({ reset: true });
8 | } else {
9 | // Defer loading until the whole page loading is completed
10 | NexT.utils.getScript(`https://${CONFIG.disqus.shortname}.disqus.com/count.js`, {
11 | attributes: { id: 'dsq-count-scr', defer: true }
12 | });
13 | }
14 | }
15 |
16 | if (CONFIG.page.comments) {
17 | // `disqus_config` should be a global variable
18 | // See https://help.disqus.com/en/articles/1717084-javascript-configuration-variables
19 | window.disqus_config = function() {
20 | this.page.url = CONFIG.page.permalink;
21 | this.page.identifier = CONFIG.page.path;
22 | this.page.title = CONFIG.page.title;
23 | if (CONFIG.disqus.i18n.disqus !== 'disqus') {
24 | this.language = CONFIG.disqus.i18n.disqus;
25 | }
26 | };
27 | NexT.utils.loadComments('#disqus_thread').then(() => {
28 | if (window.DISQUS) {
29 | DISQUS.reset({
30 | reload: true,
31 | config: window.disqus_config
32 | });
33 | } else {
34 | NexT.utils.getScript(`https://${CONFIG.disqus.shortname}.disqus.com/embed.js`, {
35 | attributes: { dataset: { timestamp: '' + +new Date() } }
36 | });
37 | }
38 | });
39 | }
40 |
41 | });
42 |
--------------------------------------------------------------------------------
/source/js/third-party/comments/disqusjs.js:
--------------------------------------------------------------------------------
1 | /* global NexT, CONFIG, DisqusJS */
2 |
3 | document.addEventListener('page:loaded', () => {
4 | if (!CONFIG.page.comments) return;
5 |
6 | NexT.utils.loadComments('#disqus_thread')
7 | .then(() => NexT.utils.getScript(CONFIG.disqusjs.js, { condition: window.DisqusJS }))
8 | .then(() => {
9 | window.dsqjs = new DisqusJS({
10 | api : CONFIG.disqusjs.api || 'https://disqus.com/api/',
11 | apikey : CONFIG.disqusjs.apikey,
12 | shortname : CONFIG.disqusjs.shortname,
13 | url : CONFIG.page.permalink,
14 | identifier: CONFIG.page.path,
15 | title : CONFIG.page.title
16 | });
17 | window.dsqjs.render(document.querySelector('.disqusjs-container'));
18 | });
19 | });
20 |
21 | document.addEventListener('pjax:send', () => {
22 | if (window.dsqjs) window.dsqjs.destroy();
23 | });
24 |
--------------------------------------------------------------------------------
/source/js/third-party/comments/gitalk.js:
--------------------------------------------------------------------------------
1 | /* global NexT, CONFIG, Gitalk */
2 |
3 | document.addEventListener('page:loaded', () => {
4 | if (!CONFIG.page.comments) return;
5 |
6 | NexT.utils.loadComments('.gitalk-container')
7 | .then(() => NexT.utils.getScript(CONFIG.gitalk.js, {
8 | condition: window.Gitalk
9 | }))
10 | .then(() => {
11 | const gitalk = new Gitalk({
12 | clientID : CONFIG.gitalk.client_id,
13 | clientSecret : CONFIG.gitalk.client_secret,
14 | repo : CONFIG.gitalk.repo,
15 | owner : CONFIG.gitalk.github_id,
16 | admin : [CONFIG.gitalk.admin_user],
17 | id : CONFIG.gitalk.path_md5,
18 | proxy : CONFIG.gitalk.proxy,
19 | language : CONFIG.gitalk.language || window.navigator.language,
20 | distractionFreeMode: CONFIG.gitalk.distraction_free_mode
21 | });
22 | gitalk.render(document.querySelector('.gitalk-container'));
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/source/js/third-party/comments/isso.js:
--------------------------------------------------------------------------------
1 | /* global NexT, CONFIG */
2 |
3 | document.addEventListener('page:loaded', () => {
4 | if (!CONFIG.page.comments) return;
5 |
6 | NexT.utils.loadComments('#isso-thread')
7 | .then(() => NexT.utils.getScript(`${CONFIG.isso}js/embed.min.js`, {
8 | attributes: {
9 | dataset: {
10 | isso: `${CONFIG.isso}`
11 | }
12 | },
13 | parentNode: document.querySelector('#isso-thread')
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/source/js/third-party/comments/livere.js:
--------------------------------------------------------------------------------
1 | /* global NexT, CONFIG, LivereTower */
2 |
3 | document.addEventListener('page:loaded', () => {
4 | if (!CONFIG.page.comments) return;
5 |
6 | NexT.utils.loadComments('#lv-container').then(() => {
7 | window.livereOptions = {
8 | refer: CONFIG.page.path.replace(/index\.html$/, '')
9 | };
10 |
11 | if (typeof LivereTower === 'function') return;
12 |
13 | NexT.utils.getScript('https://cdn-city.livere.com/js/embed.dist.js', {
14 | attributes: {
15 | async: true
16 | }
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/source/js/third-party/comments/utterances.js:
--------------------------------------------------------------------------------
1 | /* global NexT, CONFIG */
2 |
3 | document.addEventListener('page:loaded', () => {
4 | if (!CONFIG.page.comments) return;
5 |
6 | NexT.utils.loadComments('.utterances-container')
7 | .then(() => NexT.utils.getScript('https://utteranc.es/client.js', {
8 | attributes: {
9 | async : true,
10 | crossOrigin : 'anonymous',
11 | 'repo' : CONFIG.utterances.repo,
12 | 'issue-term': CONFIG.utterances.issue_term,
13 | 'theme' : CONFIG.utterances.theme
14 | },
15 | parentNode: document.querySelector('.utterances-container')
16 | }));
17 | });
18 |
--------------------------------------------------------------------------------
/source/js/third-party/fancybox.js:
--------------------------------------------------------------------------------
1 | /* global Fancybox */
2 |
3 | document.addEventListener('page:loaded', () => {
4 |
5 | /**
6 | * Wrap images with fancybox.
7 | */
8 | document.querySelectorAll('.post-body :not(a) > img, .post-body > img').forEach(image => {
9 | const imageLink = image.dataset.src || image.src;
10 | const imageWrapLink = document.createElement('a');
11 | imageWrapLink.classList.add('fancybox');
12 | imageWrapLink.href = imageLink;
13 | imageWrapLink.setAttribute('itemscope', '');
14 | imageWrapLink.setAttribute('itemtype', 'http://schema.org/ImageObject');
15 | imageWrapLink.setAttribute('itemprop', 'url');
16 |
17 | let dataFancybox = 'default';
18 | if (image.closest('.post-gallery') !== null) {
19 | dataFancybox = 'gallery';
20 | } else if (image.closest('.group-picture') !== null) {
21 | dataFancybox = 'group';
22 | }
23 | imageWrapLink.dataset.fancybox = dataFancybox;
24 |
25 | const imageTitle = image.title || image.alt;
26 | if (imageTitle) {
27 | imageWrapLink.title = imageTitle;
28 | // Make sure img captions will show correctly in fancybox
29 | imageWrapLink.dataset.caption = imageTitle;
30 | }
31 | image.wrap(imageWrapLink);
32 | });
33 |
34 | Fancybox.bind('[data-fancybox]');
35 | });
36 |
--------------------------------------------------------------------------------
/source/js/third-party/math/katex.js:
--------------------------------------------------------------------------------
1 | /* global NexT, CONFIG */
2 |
3 | document.addEventListener('page:loaded', () => {
4 | if (!CONFIG.enableMath) return;
5 |
6 | NexT.utils.getScript(CONFIG.katex.copy_tex_js).catch(() => {});
7 | });
8 |
--------------------------------------------------------------------------------
/source/js/third-party/math/mathjax.js:
--------------------------------------------------------------------------------
1 | /* global NexT, CONFIG, MathJax */
2 |
3 | document.addEventListener('page:loaded', () => {
4 | if (!CONFIG.enableMath) return;
5 |
6 | if (typeof MathJax === 'undefined') {
7 | window.MathJax = {
8 | tex: {
9 | inlineMath: { '[+]': [['$', '$']] },
10 | tags : CONFIG.mathjax.tags
11 | },
12 | options: {
13 | renderActions: {
14 | insertedScript: [200, () => {
15 | document.querySelectorAll('mjx-container').forEach(node => {
16 | const target = node.parentNode;
17 | if (target.nodeName.toLowerCase() === 'li') {
18 | target.parentNode.classList.add('has-jax');
19 | }
20 | });
21 | }, '', false]
22 | }
23 | }
24 | };
25 | NexT.utils.getScript(CONFIG.mathjax.js, {
26 | attributes: {
27 | defer: true
28 | }
29 | });
30 | } else {
31 | MathJax.startup.document.state(0);
32 | MathJax.typesetClear();
33 | MathJax.texReset();
34 | MathJax.typesetPromise();
35 | }
36 | });
37 |
--------------------------------------------------------------------------------
/source/js/third-party/pace.js:
--------------------------------------------------------------------------------
1 | /* global Pace */
2 |
3 | Pace.options.restartOnPushState = false;
4 |
5 | document.addEventListener('pjax:send', () => {
6 | Pace.restart();
7 | });
8 |
--------------------------------------------------------------------------------
/source/js/third-party/quicklink.js:
--------------------------------------------------------------------------------
1 | /* global CONFIG, quicklink */
2 |
3 | (function() {
4 | if (typeof CONFIG.quicklink.ignores === 'string') {
5 | const ignoresStr = `[${CONFIG.quicklink.ignores}]`;
6 | CONFIG.quicklink.ignores = JSON.parse(ignoresStr);
7 | }
8 |
9 | let resetFn = null;
10 |
11 | const onRefresh = () => {
12 | if (resetFn) resetFn();
13 | if (!CONFIG.quicklink.enable) return;
14 |
15 | let ignoresArr = CONFIG.quicklink.ignores || [];
16 | if (!Array.isArray(ignoresArr)) {
17 | ignoresArr = [ignoresArr];
18 | }
19 |
20 | resetFn = quicklink.listen({
21 | timeout : CONFIG.quicklink.timeout,
22 | priority: CONFIG.quicklink.priority,
23 | ignores : [
24 | uri => uri.includes('#'),
25 | uri => uri === CONFIG.quicklink.url,
26 | ...ignoresArr
27 | ]
28 | });
29 | };
30 |
31 | if (CONFIG.quicklink.delay) {
32 | window.addEventListener('load', onRefresh);
33 | document.addEventListener('pjax:success', onRefresh);
34 | } else {
35 | document.addEventListener('page:loaded', onRefresh);
36 | }
37 | })();
38 |
--------------------------------------------------------------------------------
/source/js/third-party/statistics/firestore.js:
--------------------------------------------------------------------------------
1 | /* global CONFIG, firebase */
2 |
3 | firebase.initializeApp({
4 | apiKey : CONFIG.firestore.apiKey,
5 | projectId: CONFIG.firestore.projectId
6 | });
7 |
8 | (function() {
9 | const getCount = (doc, increaseCount) => {
10 | // IncreaseCount will be false when not in article page
11 | return doc.get().then(d => {
12 | // Has no data, initialize count
13 | let count = d.exists ? d.data().count : 0;
14 | // If first view this article
15 | if (increaseCount) {
16 | // Increase count
17 | count++;
18 | doc.set({
19 | count
20 | });
21 | }
22 | return count;
23 | });
24 | };
25 |
26 | const db = firebase.firestore();
27 | const articles = db.collection(CONFIG.firestore.collection);
28 |
29 | document.addEventListener('page:loaded', () => {
30 |
31 | if (CONFIG.page.isPost) {
32 | // Fix issue #118
33 | // https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent
34 | const title = document.querySelector('.post-title').textContent.trim();
35 | const doc = articles.doc(title);
36 | let increaseCount = CONFIG.hostname === location.hostname;
37 | if (localStorage.getItem(title)) {
38 | increaseCount = false;
39 | } else {
40 | // Mark as visited
41 | localStorage.setItem(title, true);
42 | }
43 | getCount(doc, increaseCount).then(count => {
44 | document.querySelector('.firestore-visitors-count').innerText = count;
45 | });
46 | } else if (CONFIG.page.isHome) {
47 | const promises = [...document.querySelectorAll('.post-title')].map(element => {
48 | const title = element.textContent.trim();
49 | const doc = articles.doc(title);
50 | return getCount(doc);
51 | });
52 | Promise.all(promises).then(counts => {
53 | const metas = document.querySelectorAll('.firestore-visitors-count');
54 | counts.forEach((val, idx) => {
55 | metas[idx].innerText = val;
56 | });
57 | });
58 | }
59 | });
60 | })();
61 |
--------------------------------------------------------------------------------
/source/js/third-party/tags/mermaid.js:
--------------------------------------------------------------------------------
1 | /* global NexT, CONFIG, mermaid */
2 |
3 | document.addEventListener('page:loaded', () => {
4 | const mermaidElements = document.querySelectorAll('pre > .mermaid');
5 | if (mermaidElements.length) {
6 | NexT.utils.getScript(CONFIG.mermaid.js, {
7 | condition: window.mermaid
8 | }).then(() => {
9 | mermaidElements.forEach(element => {
10 | const box = document.createElement('div');
11 | box.className = 'code-container';
12 | const newElement = document.createElement('div');
13 | newElement.innerHTML = element.innerHTML;
14 | newElement.className = 'mermaid';
15 | box.appendChild(newElement);
16 | if (CONFIG.codeblock.copy_button.enable) {
17 | NexT.utils.registerCopyButton(box, box, element.textContent);
18 | }
19 | const parent = element.parentNode;
20 | parent.parentNode.replaceChild(box, parent);
21 | });
22 | mermaid.initialize({
23 | theme : CONFIG.darkmode && window.matchMedia('(prefers-color-scheme: dark)').matches ? CONFIG.mermaid.theme.dark : CONFIG.mermaid.theme.light,
24 | logLevel : 4,
25 | flowchart: { curve: 'linear' },
26 | gantt : { axisFormat: '%m/%d/%Y' },
27 | sequence : { actorMargin: 50 }
28 | });
29 | mermaid.run();
30 | });
31 | }
32 | });
33 |
--------------------------------------------------------------------------------
/source/js/third-party/tags/pdf.js:
--------------------------------------------------------------------------------
1 | /* global NexT, CONFIG, PDFObject */
2 |
3 | document.addEventListener('page:loaded', () => {
4 | if (document.querySelectorAll('.pdf-container').length) {
5 | NexT.utils.getScript(CONFIG.pdf.object_url, {
6 | condition: window.PDFObject
7 | }).then(() => {
8 | document.querySelectorAll('.pdf-container').forEach(element => {
9 | PDFObject.embed(element.dataset.target, element, {
10 | pdfOpenParams: {
11 | navpanes : 0,
12 | toolbar : 0,
13 | statusbar: 0,
14 | pagemode : 'thumbs',
15 | view : 'FitH'
16 | },
17 | PDFJS_URL: CONFIG.pdf.url,
18 | height : element.dataset.height
19 | });
20 | });
21 | });
22 | }
23 | });
24 |
--------------------------------------------------------------------------------
/source/js/third-party/tags/wavedrom.js:
--------------------------------------------------------------------------------
1 | /* global NexT, CONFIG, WaveDrom */
2 |
3 | document.addEventListener('page:loaded', () => {
4 | NexT.utils.getScript(CONFIG.wavedrom.js, {
5 | condition: window.WaveDrom
6 | }).then(() => {
7 | NexT.utils.getScript(CONFIG.wavedrom_skin.js, {
8 | condition: window.WaveSkin
9 | }).then(() => {
10 | WaveDrom.ProcessAll();
11 | });
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/test/helpers/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Helpers', () => {
4 | require('./font');
5 | require('./next-url');
6 | });
7 |
--------------------------------------------------------------------------------
/test/helpers/next-url.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Hexo = require('hexo');
4 | const hexo = new Hexo();
5 |
6 | function btoa(str) {
7 | return Buffer.from(str).toString('base64');
8 | }
9 |
10 | describe('next-url', () => {
11 | const nextUrl = require('../../scripts/helpers/next-url').bind(hexo);
12 |
13 | before(() => {
14 | hexo.config.url = 'https://example.com';
15 | hexo.url_for = require('hexo/dist/plugins/helper/url_for').bind(hexo);
16 | });
17 |
18 | it('text', () => {
19 | nextUrl('/child/', 'Text').should.eql('Text ');
20 | });
21 |
22 | it('icon', () => {
23 | nextUrl('/child/', ' ').should.eql(' ');
24 | });
25 |
26 | it('class', () => {
27 | nextUrl('/child/', 'Text', { class: 'theme-link' }).should.eql('Text ');
28 | });
29 |
30 | it('external', () => {
31 | nextUrl('https://theme-next.js.org', 'Text').should.eql('Text ');
32 | });
33 |
34 | it('decodeURI', () => {
35 | (() => nextUrl('https://theme-next.js.org', 'A % B')).should.not.throw();
36 | });
37 |
38 | it('exturl enabled', () => {
39 | hexo.theme.exturl = true;
40 | const encoded = btoa('https://theme-next.js.org');
41 | nextUrl('https://theme-next.js.org', 'Text').should.eql(`Text `);
42 | });
43 |
44 | it('class with exturl enabled', () => {
45 | hexo.theme.exturl = true;
46 | const encoded = btoa('https://theme-next.js.org');
47 | nextUrl('https://theme-next.js.org', 'Text', { class: 'theme-link' }).should.eql(`Text `);
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('chai').should();
4 |
5 | describe('NexT', () => {
6 | require('./helpers');
7 | require('./tags');
8 | require('./validate');
9 | });
10 |
--------------------------------------------------------------------------------
/test/tags/button.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Hexo = require('hexo');
4 | const hexo = new Hexo();
5 |
6 | describe('button', () => {
7 | const postButton = require('../../scripts/tags/button')(hexo);
8 |
9 | it('only url', () => {
10 | postButton(['#']).should.eql(' ');
11 | });
12 |
13 | it('url and text', () => {
14 | postButton('#, Hello world'.split(' ')).should.eql('Hello world ');
15 | });
16 |
17 | it('url and icon (Font Awesome 4)', () => {
18 | postButton('#,, home fa-5x'.split(' ')).should.eql(' ');
19 | });
20 |
21 | it('url and icon', () => {
22 | postButton('#,, fab fa-fort-awesome fa-5x'.split(' ')).should.eql(' ');
23 | });
24 |
25 | it('url, text and title', () => {
26 | postButton('#, Hello world,, Title'.split(' ')).should.eql('Hello world ');
27 | });
28 |
29 | it('url, text, icon and title', () => {
30 | postButton('#, Hello world, home, Title'.split(' ')).should.eql(' Hello world ');
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/test/tags/caniuse.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Hexo = require('hexo');
4 | const hexo = new Hexo();
5 |
6 | describe('caniuse', () => {
7 | const caniUse = require('../../scripts/tags/caniuse')(hexo);
8 |
9 | it('only feature', () => {
10 | caniUse(['loading-lazy-attr']).should.eql('');
11 | });
12 |
13 | it('feature and periods', () => {
14 | caniUse('fetch @ future_3,future_2,future_1'.split(' ')).should.eql('');
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/test/tags/center-quote.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Hexo = require('hexo');
4 | const hexo = new Hexo();
5 |
6 | const content = 'Test **Bold** *Italic*';
7 | const result = 'Test Bold Italic
';
8 |
9 | describe('center-quote', () => {
10 | const centerQuote = require('../../scripts/tags/center-quote')(hexo);
11 |
12 | before(() => hexo.init().then(() => hexo.loadPlugin(require.resolve('hexo-renderer-marked'))));
13 |
14 | it('markdown content', () => {
15 | centerQuote([], content).should.eql(`
16 | ${result}
17 |
18 | `);
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/test/tags/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Tags', () => {
4 | require('./button');
5 | require('./caniuse');
6 | require('./center-quote');
7 | require('./group-pictures');
8 | require('./label');
9 | require('./link-grid');
10 | require('./mermaid');
11 | require('./note');
12 | require('./pdf');
13 | require('./tabs');
14 | require('./video');
15 | });
16 |
--------------------------------------------------------------------------------
/test/tags/label.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Hexo = require('hexo');
4 | const hexo = new Hexo();
5 |
6 | describe('label', () => {
7 | const postLabel = require('../../scripts/tags/label')(hexo);
8 |
9 | it('only text', () => {
10 | postLabel('@Hello world'.split(' ')).should.eql('Hello world ');
11 | });
12 |
13 | it('classes and text', () => {
14 | postLabel('primary@Hello world'.split(' ')).should.eql('Hello world ');
15 | });
16 |
17 | it('classes and text with space', () => {
18 | postLabel('primary @Hello world'.split(' ')).should.eql('Hello world ');
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/test/tags/link-grid.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | const result = `
5 |
6 |
Theme NexT
Stay Simple. Stay NexT.
7 |
8 |
9 |
10 |
Theme NexT
Stay Simple. Stay NexT.
11 |
12 |
`;
13 |
14 | describe('link-grid', () => {
15 | const linkGrid = require('../../scripts/tags/link-grid');
16 |
17 | it('default', () => {
18 | linkGrid([], `
19 | Theme NexT | https://theme-next.js.org/ | Stay Simple. Stay NexT. | /images/sample.png
20 | Theme NexT | https://theme-next.js.org/ | Stay Simple. Stay NexT. | /images/sample.png`).should.eql(result);
21 | });
22 |
23 | it('comment', () => {
24 | linkGrid([], `
25 | Theme NexT | https://theme-next.js.org/ | Stay Simple. Stay NexT. | /images/sample.png
26 | Theme NexT | https://theme-next.js.org/ | Stay Simple. Stay NexT. | /images/sample.png
27 | % Theme NexT | https://theme-next.js.org/ | Stay Simple. Stay NexT. | /images/sample.png`).should.eql(result);
28 | });
29 |
30 | it('default image', () => {
31 | linkGrid(['/images/sample.png'], `
32 | Theme NexT | https://theme-next.js.org/ | Stay Simple. Stay NexT. |
33 | Theme NexT | https://theme-next.js.org/ | Stay Simple. Stay NexT. |`).should.eql(result);
34 | });
35 |
36 | it('custom delimiter', () => {
37 | linkGrid(['/images/sample.png', ','], `
38 | Theme NexT , https://theme-next.js.org/ , Stay Simple. Stay NexT. , /images/sample.png
39 | Theme NexT , https://theme-next.js.org/ , Stay Simple. Stay NexT. , /images/sample.png`).should.eql(result);
40 | });
41 |
42 | it('custom delimiter and comment', () => {
43 | linkGrid(['/images/sample.png', ',', '#'], `
44 | Theme NexT , https://theme-next.js.org/ , Stay Simple. Stay NexT. , /images/sample.png
45 | Theme NexT , https://theme-next.js.org/ , Stay Simple. Stay NexT. , /images/sample.png
46 | # Theme NexT , https://theme-next.js.org/ , Stay Simple. Stay NexT. , /images/sample.png`).should.eql(result);
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/test/tags/mermaid.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | const { escapeHTML } = require('hexo-util');
5 |
6 | const result = `A[Hard] -->|Text| B(Round)
7 | B --> C{Decision}
8 | C -->|One| D[Result 1]
9 | C -->|Two| E[Result 2]`;
10 |
11 | describe('mermaid', () => {
12 | const mermaid = require('../../scripts/tags/mermaid');
13 |
14 | it('default', () => {
15 | mermaid(['graph', 'TD'], result).should.eql(`
16 |
17 | graph TD
18 | ${escapeHTML(result)}
19 |
20 | `);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/test/tags/note.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Hexo = require('hexo');
4 | const hexo = new Hexo();
5 |
6 | const content = 'Test **Bold** *Italic*';
7 | const result = 'Test Bold Italic
';
8 | const args = 'This is a *summary*'.split(' ');
9 | const summary = 'This is a summary ';
10 |
11 | describe('note', () => {
12 | const postNote = require('../../scripts/tags/note')(hexo);
13 |
14 | before(() => hexo.init().then(() => hexo.loadPlugin(require.resolve('hexo-renderer-marked'))));
15 |
16 | it('only text', () => {
17 | postNote([], content).should.eql(`
${result}
18 |
`);
19 | });
20 |
21 | it('classes and text', () => {
22 | postNote(['primary'], content).should.eql(`${result}
23 |
`);
24 | });
25 |
26 | it('classes, no-icon and text', () => {
27 | postNote(['primary', 'no-icon'], content).should.eql(`${result}
28 |
`);
29 | });
30 |
31 | it('summary and text', () => {
32 | postNote(args, content).should.eql(`${summary}
33 |
34 | ${result}
35 |
36 | `);
37 | });
38 |
39 | it('classes, summary and text', () => {
40 | postNote(['primary'].concat(args), content).should.eql(`${summary}
41 |
42 | ${result}
43 |
44 | `);
45 | });
46 |
47 | it('classes, no-icon, summary and text', () => {
48 | postNote(['primary', 'no-icon'].concat(args), content).should.eql(`${summary}
49 |
50 | ${result}
51 |
52 | `);
53 | });
54 |
55 | it('keywords in summary', () => {
56 | postNote(['It\'s', 'danger'], content).should.eql(`It’s danger
57 |
58 | ${result}
59 |
60 | `);
61 | });
62 | });
63 |
--------------------------------------------------------------------------------
/test/tags/pdf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Hexo = require('hexo');
4 | const hexo = new Hexo();
5 |
6 | describe('pdf', () => {
7 | const pdf = require('../../scripts/tags/pdf')(hexo);
8 |
9 | before(() => {
10 | hexo.theme.config.pdf = {
11 | height: '500px'
12 | };
13 | });
14 |
15 | it('default', () => {
16 | pdf(['https://example.com/sample.pdf']).should.eql('
');
17 | });
18 |
19 | it('custom height', () => {
20 | pdf(['https://example.com/sample.pdf', '1000px']).should.eql('
');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/test/tags/video.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | describe('video', () => {
5 | const postVideo = require('../../scripts/tags/video');
6 |
7 | it('default', () => {
8 | postVideo(['https://example.com/sample.mp4']).should.eql(' ');
9 | });
10 |
11 | it('poster', () => {
12 | postVideo(['https://example.com/sample.mp4', 'https://example.com/sample.jpg']).should.eql(' ');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/test/validate/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const path = require('path');
5 | const yaml = require('js-yaml');
6 |
7 | describe('Validate', () => {
8 | it('config', () => {
9 | const themeConfig = fs.readFileSync(path.join(__dirname, '../../_config.yml'));
10 | (() => {
11 | yaml.load(themeConfig);
12 | }).should.not.throw();
13 | });
14 |
15 | it('vendors', () => {
16 | const vendorsFile = fs.readFileSync(path.join(__dirname, '../../_vendors.yml'));
17 | (() => {
18 | yaml.load(vendorsFile);
19 | }).should.not.throw();
20 | });
21 |
22 | it('language', () => {
23 | const languagesPath = path.join(__dirname, '../../languages');
24 | (() => {
25 | fs.readdirSync(languagesPath).forEach(lang => {
26 | if (!lang.endsWith('.yml')) return;
27 | const languagePath = path.join(languagesPath, lang);
28 | yaml.load(fs.readFileSync(languagePath), {
29 | filename: path.relative(__dirname, languagePath)
30 | });
31 | });
32 | }).should.not.throw();
33 | });
34 | });
35 |
--------------------------------------------------------------------------------