[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\ncustom: ['https://buy.stripe.com/3cs6rP6YA91sbbG5kk'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Bug report\r\ndescription: Create a report to help us improve\r\ntitle: '[Bug]: '\r\n\r\nbody:\r\n  - type: markdown\r\n    attributes:\r\n      value: |\r\n        重要：請依照該模板來提交 \r\n        Important: Please follow the template to create a new issue\r\n\r\n  - type: input\r\n    id: butterfly-ver\r\n    attributes:\r\n      label: 使用的 Butterfly 版本？ | What version of Butterfly are you using?\r\n      description: 檢視主題的 package.json | Check the theme's package.json\r\n    validations:\r\n      required: true\r\n\r\n  - type: dropdown\r\n    id: modify\r\n    attributes:\r\n      label: 是否修改過主題文件？ | Has the theme files been modified?\r\n      options:\r\n        - 是 (Yes)\r\n        - 否 (No)\r\n    validations:\r\n      required: true\r\n\r\n  - type: dropdown\r\n    id: browser\r\n    attributes:\r\n      label: 使用的瀏覽器？ | What browser are you using?\r\n      options:\r\n        - Chrome\r\n        - Edge\r\n        - Safari\r\n        - Opera\r\n        - Other\r\n    validations:\r\n      required: true\r\n\r\n  - type: dropdown\r\n    id: platform\r\n    attributes:\r\n      label: 使用的系統？ | What operating system are you using?\r\n      options:\r\n        - Windows\r\n        - macOS \r\n        - Linux\r\n        - Android\r\n        - iOS\r\n        - Other\r\n    validations:\r\n      required: true\r\n\r\n  - type: textarea\r\n    id: dependencies\r\n    attributes:\r\n      label: 依賴插件 | Package dependencies information\r\n      description: 在 Hexo 根目錄下執行 `npm ls --depth 0` | Run `npm ls --depth 0` in Hexo root directory\r\n      render: Text\r\n    validations:\r\n      required: true\r\n\r\n  - type: textarea\r\n    id: description\r\n    attributes:\r\n      label: 問題描述 | Describe the bug\r\n      description: 請描述你的問題現象 | A clear and concise description of what the bug is.\r\n      placeholder: 請儘量提供截圖來定位問題 | If applicable, add screenshots to help explain your problem\r\n      value:\r\n    validations:\r\n      required: true\r\n\r\n  - type: input\r\n    id: website\r\n    attributes:\r\n      label: 出現問題的網站 | Website with the issue\r\n      description: 請提供可復現問題的網站地址 | Please provide a website URL where the problem can be reproduced.\r\n      placeholder: 請填寫具體的網址，不要填寫 localhost 網站 | Please provide a specific URL, do not use localhost. \r\n    validations:\r\n      required: true"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\r\ncontact_links:\r\n  - name: Questions about Butterfly\r\n    url: https://github.com/jerryc127/hexo-theme-butterfly/discussions\r\n    about: 一些使用問題請到 Discussion 詢問。 Please ask questions in Discussion.\r\n\r\n  - name: Butterfly Q&A\r\n    url: https://butterfly.js.org/posts/98d20436/\r\n    about: Butterfly Q&A\r\n\r\n  - name: Telegram\r\n    url: https://t.me/bu2fly\r\n    about: 'Official Telegram Group'\r\n\r\n  - name: QQ 群\r\n    url: https://jq.qq.com/?_wv=1027&k=KU9105XR\r\n    about: '群號 1070540070'\r\n\r\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "content": "name: Feature request\r\ndescription: Suggest an idea for this project\r\ntitle: '[Feature]: '\r\n\r\nbody:\r\n  - type: textarea\r\n    id: feature-request\r\n    attributes:\r\n      label: 想要的功能 | What feature do you want?\r\n      description: 請描述你需要的新功能 | A clear and concise description of what the feature is.\r\n      placeholder:\r\n      value:\r\n    validations:\r\n      require: true"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: npm publish\r\n\r\non:\r\n  release:\r\n    types: [created]\r\njobs:\r\n  build:\r\n    runs-on: ubuntu-latest\r\n    steps:\r\n    - uses: actions/checkout@v2\r\n    # Setup .npmrc file to publish to npm\r\n    - uses: actions/setup-node@v1\r\n      with:\r\n        node-version: '12.x'\r\n        registry-url: 'https://registry.npmjs.org'\r\n    - run: npm install\r\n    - run: npm publish\r\n      env:\r\n        NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}"
  },
  {
    "path": ".github/workflows/stale.yml",
    "content": "name: 'Close stale issues and PRs'\r\non:\r\n  schedule:\r\n    - cron: '30 1 * * *'\r\n\r\njobs:\r\n  stale:\r\n    runs-on: ubuntu-latest\r\n    steps:\r\n      - uses: actions/stale@v5\r\n        with:\r\n          days-before-issue-stale: 30\r\n          days-before-pr-stale: -1\r\n          days-before-close: 7\r\n          stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.'\r\n          close-pr-message: 'This issue has not seen any activity since it was marked stale. Closing.'\r\n          stale-issue-label: 'Stale'\r\n          exempt-issue-labels: 'pinned,bug,enhancement,documentation,Plan'\r\n          operations-per-run: 1000"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\nnode_modules/\n"
  },
  {
    "path": "LICENSE",
    "content": "\r\n                                 Apache License\r\n                           Version 2.0, January 2004\r\n                        http://www.apache.org/licenses/\r\n\r\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\r\n\r\n   1. Definitions.\r\n\r\n      \"License\" shall mean the terms and conditions for use, reproduction,\r\n      and distribution as defined by Sections 1 through 9 of this document.\r\n\r\n      \"Licensor\" shall mean the copyright owner or entity authorized by\r\n      the copyright owner that is granting the License.\r\n\r\n      \"Legal Entity\" shall mean the union of the acting entity and all\r\n      other entities that control, are controlled by, or are under common\r\n      control with that entity. For the purposes of this definition,\r\n      \"control\" means (i) the power, direct or indirect, to cause the\r\n      direction or management of such entity, whether by contract or\r\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\r\n      outstanding shares, or (iii) beneficial ownership of such entity.\r\n\r\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\r\n      exercising permissions granted by this License.\r\n\r\n      \"Source\" form shall mean the preferred form for making modifications,\r\n      including but not limited to software source code, documentation\r\n      source, and configuration files.\r\n\r\n      \"Object\" form shall mean any form resulting from mechanical\r\n      transformation or translation of a Source form, including but\r\n      not limited to compiled object code, generated documentation,\r\n      and conversions to other media types.\r\n\r\n      \"Work\" shall mean the work of authorship, whether in Source or\r\n      Object form, made available under the License, as indicated by a\r\n      copyright notice that is included in or attached to the work\r\n      (an example is provided in the Appendix below).\r\n\r\n      \"Derivative Works\" shall mean any work, whether in Source or Object\r\n      form, that is based on (or derived from) the Work and for which the\r\n      editorial revisions, annotations, elaborations, or other modifications\r\n      represent, as a whole, an original work of authorship. For the purposes\r\n      of this License, Derivative Works shall not include works that remain\r\n      separable from, or merely link (or bind by name) to the interfaces of,\r\n      the Work and Derivative Works thereof.\r\n\r\n      \"Contribution\" shall mean any work of authorship, including\r\n      the original version of the Work and any modifications or additions\r\n      to that Work or Derivative Works thereof, that is intentionally\r\n      submitted to Licensor for inclusion in the Work by the copyright owner\r\n      or by an individual or Legal Entity authorized to submit on behalf of\r\n      the copyright owner. For the purposes of this definition, \"submitted\"\r\n      means any form of electronic, verbal, or written communication sent\r\n      to the Licensor or its representatives, including but not limited to\r\n      communication on electronic mailing lists, source code control systems,\r\n      and issue tracking systems that are managed by, or on behalf of, the\r\n      Licensor for the purpose of discussing and improving the Work, but\r\n      excluding communication that is conspicuously marked or otherwise\r\n      designated in writing by the copyright owner as \"Not a Contribution.\"\r\n\r\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\r\n      on behalf of whom a Contribution has been received by Licensor and\r\n      subsequently incorporated within the Work.\r\n\r\n   2. Grant of Copyright License. Subject to the terms and conditions of\r\n      this License, each Contributor hereby grants to You a perpetual,\r\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r\n      copyright license to reproduce, prepare Derivative Works of,\r\n      publicly display, publicly perform, sublicense, and distribute the\r\n      Work and such Derivative Works in Source or Object form.\r\n\r\n   3. Grant of Patent License. Subject to the terms and conditions of\r\n      this License, each Contributor hereby grants to You a perpetual,\r\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r\n      (except as stated in this section) patent license to make, have made,\r\n      use, offer to sell, sell, import, and otherwise transfer the Work,\r\n      where such license applies only to those patent claims licensable\r\n      by such Contributor that are necessarily infringed by their\r\n      Contribution(s) alone or by combination of their Contribution(s)\r\n      with the Work to which such Contribution(s) was submitted. If You\r\n      institute patent litigation against any entity (including a\r\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\r\n      or a Contribution incorporated within the Work constitutes direct\r\n      or contributory patent infringement, then any patent licenses\r\n      granted to You under this License for that Work shall terminate\r\n      as of the date such litigation is filed.\r\n\r\n   4. Redistribution. You may reproduce and distribute copies of the\r\n      Work or Derivative Works thereof in any medium, with or without\r\n      modifications, and in Source or Object form, provided that You\r\n      meet the following conditions:\r\n\r\n      (a) You must give any other recipients of the Work or\r\n          Derivative Works a copy of this License; and\r\n\r\n      (b) You must cause any modified files to carry prominent notices\r\n          stating that You changed the files; and\r\n\r\n      (c) You must retain, in the Source form of any Derivative Works\r\n          that You distribute, all copyright, patent, trademark, and\r\n          attribution notices from the Source form of the Work,\r\n          excluding those notices that do not pertain to any part of\r\n          the Derivative Works; and\r\n\r\n      (d) If the Work includes a \"NOTICE\" text file as part of its\r\n          distribution, then any Derivative Works that You distribute must\r\n          include a readable copy of the attribution notices contained\r\n          within such NOTICE file, excluding those notices that do not\r\n          pertain to any part of the Derivative Works, in at least one\r\n          of the following places: within a NOTICE text file distributed\r\n          as part of the Derivative Works; within the Source form or\r\n          documentation, if provided along with the Derivative Works; or,\r\n          within a display generated by the Derivative Works, if and\r\n          wherever such third-party notices normally appear. The contents\r\n          of the NOTICE file are for informational purposes only and\r\n          do not modify the License. You may add Your own attribution\r\n          notices within Derivative Works that You distribute, alongside\r\n          or as an addendum to the NOTICE text from the Work, provided\r\n          that such additional attribution notices cannot be construed\r\n          as modifying the License.\r\n\r\n      You may add Your own copyright statement to Your modifications and\r\n      may provide additional or different license terms and conditions\r\n      for use, reproduction, or distribution of Your modifications, or\r\n      for any such Derivative Works as a whole, provided Your use,\r\n      reproduction, and distribution of the Work otherwise complies with\r\n      the conditions stated in this License.\r\n\r\n   5. Submission of Contributions. Unless You explicitly state otherwise,\r\n      any Contribution intentionally submitted for inclusion in the Work\r\n      by You to the Licensor shall be under the terms and conditions of\r\n      this License, without any additional terms or conditions.\r\n      Notwithstanding the above, nothing herein shall supersede or modify\r\n      the terms of any separate license agreement you may have executed\r\n      with Licensor regarding such Contributions.\r\n\r\n   6. Trademarks. This License does not grant permission to use the trade\r\n      names, trademarks, service marks, or product names of the Licensor,\r\n      except as required for reasonable and customary use in describing the\r\n      origin of the Work and reproducing the content of the NOTICE file.\r\n\r\n   7. Disclaimer of Warranty. Unless required by applicable law or\r\n      agreed to in writing, Licensor provides the Work (and each\r\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\r\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\r\n      implied, including, without limitation, any warranties or conditions\r\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\r\n      PARTICULAR PURPOSE. You are solely responsible for determining the\r\n      appropriateness of using or redistributing the Work and assume any\r\n      risks associated with Your exercise of permissions under this License.\r\n\r\n   8. Limitation of Liability. In no event and under no legal theory,\r\n      whether in tort (including negligence), contract, or otherwise,\r\n      unless required by applicable law (such as deliberate and grossly\r\n      negligent acts) or agreed to in writing, shall any Contributor be\r\n      liable to You for damages, including any direct, indirect, special,\r\n      incidental, or consequential damages of any character arising as a\r\n      result of this License or out of the use or inability to use the\r\n      Work (including but not limited to damages for loss of goodwill,\r\n      work stoppage, computer failure or malfunction, or any and all\r\n      other commercial damages or losses), even if such Contributor\r\n      has been advised of the possibility of such damages.\r\n\r\n   9. Accepting Warranty or Additional Liability. While redistributing\r\n      the Work or Derivative Works thereof, You may choose to offer,\r\n      and charge a fee for, acceptance of support, warranty, indemnity,\r\n      or other liability obligations and/or rights consistent with this\r\n      License. However, in accepting such obligations, You may act only\r\n      on Your own behalf and on Your sole responsibility, not on behalf\r\n      of any other Contributor, and only if You agree to indemnify,\r\n      defend, and hold each Contributor harmless for any liability\r\n      incurred by, or claims asserted against, such Contributor by reason\r\n      of your accepting any such warranty or additional liability.\r\n\r\n   END OF TERMS AND CONDITIONS\r\n\r\n   APPENDIX: How to apply the Apache License to your work.\r\n\r\n      To apply the Apache License to your work, attach the following\r\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\r\n      replaced with your own identifying information. (Don't include\r\n      the brackets!)  The text should be enclosed in the appropriate\r\n      comment syntax for the file format. We also recommend that a\r\n      file or class name and description of purpose be included on the\r\n      same \"printed page\" as the copyright notice for easier\r\n      identification within third-party archives.\r\n\r\n   Copyright [yyyy] [name of copyright owner]\r\n\r\n   Licensed under the Apache License, Version 2.0 (the \"License\");\r\n   you may not use this file except in compliance with the License.\r\n   You may obtain a copy of the License at\r\n\r\n       http://www.apache.org/licenses/LICENSE-2.0\r\n\r\n   Unless required by applicable law or agreed to in writing, software\r\n   distributed under the License is distributed on an \"AS IS\" BASIS,\r\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n   See the License for the specific language governing permissions and\r\n   limitations under the License."
  },
  {
    "path": "README.md",
    "content": "<div align=\"right\">\n  <a title=\"中文\" href=\"/README_CN.md\">中文</a>\n</div>\n\n<div align=\"center\">\n\n<img src=\"./source/img/butterfly-icon.png\" width=\"150\" height=\"150\" alt=\"Butterfly Logo\" />\n\n# hexo-theme-butterfly\n\nA modern, elegant and feature-rich theme for Hexo\n\n![master version](https://img.shields.io/github/package-json/v/jerryc127/hexo-theme-butterfly/master?color=%231ab1ad&label=master)\n![dev version](https://img.shields.io/github/package-json/v/jerryc127/hexo-theme-butterfly/dev?label=dev)\n![npm version](https://img.shields.io/npm/v/hexo-theme-butterfly?color=%09%23bf00ff)\n![hexo version](https://img.shields.io/badge/hexo-5.3.0+-0e83cd)\n![license](https://img.shields.io/github/license/jerryc127/hexo-theme-butterfly?color=FF5531)\n![GitHub stars](https://img.shields.io/github/stars/jerryc127/hexo-theme-butterfly?style=social)\n\n📢 **Demo**: [Butterfly Official](https://butterfly.js.org/) | [CrazyWong's Blog](https://blog.crazywong.com/)\n\n📖 **Documentation**: [English Docs](https://butterfly.js.org/en/posts/butterfly-docs-en-get-started/) | [中文文档](https://butterfly.js.org/posts/21cfbf15/)\n\n![Butterfly Theme Preview](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/theme-butterfly-readme.png)\n\n</div>\n\n---\n\n## 🚀 Quick Start\n\n### 💾 Installation\n\n#### Method 1: Git Installation (Recommended)\n\n> 💡 **Tip**: If GitHub access is slow in mainland China, you can use the [Gitee Mirror](https://gitee.com/immyw/hexo-theme-butterfly.git)\n\nExecute in your Hexo blog root directory:\n\n```bash\n# Install stable version (recommended)\ngit clone -b master https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly\n```\n\n```bash\n# Install development version (early access to new features)\ngit clone -b dev https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly\n```\n\n#### Method 2: NPM Installation\n\n> ⚠️ **Note**: NPM installation only supports Hexo 5.0.0 and above\n\n```bash\nnpm install hexo-theme-butterfly\n```\n\n### ⚙️ Theme Configuration\n\n1. **Enable Theme**: Modify your Hexo configuration file `_config.yml`:\n\n```yaml\ntheme: butterfly\n```\n\n2. **Install Dependencies**: If you haven't installed pug and stylus renderers, please run:\n\n```bash\nnpm install hexo-renderer-pug hexo-renderer-stylus --save\n```\n\n## ✨ Theme Features\n\n### 🎨 Design Style\n- [x] **Card-based Design** - Modern card-style layout\n- [x] **Rounded/Square Design** - Customizable border styles\n- [x] **Responsive Design** - Perfect adaptation to all screen sizes\n- [x] **Two-column Layout** - Optimized reading experience\n- [x] **Dark Mode** - Eye-friendly night mode\n\n### 📝 Content Features\n- [x] **Multi-level Menu** - Support for secondary navigation menus\n- [x] **Reading Mode** - Focused article reading experience\n- [x] **TOC Navigation** - Desktop and mobile TOC support\n- [x] **Word Count** - Display article word count and reading time\n- [x] **Related Articles** - Smart recommendation of related content\n- [x] **Outdated Reminder** - Automatic article update status alerts\n- [x] **Traditional/Simplified Chinese** - Support for Traditional and Simplified Chinese switching\n- [x] **Tag Plugins** - Rich tag plugin support\n\n### 🔍 Search & Navigation\n- [x] **Multiple Search Options** - Algolia Search / Local Search / Docsearch\n- [x] **Built-in 404** - Beautiful 404 error page\n- [x] **Pjax Support** - Smooth page transition experience\n\n### 🎨 Code Display\n- [x] **Syntax Highlighting** - Built-in multiple themes (darker/pale night/light/ocean)\n- [x] **Code Features** - Language display/fold expand/copy button/auto-wrap\n- [x] **Math Formulas** - Support for Mathjax and Katex\n\n### 💬 Social Interaction\n- [x] **Multiple Comment Systems** - Disqus/Gitalk/Valine/Waline/Twikoo/Giscus/Artalk etc.\n- [x] **Dual Comments Support** - Enable two comment systems simultaneously\n- [x] **Share Features** - Sharejs/Addtoany sharing components\n- [x] **Live Chat** - Chatra/Tidio/Crisp instant messaging\n\n### 📊 Analytics & Statistics\n- [x] **Visit Statistics** - Busuanzi counter\n- [x] **Site Analytics** - Google Analytics/Baidu Analytics/Cloudflare Analytics/Microsoft Clarity/Umami\n- [x] **Webmaster Verification** - Major search engine verification\n- [x] **Ad Support** - Google AdSense/custom ad slots\n\n### 🎪 Visual Effects\n- [x] **Typing Effects** - activate_power_mode animations\n- [x] **Background Effects** - Static ribbons/dynamic ribbons/floating ribbons/Canvas Nest\n- [x] **Mouse Effects** - Fireworks/hearts/text click effects\n- [x] **Loading Animations** - Preloader and pace.js progress bars\n- [x] **Image Effects** - Medium Zoom/Fancybox image lightbox\n- [x] **Lazy Loading** - Image lazy loading optimization\n\n### 🛠️ Advanced Features\n- [x] **PWA Support** - Progressive Web App\n- [x] **Copy Protection** - Disable text copying/copyright info append\n- [x] **Theme Customization** - Custom site color schemes\n- [x] **Chart Support** - Mermaid flowcharts/Chart.js data charts\n- [x] **Music Notation** - ABCJS music notation support\n- [x] **Music Player** - APlayer/Meting music playback\n- [x] **Article Series** - Series article organization\n- [x] **Instantpage** - Page preloading acceleration\n- [x] **Snackbar** - Elegant notification messages\n\n## 🤝 Contributors\n\nThanks to all the developers who have contributed to the Butterfly theme!\n\n[![Contributors](https://contrib.rocks/image?repo=jerryc127/hexo-theme-butterfly)](https://github.com/jerryc127/hexo-theme-butterfly/graphs/contributors)\n\n## 📸 Screenshots\n\n<div align=\"center\">\n\n![Theme Demo](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/butterfly-readme-screenshots-1.jpg)\n\n![Theme Demo](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/butterfly-readme-screenshots-2.jpg)\n\n![Theme Demo](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/butterfly-readme-screenshots-3.jpg)\n\n![Theme Demo](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/butterfly-readme-screenshots-4.jpg)\n\n</div>\n\n\n## ⭐ Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=jerryc127/hexo-theme-butterfly&type=Date)](https://star-history.com/#jerryc127/hexo-theme-butterfly&Date)\n\n## 🤝 Building a Better Theme Together\n\nWe believe **the power of open source comes from everyone's participation**! Whether you're a developer, designer, or user, you can contribute to the development of the Butterfly theme.\n\n### 💬 Get Help & Support\n\n- 🐛 **Found a bug?** → [GitHub Issues](https://github.com/jerryc127/hexo-theme-butterfly/issues) - Let's solve it together!\n- 💡 **Have ideas?** → [GitHub Discussions](https://github.com/jerryc127/hexo-theme-butterfly/discussions) - Share your creative ideas!\n- 📚 **Learning to use?** → [Official Documentation](https://butterfly.js.org/) - Detailed usage guide\n- 💬 **Real-time discussion?** → [Telegram Group](https://t.me/bu2fly) - Chat with community members\n\n### 🎯 Contributing\n\nWant to make Butterfly better? We welcome any form of contribution:\n\n- **🔧 Code Contributions** - Fix bugs, add new features, optimize performance\n- **📝 Documentation** - Improve docs, translate content, write tutorials\n- **🎨 Design Suggestions** - UI/UX improvements, theme colors, icon design\n- **🧪 Testing & Feedback** - Test new features, report issues, provide user experience\n- **💰 Financial Support** - [Sponsor the Project](https://buy.stripe.com/3cs6rP6YA91sbbG5kk) - Support long-term development\n\n## 📄 License\n\nThis project is licensed under the [Apache 2.0](LICENSE) License.\n\n## 🙏 Acknowledgments\n\nThis theme is developed based on [hexo-theme-melody](https://github.com/Molunerfinn/hexo-theme-melody). Thanks to the original author for their excellent work that provided inspiration and foundation!\n\nThanks to all friends who have contributed to the development of the Butterfly theme. Your support has made this theme continuously improve and progress.\n\n---\n\n<div align=\"center\">\n\n**✨ If this theme helps you, please give us a ⭐ Star! ✨**\n</div>\n"
  },
  {
    "path": "README_CN.md",
    "content": "<div align=\"right\">\r\n  <a title=\"English\" href=\"/README.md\">English</a>\r\n</div>\r\n\r\n<div align=\"center\">\r\n\r\n<img src=\"./source/img/butterfly-icon.png\" width=\"150\" height=\"150\" alt=\"Butterfly Logo\" />\r\n\r\n# hexo-theme-butterfly\r\n\r\n一個適用於 Hexo 的現代化、美觀且功能豐富的主題\r\n\r\n![master version](https://img.shields.io/github/package-json/v/jerryc127/hexo-theme-butterfly/master?color=%231ab1ad&label=master)\r\n![dev version](https://img.shields.io/github/package-json/v/jerryc127/hexo-theme-butterfly/dev?label=dev)\r\n![npm version](https://img.shields.io/npm/v/hexo-theme-butterfly?color=%09%23bf00ff)\r\n![hexo version](https://img.shields.io/badge/hexo-5.3.0+-0e83cd)\r\n![license](https://img.shields.io/github/license/jerryc127/hexo-theme-butterfly?color=FF5531)\r\n![GitHub stars](https://img.shields.io/github/stars/jerryc127/hexo-theme-butterfly?style=social)\r\n\r\n📢 **在線預覽**: [Butterfly 官方](https://butterfly.js.org/) | [CrazyWong 博客](https://blog.crazywong.com/)\r\n\r\n📖 **完整文檔**: [中文文檔](https://butterfly.js.org/posts/21cfbf15/) | [English Docs](https://butterfly.js.org/en/posts/butterfly-docs-en-get-started/)\r\n\r\n![Butterfly 主題預覽](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/theme-butterfly-readme.png)\r\n\r\n</div>\r\n\r\n---\r\n\r\n## 🚀 快速開始\r\n\r\n### 💾 安裝方式\r\n\r\n#### 方式一：Git 安裝（推薦）\r\n\r\n> 💡 **提示**: 如果您在中國大陸訪問 GitHub 速度較慢，可以使用 [Gitee 鏡像](https://gitee.com/immyw/hexo-theme-butterfly.git)\r\n\r\n在您的 Hexo 博客根目錄下執行：\r\n\r\n```bash\r\n# 安裝穩定版本（推薦）\r\ngit clone -b master https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly\r\n```\r\n\r\n```bash\r\n# 安裝開發版本（搶先體驗新功能）\r\ngit clone -b dev https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly\r\n```\r\n\r\n#### 方式二：NPM 安裝\r\n\r\n> ⚠️ **注意**: NPM 安裝方式僅支援 Hexo 5.0.0 及以上版本\r\n\r\n```bash\r\nnpm install hexo-theme-butterfly\r\n```\r\n\r\n### ⚙️ 主題配置\r\n\r\n1. **啟用主題**: 修改您的 Hexo 配置檔案 `_config.yml`：\r\n\r\n```yaml\r\ntheme: butterfly\r\n```\r\n\r\n2. **安裝依賴**: 如果您尚未安裝 pug 和 stylus 渲染器，請執行：\r\n\r\n```bash\r\nnpm install hexo-renderer-pug hexo-renderer-stylus --save\r\n```\r\n\r\n## ✨ 主題特色\r\n\r\n### 🎨 設計風格\r\n- [x] **卡片化設計** - 現代化的卡片式佈局\r\n- [x] **圓角/直角設計** - 支援自訂邊框樣式\r\n- [x] **響應式設計** - 完美適配各種螢幕尺寸\r\n- [x] **雙欄佈局** - 優化的閱讀體驗\r\n- [x] **深色模式** - 護眼的夜間模式\r\n\r\n### 📝 內容功能\r\n- [x] **多級選單** - 支援二級導航選單\r\n- [x] **閱讀模式** - 專注的文章閱讀體驗\r\n- [x] **目錄導航** - 電腦和手機雙端支援 TOC\r\n- [x] **字數統計** - 顯示文章字數和閱讀時間\r\n- [x] **相關文章** - 智能推薦相關內容\r\n- [x] **過期提醒** - 自動提示文章更新狀態\r\n- [x] **簡繁轉換** - 支援繁體中文和簡體中文切換\r\n- [x] **標籤外掛** - 豐富的標籤外掛支持\r\n\r\n### 🔍 搜尋與導航\r\n- [x] **多種搜尋** - Algolia 搜尋 / 本地搜尋 / Docsearch\r\n- [x] **內建 404** - 美觀的 404 錯誤頁面\r\n- [x] **Pjax 支援** - 流暢的頁面切換體驗\r\n\r\n### 🎨 程式碼展示\r\n- [x] **語法高亮** - 內建多種主題（darker/pale night/light/ocean）\r\n- [x] **程式碼功能** - 語言顯示/摺疊展開/複製按鈕/自動換行\r\n- [x] **數學公式** - 支援 Mathjax 和 Katex\r\n\r\n### 💬 社交互動\r\n- [x] **多元評論系統** - Disqus/Gitalk/Valine/Waline/Twikoo/Giscus/Artalk 等\r\n- [x] **雙評論支援** - 可同時啟用兩套評論系統\r\n- [x] **分享功能** - Sharejs/Addtoany 分享套件\r\n- [x] **線上客服** - Chatra/Tidio/Crisp 即時聊天\r\n\r\n### 📊 數據分析\r\n- [x] **訪問統計** - 不蒜子計數器\r\n- [x] **網站分析** - Google Analytics/百度統計/Cloudflare Analytics/Microsoft Clarity/Umami\r\n- [x] **站長驗證** - 各大搜尋引擎驗證\r\n- [x] **廣告支援** - Google AdSense/自訂廣告位\r\n\r\n### 🎪 視覺效果\r\n- [x] **打字特效** - activate_power_mode 動畫\r\n- [x] **背景特效** - 靜態彩帶/動態彩帶/飄帶效果/Canvas Nest\r\n- [x] **滑鼠特效** - 煙花/愛心/文字點擊效果\r\n- [x] **載入動畫** - Preloader 和 pace.js 進度條\r\n- [x] **圖片效果** - Medium Zoom/Fancybox 圖片燈箱\r\n- [x] **懶載入** - 圖片延遲載入優化\r\n\r\n### 🛠️ 進階功能\r\n- [x] **PWA 支援** - 漸進式網頁應用\r\n- [x] **複製保護** - 可關閉文字複製/版權資訊追加\r\n- [x] **主題定製** - 自訂網站配色方案\r\n- [x] **圖表支援** - Mermaid 流程圖/Chart.js 數據圖表\r\n- [x] **音樂符號** - ABCJS 音樂記譜法支援\r\n- [x] **音樂播放器** - APlayer/Meting 音樂播放功能\r\n- [x] **系列文章** - 系列文章組織功能\r\n- [x] **Instantpage** - 頁面預載入加速\r\n- [x] **Snackbar** - 優雅的提示訊息\r\n\r\n## 🤝 貢獻者\r\n\r\n感謝所有為 Butterfly 主題做出貢獻的開發者們！\r\n\r\n[![Contributors](https://contrib.rocks/image?repo=jerryc127/hexo-theme-butterfly)](https://github.com/jerryc127/hexo-theme-butterfly/graphs/contributors)\r\n\r\n## 📸 主題截圖\r\n\r\n<div align=\"center\">\r\n\r\n![主題展示](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/butterfly-readme-screenshots-1.jpg)\r\n\r\n![主題展示](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/butterfly-readme-screenshots-2.jpg)\r\n\r\n![主題展示](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/butterfly-readme-screenshots-3.jpg)\r\n\r\n![主題展示](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/butterfly-readme-screenshots-4.jpg)\r\n\r\n</div>\r\n\r\n\r\n## ⭐ Star 趨勢\r\n\r\n[![Star History Chart](https://api.star-history.com/svg?repos=jerryc127/hexo-theme-butterfly&type=Date)](https://star-history.com/#jerryc127/hexo-theme-butterfly&Date)\r\n\r\n## 🤝 一起構建更美好的主題\r\n\r\n我們相信，**開源的力量來自於每一個人的參與**！無論您是開發者、設計師還是用戶，都可以為 Butterfly 主題的發展貢獻力量。\r\n\r\n### 💬 獲取幫助與支援\r\n\r\n- 🐛 **發現問題？** → [GitHub Issues](https://github.com/jerryc127/hexo-theme-butterfly/issues) - 讓我們一起解決！\r\n- 💡 **有好想法？** → [GitHub Discussions](https://github.com/jerryc127/hexo-theme-butterfly/discussions) - 分享您的創意想法！\r\n- 📚 **學習使用？** → [官方文檔](https://butterfly.js.org/) - 詳細的使用指南\r\n- 💬 **即時討論？** → [Telegram 群組](https://t.me/bu2fly) - 與社群成員實時交流\r\n\r\n### 🎯 參與貢獻\r\n\r\n想要讓 Butterfly 變得更好嗎？我們歡迎您的任何形式的貢獻：\r\n\r\n- **🔧 代碼貢獻** - 修復 Bug、添加新功能、優化性能\r\n- **📝 文檔完善** - 改進文檔、翻譯內容、撰寫教程\r\n- **🎨 設計建議** - UI/UX 改進、主題配色、圖示設計\r\n- **🧪 測試反饋** - 測試新功能、回報問題、提供使用體驗\r\n- **💰 資金支援** - [贊助項目](https://buy.stripe.com/3cs6rP6YA91sbbG5kk) - 支持長期發展\r\n\r\n## 📄 授權條款\r\n\r\n本專案採用 [Apache 2.0](LICENSE) 授權條款。\r\n\r\n## 🙏 致敬與感謝\r\n\r\n本主題基於 [hexo-theme-melody](https://github.com/Molunerfinn/hexo-theme-melody) 進行開發，感謝原作者的精彩創作為我們提供了靈感與基礎！\r\n\r\n感謝所有為 Butterfly 主題發展做出貢獻的朋友們，是你們的支持讓這個主題能夠不斷完善與進步。\r\n\r\n---\r\n\r\n<div align=\"center\">\r\n\r\n**✨ 如果這個主題對您有幫助，請給我們一個 ⭐ Star！✨**\r\n</div>\r\n"
  },
  {
    "path": "_config.yml",
    "content": "# --------------------------------------\r\n# Hexo Butterfly Theme Configuration\r\n# If you have any questions, please refer to the documentation\r\n# Chinese: https://butterfly.js.org/\r\n# English: https://butterfly.js.org/en/\r\n# --------------------------------------\r\n\r\n# --------------------------------------\r\n# Navigation Settings\r\n# --------------------------------------\r\n\r\nnav:\r\n  # Navigation bar logo image\r\n  logo:\r\n  display_title: true\r\n  display_post_title: true\r\n  # Whether to fix navigation bar\r\n  fixed: false\r\n\r\nmenu:\r\n  # Home: / || fas fa-home\r\n  # List||fas fa-list:\r\n  #   Music: /music/ || fas fa-music\r\n  #   Movie: /movies/ || fas fa-video\r\n\r\n# --------------------------------------\r\n# Code Blocks Settings\r\n# --------------------------------------\r\n\r\ncode_blocks:\r\n  # Code block theme: darker / pale night / light / ocean / false\r\n  theme: light\r\n  macStyle: false\r\n  # Code block height limit (unit: px)\r\n  height_limit: false\r\n  word_wrap: false\r\n\r\n  # Toolbar\r\n  copy: true\r\n  language: true\r\n  # true: shrink the code blocks | false: expand the code blocks | none: expand code blocks and hide the button\r\n  shrink: false\r\n  fullpage: false\r\n\r\n# Social media links\r\n# Formal:\r\n#   icon: link || the description || color\r\nsocial:\r\n  # fab fa-github: https://github.com/xxxxx || Github || '#24292e'\r\n  # fas fa-envelope: mailto:xxxxxx@gmail.com || Email || '#4a7dbe'\r\n\r\n# --------------------------------------\r\n# Image Settings\r\n# --------------------------------------\r\n\r\nfavicon: /img/favicon.png\r\n\r\navatar:\r\n  img: /img/butterfly-icon.png\r\n  effect: false\r\n\r\n# Disable all banner images\r\ndisable_top_img: false\r\n\r\n# If the banner of page not setting, it will show the default_top_img\r\ndefault_top_img:\r\n\r\n# The banner image of index page\r\nindex_img:\r\n\r\n# The banner image of archive page\r\narchive_img:\r\n\r\n# Note: tag page, not tags page\r\ntag_img:\r\n\r\n# The banner image of tag page, you can set the banner image for each tag\r\n# Format:\r\n#  - tag name: xxxxx\r\ntag_per_img:\r\n\r\n# Note: category page, not categories page\r\ncategory_img:\r\n\r\n# The banner image of category page, you can set the banner image for each category\r\n# Format:\r\n#  - category name: xxxxx\r\ncategory_per_img:\r\n\r\n# The background image of footer\r\nfooter_img: false\r\n\r\n# Website Background\r\n# Can set it to color, image URL or an array containing colors and/or image URLs\r\n# If an array is provided, a random background will be selected from the array on each load\r\nbackground:\r\n\r\ncover:\r\n  # Disable the cover or not\r\n  index_enable: true\r\n  aside_enable: true\r\n  archives_enable: true\r\n  # When cover is not set, the default cover is displayed\r\n  default_cover:\r\n    # - xxx.jpg\r\n\r\n# Replace Broken Images\r\nerror_img:\r\n  flink: /img/friend_404.gif\r\n  post_page: /img/404.jpg\r\n\r\n# A simple 404 page\r\nerror_404:\r\n  enable: false\r\n  subtitle: 'Page Not Found'\r\n  background: /img/error-page.png\r\n\r\npost_meta:\r\n  # Home Page\r\n  page:\r\n    # Choose: created / updated / both\r\n    date_type: created\r\n    # Choose: date / relative\r\n    date_format: date\r\n    categories: true\r\n    tags: false\r\n    label: true\r\n  post:\r\n    # Choose: left / center\r\n    position: left\r\n    # Choose: created / updated / both\r\n    date_type: both\r\n    # Choose: date / relative\r\n    date_format: date\r\n    categories: true\r\n    tags: true\r\n    label: true\r\n\r\n# --------------------------------------\r\n# Index page settings\r\n# --------------------------------------\r\n\r\n# The top_img settings of home page\r\n# default: top img - full screen, site info - middle\r\n# The position of site info, eg: 300px/300em/300rem/10%\r\nindex_site_info_top:\r\n# The height of top_img, eg: 300px/300em/300rem\r\nindex_top_img_height:\r\n\r\n# The subtitle on homepage\r\nsubtitle:\r\n  enable: false\r\n  # Typewriter Effect\r\n  effect: true\r\n  # Customize typed.js\r\n  # https://github.com/mattboldt/typed.js/#customization\r\n  typed_option:\r\n  # Source - Call the third-party service API (Chinese only)\r\n  # It will show the source first, then show the content of sub\r\n  # Choose: false/1/2/3\r\n  # false - disable the function\r\n  # 1 - hitokoto.cn\r\n  # 2 - https://api.aa1.cn/doc/yiyan.html\r\n  # 3 - jinrishici.com\r\n  source: false\r\n  # If you close the typewriter effect, the subtitle will only show the first line of sub\r\n  sub:\r\n\r\n# Article layout on the homepage\r\n# 1: Cover on the left, info on the right\r\n# 2: Cover on the right, info on the left\r\n# 3: Cover and info alternate between left and right\r\n# 4: Cover on top, info on the bottom\r\n# 5: Info displayed on the cover\r\n# 6: Masonry layout - Cover on top, info on the bottom\r\n# 7: Masonry layout - Info displayed on the cover\r\nindex_layout: 3\r\n\r\n# Display the article introduction on homepage\r\n# 1: description\r\n# 2: both (if the description exists, it will show description, or show the auto_excerpt)\r\n# 3: auto_excerpt (default)\r\n# false: do not show the article introduction\r\nindex_post_content:\r\n  method: 3\r\n  # If you set method to 2 or 3, the length need to config\r\n  length: 500\r\n\r\n# --------------------------------------\r\n# Post Settings\r\n# --------------------------------------\r\n\r\ntoc:\r\n  post: true\r\n  page: false\r\n  number: true\r\n  expand: false\r\n  # Only for post\r\n  style_simple: false\r\n  scroll_percent: true\r\n\r\npost_copyright:\r\n  enable: true\r\n  decode: false\r\n  author_href:\r\n  license: CC BY-NC-SA 4.0\r\n  license_url: https://creativecommons.org/licenses/by-nc-sa/4.0/\r\n\r\n# Sponsor/reward\r\nreward:\r\n  enable: false\r\n  text:\r\n  QR_code:\r\n    # - img: /img/wechat.jpg\r\n    #   link:\r\n    #   text: wechat\r\n    # - img: /img/alipay.jpg\r\n    #   link:\r\n    #   text: alipay\r\n\r\n# Post edit\r\n# Easily browse and edit blog source code online.\r\npost_edit:\r\n  enable: false\r\n  # url: https://github.com/user-name/repo-name/edit/branch-name/subdirectory-name/\r\n  # For example: https://github.com/jerryc127/butterfly.js.org/edit/main/source/\r\n  url:\r\n\r\n# Related Articles\r\nrelated_post:\r\n  enable: true\r\n  # Number of posts displayed\r\n  limit: 6\r\n  # Choose: created / updated\r\n  date_type: created\r\n\r\n# Choose: 1 / 2 / false\r\n# 1: The 'next post' will link to old post\r\n# 2: The 'next post' will link to new post\r\n# false: disable pagination\r\npost_pagination: 1\r\n\r\n# Displays outdated notice for a post\r\nnoticeOutdate:\r\n  enable: false\r\n  # Style: simple / flat\r\n  style: flat\r\n  # When will it be shown\r\n  limit_day: 365\r\n  # Position: top / bottom\r\n  position: top\r\n  message_prev: It has been\r\n  message_next: days since the last update, the content of the article may be outdated.\r\n\r\n# --------------------------------------\r\n# Footer Settings\r\n# --------------------------------------\r\nfooter:\r\n  nav:\r\n  owner:\r\n    enable: true\r\n    since: 2025\r\n  # Copyright of theme and framework\r\n  copyright:\r\n    enable: true\r\n    version: true\r\n  custom_text:\r\n\r\n# --------------------------------------\r\n# Aside Settings\r\n# --------------------------------------\r\n\r\naside:\r\n  enable: true\r\n  hide: false\r\n  # Show the button to hide the aside in bottom right button\r\n  button: true\r\n  mobile: true\r\n  # Position: left / right\r\n  position: right\r\n  display:\r\n    archive: true\r\n    tag: true\r\n    category: true\r\n  card_author:\r\n    enable: true\r\n    description:\r\n    button:\r\n      enable: true\r\n      icon: fab fa-github\r\n      text: Follow Me\r\n      link: https://github.com/xxxxxx\r\n  card_announcement:\r\n    enable: true\r\n    content: This is my Blog\r\n  card_recent_post:\r\n    enable: true\r\n    # If set 0 will show all\r\n    limit: 5\r\n    # Sort: date / updated\r\n    sort: date\r\n    sort_order:\r\n  card_newest_comments:\r\n    enable: false\r\n    sort_order:\r\n    limit: 6\r\n    # Unit: mins, save data to localStorage\r\n    storage: 10\r\n    avatar: true\r\n  card_categories:\r\n    enable: true\r\n    # If set 0 will show all\r\n    limit: 8\r\n    # Choose: none / true / false\r\n    expand: none\r\n    sort_order:\r\n  card_tags:\r\n    enable: true\r\n    # If set 0 will show all\r\n    limit: 40\r\n    color: false\r\n    custom_colors:\r\n    # Order of tags, random/name/length\r\n    orderby: random\r\n    # Sort of order. 1, asc for ascending; -1, desc for descending\r\n    order: 1\r\n    sort_order:\r\n  card_archives:\r\n    enable: true\r\n    # Type: monthly / yearly\r\n    type: monthly\r\n    # Eg: YYYY年MM月\r\n    format: MMMM YYYY\r\n    # Sort of order. 1, asc for ascending; -1, desc for descending\r\n    order: -1\r\n    # If set 0 will show all\r\n    limit: 8\r\n    sort_order:\r\n  card_post_series:\r\n    enable: true\r\n    # The title shows the series name\r\n    series_title: false\r\n    # Order by title or date\r\n    orderBy: 'date'\r\n    # Sort of order. 1, asc for ascending; -1, desc for descending\r\n    order: -1\r\n  card_webinfo:\r\n    enable: true\r\n    post_count: true\r\n    last_push_date: true\r\n    sort_order:\r\n    # Time difference between publish date and now\r\n    # Formal: Month/Day/Year Time or Year/Month/Day Time\r\n    # Leave it empty if you don't enable this feature\r\n    runtime_date:\r\n\r\n# --------------------------------------\r\n# Bottom right button\r\n# --------------------------------------\r\n\r\n# The distance between the bottom right button and the bottom (default unit: px)\r\nrightside_bottom:\r\n\r\n# Conversion between Traditional and Simplified Chinese\r\ntranslate:\r\n  enable: false\r\n  # The text of a button\r\n  default: 繁\r\n  # the language of website (1 - Traditional Chinese/ 2 - Simplified Chinese）\r\n  defaultEncoding: 2\r\n  # Time delay\r\n  translateDelay: 0\r\n  # The text of the button when the language is Simplified Chinese\r\n  msgToTraditionalChinese: '繁'\r\n  # The text of the button when the language is Traditional Chinese\r\n  msgToSimplifiedChinese: '簡'\r\n\r\n# Read Mode\r\nreadmode: true\r\n\r\n# Dark Mode\r\ndarkmode:\r\n  enable: true\r\n  # Toggle Button to switch dark/light mode\r\n  button: true\r\n  # Switch dark/light mode automatically\r\n  # autoChangeMode: 1  Following System Settings, if the system doesn't support dark mode, it will switch dark mode between 6 pm to 6 am\r\n  # autoChangeMode: 2  Switch dark mode between 6 pm to 6 am\r\n  # autoChangeMode: false\r\n  autoChangeMode: false\r\n  # Set the light mode time. The value is between 0 and 24. If not set, the default value is 6 and 18\r\n  start:\r\n  end:\r\n\r\n# Show scroll percent in scroll-to-top button\r\nrightside_scroll_percent: false\r\n\r\n# Don't modify the following settings unless you know how they work\r\n# Choose: readmode,translate,darkmode,hideAside,toc,chat,comment\r\n# Don't repeat the same value\r\nrightside_item_order:\r\n  enable: false\r\n  # Default: readmode,translate,darkmode,hideAside\r\n  hide:\r\n  # Default: toc,chat,comment\r\n  show:\r\n\r\n# Animation for the bottom right config button\r\nrightside_config_animation: true\r\n\r\n# --------------------------------------\r\n# Global Settings\r\n# --------------------------------------\r\n\r\nanchor:\r\n  # When you scroll, the URL will update according to header id.\r\n  auto_update: false\r\n  # Click the headline to scroll and update the anchor\r\n  click_to_scroll: false\r\n\r\nphotofigcaption: false\r\n\r\ncopy:\r\n  enable: true\r\n  # Add the copyright information after copied content\r\n  copyright:\r\n    enable: false\r\n    limit_count: 150\r\n\r\n# Need to install the hexo-wordcount plugin\r\nwordcount:\r\n  enable: false\r\n  # Display the word count of the article in post meta\r\n  post_wordcount: true\r\n  # Display the time to read the article in post meta\r\n  min2read: true\r\n  # Display the total word count of the website in aside's webinfo\r\n  total_wordcount: true\r\n\r\n# Busuanzi count for PV / UV in site\r\nbusuanzi:\r\n  site_uv: true\r\n  site_pv: true\r\n  page_pv: true\r\n\r\n# --------------------------------------\r\n# Math\r\n# --------------------------------------\r\n\r\n# About the per_page\r\n# if you set it to true, it will load mathjax/katex script in each page\r\n# if you set it to false, it will load mathjax/katex script according to your setting (add the 'mathjax: true' or 'katex: true' in page's front-matter)\r\nmath:\r\n  # Choose: mathjax, katex\r\n  # Leave it empty if you don't need math\r\n  use:\r\n  per_page: true\r\n  hide_scrollbar: false\r\n\r\n  mathjax:\r\n    # Enable the contextual menu\r\n    enableMenu: true\r\n    # Choose: all / ams / none, This controls whether equations are numbered and how\r\n    tags: none\r\n\r\n  katex:\r\n    # Enable the copy KaTeX formula\r\n    copy_tex: false\r\n\r\n# --------------------------------------\r\n# Search\r\n# --------------------------------------\r\n\r\nsearch:\r\n  # Choose: algolia_search / local_search / docsearch\r\n  # leave it empty if you don't need search\r\n  use:\r\n  placeholder:\r\n\r\n  # Algolia Search\r\n  algolia_search:\r\n    # Number of search results per page\r\n    hitsPerPage: 6\r\n\r\n  # Local Search\r\n  local_search:\r\n    # Preload the search data when the page loads.\r\n    preload: false\r\n    # Show top n results per article, show all results by setting to -1\r\n    top_n_per_article: 1\r\n    # Unescape html strings to the readable one.\r\n    unescape: false\r\n    # Enable pagination for search results\r\n    pagination:\r\n      enable: false\r\n      # Number of search results per page\r\n      hitsPerPage: 8\r\n    CDN:\r\n\r\n  # Docsearch\r\n  # https://docsearch.algolia.com/\r\n  docsearch:\r\n    appId:\r\n    apiKey:\r\n    indexName:\r\n    option:\r\n\r\n# --------------------------------------\r\n# Share System\r\n# --------------------------------------\r\n\r\nshare:\r\n  # Choose: sharejs / addtoany\r\n  # Leave it empty if you don't need share\r\n  use: sharejs\r\n\r\n  # Share.js\r\n  # https://github.com/overtrue/share.js\r\n  sharejs:\r\n    sites: facebook,x,wechat,weibo,qq\r\n\r\n  # AddToAny\r\n  # https://www.addtoany.com/\r\n  addtoany:\r\n    item: facebook,x,wechat,sina_weibo,facebook_messenger,email,copy_link\r\n\r\n# --------------------------------------\r\n# Comments System\r\n# --------------------------------------\r\n\r\ncomments:\r\n  # Up to two comments system, the first will be shown as default\r\n  # Leave it empty if you don't need comments\r\n  # Choose: Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus/Remark42/Artalk\r\n  # Format of two comments system : Disqus,Waline\r\n  use:\r\n  # Display the comment name next to the button\r\n  text: true\r\n  # Lazyload: The comment system will be load when comment element enters the browser's viewport.\r\n  # If you set it to true, the comment count will be invalid\r\n  lazyload: false\r\n  # Display comment count in post's top_img\r\n  count: false\r\n  # Display comment count in Home Page\r\n  card_post_count: false\r\n\r\n# Disqus\r\n# https://disqus.com/\r\ndisqus:\r\n  shortname:\r\n  # For newest comments widget\r\n  apikey:\r\n\r\n# Alternative Disqus - Render comments with Disqus API\r\n# https://github.com/SukkaW/DisqusJS\r\ndisqusjs:\r\n  shortname:\r\n  apikey:\r\n  option:\r\n\r\n# Livere\r\n# https://www.livere.com/\r\nlivere:\r\n  uid:\r\n\r\n# Gitalk\r\n# https://github.com/gitalk/gitalk\r\ngitalk:\r\n  client_id:\r\n  client_secret:\r\n  repo:\r\n  owner:\r\n  admin:\r\n  option:\r\n\r\n# Valine\r\n# https://valine.js.org\r\nvaline:\r\n  appId:\r\n  appKey:\r\n  avatar: monsterid\r\n  # This configuration is suitable for domestic custom domain name users, overseas version will be automatically detected (no need to manually fill in)\r\n  serverURLs:\r\n  bg:\r\n  # Use Valine visitor count as the page view count\r\n  visitor: false\r\n  option:\r\n\r\n# Waline - A simple comment system with backend support fork from Valine\r\n# https://waline.js.org/\r\nwaline:\r\n  serverURL:\r\n  bg:\r\n  # Use Waline pageview count as the page view count\r\n  pageview: false\r\n  option:\r\n\r\n# Utterances\r\n# https://utteranc.es/\r\nutterances:\r\n  repo:\r\n  # Issue Mapping: pathname/url/title/og:title\r\n  issue_term: pathname\r\n  # Theme: github-light/github-dark/github-dark-orange/icy-dark/dark-blue/photon-dark\r\n  light_theme: github-light\r\n  dark_theme: photon-dark\r\n  js:\r\n  option:\r\n\r\n# Facebook Comments Plugin\r\n# https://developers.facebook.com/docs/plugins/comments/\r\nfacebook_comments:\r\n  app_id:\r\n  # optional\r\n  user_id:\r\n  pageSize: 10\r\n  # Choose: social / time / reverse_time\r\n  order_by: social\r\n  lang: en_US\r\n\r\n# Twikoo\r\n# https://github.com/imaegoo/twikoo\r\ntwikoo:\r\n  envId:\r\n  region:\r\n  # Use Twikoo visitor count as the page view count\r\n  visitor: false\r\n  option:\r\n\r\n# Giscus\r\n# https://giscus.app/\r\ngiscus:\r\n  repo:\r\n  repo_id:\r\n  category_id:\r\n  light_theme: light\r\n  dark_theme: dark\r\n  js:\r\n  option:\r\n\r\n# Remark42\r\n# https://remark42.com/docs/configuration/frontend/\r\nremark42:\r\n  host:\r\n  siteId:\r\n  option:\r\n\r\n# Artalk\r\n# https://artalk.js.org/guide/frontend/config.html\r\nartalk:\r\n  server:\r\n  site:\r\n  # Use Artalk visitor count as the page view count\r\n  visitor: false\r\n  option:\r\n\r\n# --------------------------------------\r\n# Chat Services\r\n# --------------------------------------\r\n\r\nchat:\r\n  # Choose: chatra/tidio/crisp\r\n  # Leave it empty if you don't need chat\r\n  use:\r\n  # Chat Button [recommend]\r\n  # It will create a button in the bottom right corner of website, and hide the origin button\r\n  rightside_button: false\r\n  # The origin chat button is displayed when scrolling up, and the button is hidden when scrolling down\r\n  button_hide_show: false\r\n\r\n# https://chatra.io/\r\nchatra:\r\n  id:\r\n\r\n# https://www.tidio.com/\r\ntidio:\r\n  public_key:\r\n\r\n# https://crisp.chat/en/\r\ncrisp:\r\n  website_id:\r\n\r\n# --------------------------------------\r\n# Analysis\r\n# --------------------------------------\r\n\r\n# https://tongji.baidu.com/web/welcome/login\r\nbaidu_analytics:\r\n\r\n# https://analytics.google.com/analytics/web/\r\ngoogle_analytics:\r\n\r\n# https://www.cloudflare.com/zh-tw/web-analytics/\r\ncloudflare_analytics:\r\n\r\n# https://clarity.microsoft.com/\r\nmicrosoft_clarity:\r\n\r\n# https://umami.is/\r\numami_analytics:\r\n  enable: false\r\n  # For self-hosted setups, configure the hostname of the Umami instance\r\n  serverURL:\r\n  script_name: script.js\r\n  website_id:\r\n  option:\r\n  UV_PV:\r\n    site_uv: false\r\n    site_pv: false\r\n    page_pv: false\r\n    # Umami Cloud (API key) / self-hosted Umami (token)\r\n    token:\r\n\r\n# https://www.googletagmanager.com/\r\ngoogle_tag_manager:\r\n  tag_id:\r\n  # optional\r\n  domain:\r\n\r\n# --------------------------------------\r\n# Advertisement\r\n# --------------------------------------\r\n\r\n# Google Adsense\r\ngoogle_adsense:\r\n  enable: false\r\n  auto_ads: true\r\n  js: https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js\r\n  client:\r\n  enable_page_level_ads: true\r\n\r\n# Insert ads manually\r\n# Leave it empty if you don't need ads\r\nad:\r\n  # Insert ads in the index (every three posts)\r\n  index:\r\n  # Insert ads in aside\r\n  aside:\r\n  # Insert ads in the post (before pagination)\r\n  post:\r\n\r\n# --------------------------------------\r\n# Verification\r\n# --------------------------------------\r\n\r\nsite_verification:\r\n  # - name: google-site-verification\r\n  #   content: xxxxxx\r\n  # - name: baidu-site-verification\r\n  #   content: xxxxxxx\r\n\r\n# --------------------------------------\r\n# Beautify / Effect\r\n# --------------------------------------\r\n\r\n# Theme color for customize\r\n# Notice: color value must in double quotes like \"#000\" or may cause error!\r\n\r\n# theme_color:\r\n#   enable: true\r\n#   main: \"#49B1F5\"\r\n#   paginator: \"#00c4b6\"\r\n#   button_hover: \"#FF7242\"\r\n#   text_selection: \"#00c4b6\"\r\n#   link_color: \"#99a9bf\"\r\n#   meta_color: \"#858585\"\r\n#   hr_color: \"#A4D8FA\"\r\n#   code_foreground: \"#F47466\"\r\n#   code_background: \"rgba(27, 31, 35, .05)\"\r\n#   toc_color: \"#00c4b6\"\r\n#   blockquote_padding_color: \"#49b1f5\"\r\n#   blockquote_background_color: \"#49b1f5\"\r\n#   scrollbar_color: \"#49b1f5\"\r\n#   meta_theme_color_light: \"ffffff\"\r\n#   meta_theme_color_dark: \"#0d0d0d\"\r\n\r\n# The user interface setting of category and tag page\r\n# Choose: index - same as Homepage UI / default - same as archives UI\r\n# leave it empty or index\r\ncategory_ui:\r\ntag_ui:\r\n\r\n# Rounded corners for UI elements\r\nrounded_corners_ui: true\r\n\r\n# Stretches the lines so that each line has equal width\r\ntext_align_justify: false\r\n\r\n# Add a mask to the header and footer\r\nmask:\r\n  header: true\r\n  footer: true\r\n\r\n# Loading Animation\r\npreloader:\r\n  enable: false\r\n  # source\r\n  # 1. fullpage-loading\r\n  # 2. pace (progress bar)\r\n  source: 1\r\n  # pace theme (see https://codebyzach.github.io/pace/)\r\n  pace_css_url:\r\n\r\n# Page Transition\r\nenter_transitions: true\r\n\r\n# Default display mode - light (default) / dark\r\ndisplay_mode: light\r\n\r\n# Configuration for beautifying the content of the article\r\nbeautify:\r\n  enable: false\r\n  # Specify the field to beautify (site or post)\r\n  field: post\r\n  # Specify the icon to be used as a prefix for the title, such as '\\f0c1'\r\n  title_prefix_icon:\r\n  # Specify the color of the title prefix icon, such as '#F47466'\r\n  title_prefix_icon_color:\r\n\r\n# Global font settings\r\n# Don't modify the following settings unless you know how they work\r\nfont:\r\n  global_font_size:\r\n  code_font_size:\r\n  font_family:\r\n  code_font_family:\r\n\r\n# Font settings for the site title and site subtitle\r\nblog_title_font:\r\n  font_link:\r\n  font_family:\r\n\r\n# The setting of divider icon\r\nhr_icon:\r\n  enable: true\r\n  # The unicode value of Font Awesome icon, such as '\\3423'\r\n  icon:\r\n  icon_top:\r\n\r\n# Typewriter Effect\r\n# https://github.com/disjukr/activate-power-mode\r\nactivate_power_mode:\r\n  enable: false\r\n  colorful: true\r\n  shake: true\r\n  mobile: false\r\n\r\n# Background effects\r\n# --------------------------------------\r\n\r\n# canvas_ribbon\r\n# See: https://github.com/hustcc/ribbon.js\r\ncanvas_ribbon:\r\n  enable: false\r\n  # The size of ribbon\r\n  size: 150\r\n  # The opacity of ribbon (0 ~ 1)\r\n  alpha: 0.6\r\n  zIndex: -1\r\n  click_to_change: false\r\n  mobile: false\r\n\r\n# Fluttering Ribbon\r\ncanvas_fluttering_ribbon:\r\n  enable: false\r\n  mobile: false\r\n\r\n# canvas_nest\r\n# https://github.com/hustcc/canvas-nest.js\r\ncanvas_nest:\r\n  enable: false\r\n  # Color of lines, default: '0,0,0'; RGB values: (R,G,B).(note: use ',' to separate.)\r\n  color: '0,0,255'\r\n  # The opacity of line (0~1)\r\n  opacity: 0.7\r\n  # The z-index property of the background\r\n  zIndex: -1\r\n  # The number of lines\r\n  count: 99\r\n  mobile: false\r\n\r\n# Mouse click effects: fireworks\r\nfireworks:\r\n  enable: false\r\n  zIndex: 9999\r\n  mobile: false\r\n\r\n# Mouse click effects: Heart symbol\r\nclick_heart:\r\n  enable: false\r\n  mobile: false\r\n\r\n# Mouse click effects: words\r\nclickShowText:\r\n  enable: false\r\n  text:\r\n    # - I\r\n    # - LOVE\r\n    # - YOU\r\n  fontSize: 15px\r\n  random: false\r\n  mobile: false\r\n\r\n# --------------------------------------\r\n# Lightbox Settings\r\n# --------------------------------------\r\n\r\n# Choose: fancybox / medium_zoom\r\n# https://github.com/francoischalifour/medium-zoom\r\n# https://fancyapps.com/fancybox/\r\n# Leave it empty if you don't need lightbox\r\nlightbox:\r\n\r\n# --------------------------------------\r\n# Tag Plugins settings\r\n# --------------------------------------\r\n\r\n# Series\r\nseries:\r\n  enable: false\r\n  # Order by title or date\r\n  orderBy: 'title'\r\n  # Sort of order. 1, asc for ascending; -1, desc for descending\r\n  order: 1\r\n  number: true\r\n\r\n# ABCJS - The ABC Music Notation Plugin\r\n# https://github.com/paulrosen/abcjs\r\nabcjs:\r\n  enable: false\r\n  per_page: true\r\n\r\n# Mermaid\r\n# https://github.com/mermaid-js/mermaid\r\nmermaid:\r\n  enable: false\r\n  # Write Mermaid diagrams using code blocks\r\n  code_write: false\r\n  # built-in themes: default / forest / dark / neutral\r\n  theme:\r\n    light: default\r\n    dark: dark\r\n  # Enable \"Open in New Tab\" button to view diagram in a separate window\r\n  open_in_new_tab: true\r\n  # Enable zoom and pan interactions on diagrams\r\n  zoom_pan: true\r\n\r\n# chartjs\r\n# see https://www.chartjs.org/docs/latest/\r\nchartjs:\r\n  enable: false\r\n  # Do not modify unless you understand how they work.\r\n  # The default settings are only used when the MD syntax is not specified.\r\n  # General font color for the chart\r\n  fontColor:\r\n    light: 'rgba(0, 0, 0, 0.8)'\r\n    dark: 'rgba(255, 255, 255, 0.8)'\r\n  # General border color for the chart\r\n  borderColor:\r\n    light: 'rgba(0, 0, 0, 0.1)'\r\n    dark: 'rgba(255, 255, 255, 0.2)'\r\n  # Background color for scale labels on radar and polar area charts\r\n  scale_ticks_backdropColor:\r\n    light: 'transparent'\r\n    dark: 'transparent'\r\n\r\n# Note - Bootstrap Callout\r\nnote:\r\n  # Note tag style values:\r\n  #  - simple    bs-callout old alert style. Default.\r\n  #  - modern    bs-callout new (v2-v3) alert style.\r\n  #  - flat      flat callout style with background, like on Mozilla or StackOverflow.\r\n  #  - disabled  disable all CSS styles import of note tag.\r\n  style: flat\r\n  icons: true\r\n  border_radius: 3\r\n  # Offset lighter of background in % for modern and flat styles (modern: -12 | 12; flat: -18 | 6).\r\n  # Offset also applied to label tag variables. This option can work with disabled note tag.\r\n  light_bg_offset: 0\r\n\r\n# --------------------------------------\r\n# Other Settings\r\n# --------------------------------------\r\n\r\n# https://github.com/MoOx/pjax\r\npjax:\r\n  enable: false\r\n  # Exclude the specified pages from pjax, such as '/music/'\r\n  exclude:\r\n    # - /xxxxxx/\r\n\r\n# Inject the css and script (aplayer/meting)\r\naplayerInject:\r\n  enable: false\r\n  per_page: true\r\n\r\n# Snackbar - Toast Notification\r\n# https://github.com/polonel/SnackBar\r\n# position: top-left / top-center / top-right / bottom-left / bottom-center / bottom-right\r\nsnackbar:\r\n  enable: false\r\n  position: bottom-left\r\n  # The background color of Toast Notification in light mode and dark mode\r\n  bg_light: '#49b1f5'\r\n  bg_dark: '#1f1f1f'\r\n\r\n# Instant.page\r\n# https://instant.page/\r\ninstantpage: false\r\n\r\n# Lazyload\r\n# https://github.com/verlok/vanilla-lazyload\r\nlazyload:\r\n  enable: false\r\n  # Use browser's native lazyload instead of vanilla-lazyload\r\n  native: false\r\n  # Specify the field to use lazyload (site or post)\r\n  field: site\r\n  placeholder:\r\n  blur: false\r\n\r\n# PWA\r\n# See https://github.com/JLHwung/hexo-offline\r\n# ---------------\r\npwa:\r\n  enable: false\r\n  manifest:\r\n  apple_touch_icon:\r\n  favicon_32_32:\r\n  favicon_16_16:\r\n  mask_icon:\r\n\r\n# Open graph meta tags\r\n# https://hexo.io/docs/helpers#open-graph\r\nOpen_Graph_meta:\r\n  enable: true\r\n  option:\r\n    # twitter_card:\r\n    # twitter_image:\r\n    # twitter_id:\r\n    # twitter_site:\r\n    # google_plus:\r\n    # fb_admins:\r\n    # fb_app_id:\r\n\r\n# Structured Data\r\n# https://developers.google.com/search/docs/guides/intro-structured-data\r\nstructured_data:\r\n  enable: false\r\n  # Alternate name for the site, used in structured data\r\n  # Format: ['name1', 'name2']\r\n  alternate_name:\r\n\r\n# Add the vendor prefixes to ensure compatibility\r\ncss_prefix: true\r\n\r\n# Inject\r\n# Insert the code to head (before '</head>' tag) and the bottom (before '</body>' tag)\r\ninject:\r\n  head:\r\n    # - <link rel=\"stylesheet\" href=\"/xxx.css\">\r\n  bottom:\r\n    # - <script src=\"xxxx\"></script>\r\n\r\n# CDN Settings\r\n# Don't modify the following settings unless you know how they work\r\nCDN:\r\n  # The CDN provider for internal and third-party scripts\r\n  # Options for both: local/jsdelivr/unpkg/cdnjs/custom\r\n  # Note: Dev version can only use 'local' for internal scripts\r\n  # Note: When setting third-party scripts to 'local', you need to install hexo-butterfly-extjs\r\n  internal_provider: local\r\n  third_party_provider: jsdelivr\r\n\r\n  # Add version number to url, true or false\r\n  version: true\r\n\r\n  # Custom format\r\n  # For example: https://cdn.staticfile.org/${cdnjs_name}/${version}/${min_cdnjs_file}\r\n  custom_format:\r\n\r\n  option:\r\n    # abcjs_basic_js:\r\n    # activate_power_mode:\r\n    # algolia_js:\r\n    # algolia_search:\r\n    # aplayer_css:\r\n    # aplayer_js:\r\n    # artalk_css:\r\n    # artalk_js:\r\n    # blueimp_md5:\r\n    # busuanzi:\r\n    # canvas_fluttering_ribbon:\r\n    # canvas_nest:\r\n    # canvas_ribbon:\r\n    # chartjs:\r\n    # click_heart:\r\n    # clickShowText:\r\n    # disqusjs:\r\n    # disqusjs_css:\r\n    # docsearch_css:\r\n    # docsearch_js:\r\n    # egjs_infinitegrid:\r\n    # fancybox:\r\n    # fancybox_css:\r\n    # fireworks:\r\n    # fontawesome:\r\n    # gitalk:\r\n    # gitalk_css:\r\n    # giscus:\r\n    # instantpage:\r\n    # katex:\r\n    # katex_copytex:\r\n    # lazyload:\r\n    # local_search:\r\n    # main:\r\n    # main_css:\r\n    # mathjax:\r\n    # medium_zoom:\r\n    # mermaid:\r\n    # meting_js:\r\n    # prismjs_autoloader:\r\n    # prismjs_js:\r\n    # prismjs_lineNumber_js:\r\n    # pjax:\r\n    # sharejs:\r\n    # sharejs_css:\r\n    # snackbar:\r\n    # snackbar_css:\r\n    # translate:\r\n    # twikoo:\r\n    # typed:\r\n    # utils:\r\n    # valine:\r\n    # waline_css:\r\n    # waline_js:\r\n"
  },
  {
    "path": "languages/default.yml",
    "content": "footer:\r\n  framework: Framework\r\n  theme: Theme\r\n\r\ncopy:\r\n  success: Copy Successful\r\n  error: Copy Failed\r\n  noSupport: Browser Not Supported\r\n\r\npage:\r\n  articles: All Articles\r\n  tag: Tag\r\n  category: Category\r\n  archives: Archives\r\n\r\ncard_post_count: comments\r\n\r\nno_title: Untitled\r\n\r\npost:\r\n  created: Created\r\n  updated: Updated\r\n  wordcount: Word Count\r\n  min2read: Reading Time\r\n  min2read_unit: mins\r\n  page_pv: Post Views\r\n  comments: Comments\r\n  copyright:\r\n    author: Author\r\n    link: Link\r\n    copyright_notice: Copyright Notice\r\n    copyright_content: 'All articles on this blog are licensed under <a href=\"%s\">%s</a> unless otherwise stated.'\r\n  recommend: Related Articles\r\n  edit: Edit\r\n  back_to_home: Back to Home\r\n\r\nsearch:\r\n  title: Search\r\n  load_data: Loading Database\r\n  input_placeholder: Search for Posts\r\n  algolia_search:\r\n    hits_empty: 'No results found for: ${query}'\r\n    hits_stats: '${hits} results found in ${time} ms'\r\n  local_search:\r\n    hits_empty: 'No results found for: ${query}'\r\n    hits_stats: '${hits} articles found'\r\n\r\npagination:\r\n  prev: Previous\r\n  next: Next\r\n  page_info: 'Page ${current} of ${total}' \r\n\r\ncomment: Comments\r\n\r\naside:\r\n  articles: Articles\r\n  tags: Tags\r\n  categories: Categories\r\n  card_announcement: Announcement\r\n  card_categories: Categories\r\n  card_tags: Tags\r\n  card_archives: Archives\r\n  card_recent_post: Recent Posts\r\n  card_webinfo:\r\n    headline: Website Info\r\n    article_name: Article Count\r\n    runtime:\r\n      name: Runtime\r\n      unit: days\r\n    last_push_date:\r\n      name: Last Update\r\n    site_wordcount: Total Word Count\r\n    site_uv_name: Unique Visitors\r\n    site_pv_name: Page Views\r\n  more_button: View More\r\n  card_newest_comments:\r\n    headline: Latest Comments\r\n    loading_text: Loading...\r\n    error: Unable to retrieve comments, please check the configuration\r\n    zero: No comments\r\n    image: Image\r\n    link: Link\r\n    code: Code\r\n  card_toc: Contents\r\n  card_post_series: Post Series\r\n\r\ndate_suffix:\r\n  just: Just now\r\n  min: minutes ago\r\n  hour: hours ago\r\n  day: days ago\r\n  month: months ago\r\n\r\ndonate: Sponsor\r\nshare: Share\r\n\r\nrightside:\r\n  readmode_title: Reading Mode\r\n  translate_title: Toggle Between Traditional and Simplified Chinese\r\n  night_mode_title: Toggle Between Light and Dark Mode\r\n  back_to_top: Back to Top\r\n  toc: Table of Contents\r\n  scroll_to_comment: Scroll to Comments\r\n  setting: Settings\r\n  aside: Toggle Between Single-column and Double-column\r\n  chat: Chat\r\n\r\ncopy_copyright:\r\n  author: Author\r\n  link: Link\r\n  source: Source\r\n  info: Copyright belongs to the author. For commercial use, please contact the author for authorization. For non-commercial use, please indicate the source.\r\n\r\nSnackbar:\r\n  chs_to_cht: You have switched to Traditional Chinese\r\n  cht_to_chs: You have switched to Simplified Chinese\r\n  day_to_night: You have switched to Dark Mode\r\n  night_to_day: You have switched to Light Mode\r\n\r\nloading: Loading...\r\nload_more: Load More\r\n\r\nerror404: Page Not Found\r\n"
  },
  {
    "path": "languages/en.yml",
    "content": "footer:\r\n  framework: Framework\r\n  theme: Theme\r\n\r\ncopy:\r\n  success: Copy Successful\r\n  error: Copy Failed\r\n  noSupport: Browser Not Supported\r\n\r\npage:\r\n  articles: All Articles\r\n  tag: Tag\r\n  category: Category\r\n  archives: Archives\r\n\r\ncard_post_count: comments\r\n\r\nno_title: Untitled\r\n\r\npost:\r\n  created: Created\r\n  updated: Updated\r\n  wordcount: Word Count\r\n  min2read: Reading Time\r\n  min2read_unit: mins\r\n  page_pv: Post Views\r\n  comments: Comments\r\n  copyright:\r\n    author: Author\r\n    link: Link\r\n    copyright_notice: Copyright Notice\r\n    copyright_content: 'All articles on this blog are licensed under <a href=\"%s\">%s</a> unless otherwise stated.'\r\n  recommend: Related Articles\r\n  edit: Edit\r\n  back_to_home: Back to Home\r\n\r\nsearch:\r\n  title: Search\r\n  load_data: Loading Database\r\n  input_placeholder: Search for Posts\r\n  algolia_search:\r\n    hits_empty: 'No results found for: ${query}'\r\n    hits_stats: '${hits} results found in ${time} ms'\r\n  local_search:\r\n    hits_empty: 'No results found for: ${query}'\r\n    hits_stats: '${hits} articles found'\r\n\r\npagination:\r\n  prev: Previous\r\n  next: Next\r\n  page_info: 'Page ${current} of ${total}'\r\n\r\ncomment: Comments\r\n\r\naside:\r\n  articles: Articles\r\n  tags: Tags\r\n  categories: Categories\r\n  card_announcement: Announcement\r\n  card_categories: Categories\r\n  card_tags: Tags\r\n  card_archives: Archives\r\n  card_recent_post: Recent Posts\r\n  card_webinfo:\r\n    headline: Website Info\r\n    article_name: Article Count\r\n    runtime:\r\n      name: Runtime\r\n      unit: days\r\n    last_push_date:\r\n      name: Last Update\r\n    site_wordcount: Total Word Count\r\n    site_uv_name: Unique Visitors\r\n    site_pv_name: Page Views\r\n  more_button: View More\r\n  card_newest_comments:\r\n    headline: Latest Comments\r\n    loading_text: Loading...\r\n    error: Unable to retrieve comments, please check the configuration\r\n    zero: No comments\r\n    image: Image\r\n    link: Link\r\n    code: Code\r\n  card_toc: Contents\r\n  card_post_series: Post Series\r\n\r\ndate_suffix:\r\n  just: Just now\r\n  min: minutes ago\r\n  hour: hours ago\r\n  day: days ago\r\n  month: months ago\r\n\r\ndonate: Sponsor\r\nshare: Share\r\n\r\nrightside:\r\n  readmode_title: Reading Mode\r\n  translate_title: Toggle Between Traditional and Simplified Chinese\r\n  night_mode_title: Toggle Between Light and Dark Mode\r\n  back_to_top: Back to Top\r\n  toc: Table of Contents\r\n  scroll_to_comment: Scroll to Comments\r\n  setting: Settings\r\n  aside: Toggle Between Single-column and Double-column\r\n  chat: Chat\r\n\r\ncopy_copyright:\r\n  author: Author\r\n  link: Link\r\n  source: Source\r\n  info: Copyright belongs to the author. For commercial use, please contact the author for authorization. For non-commercial use, please indicate the source.\r\n\r\nSnackbar:\r\n  chs_to_cht: You have switched to Traditional Chinese\r\n  cht_to_chs: You have switched to Simplified Chinese\r\n  day_to_night: You have switched to Dark Mode\r\n  night_to_day: You have switched to Light Mode\r\n\r\nloading: Loading...\r\nload_more: Load More\r\n\r\nerror404: Page Not Found\r\n"
  },
  {
    "path": "languages/ja.yml",
    "content": "footer:\r\n  framework: フレームワーク\r\n  theme: テーマ\r\n\r\ncopy:\r\n  success: コピー成功\r\n  error: コピー失敗\r\n  noSupport: ブラウザが対応していません\r\n\r\npage:\r\n  articles: 記事一覧\r\n  tag: タグ\r\n  category: カテゴリ\r\n  archives: アーカイブ\r\n\r\ncard_post_count: コメント数\r\n\r\nno_title: タイトルなし\r\n\r\npost:\r\n  created: 作成日\r\n  updated: 更新日\r\n  wordcount: 総文字数\r\n  min2read: 読む時間\r\n  min2read_unit: 分\r\n  page_pv: 閲覧数\r\n  comments: コメント数\r\n  copyright:\r\n    author: 著者\r\n    link: リンク\r\n    copyright_notice: 著作権表示\r\n    copyright_content: 'このブログのすべての記事は、<a href=\"%s\">%s</a> ライセンスの下で提供されており、特に明記されていない限り、すべての権利を留保します。転載時には出典を明記してください: <a href=\"%s\">%s</a>。'\r\n  recommend: 関連記事\r\n  edit: 編集\r\n  back_to_home: ホームに戻る\r\n\r\nsearch:\r\n  title: 検索\r\n  load_data: データベースを読み込んでいます\r\n  input_placeholder: 記事を検索\r\n  algolia_search:\r\n    hits_empty: '${query} の検索結果が見つかりませんでした。'\r\n    hits_stats: '${hits} 件の結果が ${time}ms で見つかりました'\r\n  local_search:\r\n    hits_empty: '${query} の検索結果が見つかりませんでした。'\r\n    hits_stats: '${hits} 件の記事が見つかりました'\r\n\r\npagination:\r\n  prev: 前へ\r\n  next: 次へ\r\n  page_info: '${current} ページ / 合計 ${total} ページ'\r\n\r\ncomment: コメント\r\n\r\naside:\r\n  articles: 記事\r\n  tags: タグ\r\n  categories: カテゴリ\r\n  card_announcement: お知らせ\r\n  card_categories: カテゴリ\r\n  card_tags: タグ\r\n  card_archives: アーカイブ\r\n  card_recent_post: 最近の記事\r\n  card_webinfo:\r\n    headline: サイト情報\r\n    article_name: 記事数\r\n    runtime:\r\n      name: 稼働時間\r\n      unit: 日\r\n    last_push_date:\r\n      name: 最終更新日\r\n    site_wordcount: 総文字数\r\n    site_uv_name: ユーザー数\r\n    site_pv_name: ページビュー数\r\n  more_button: もっと見る\r\n  card_newest_comments:\r\n    headline: 最新コメント\r\n    loading_text: ローディング中...\r\n    error: コメントを取得できませんでした。設定を確認してください。\r\n    zero: コメントがありません\r\n    image: 画像\r\n    link: リンク\r\n    code: コード\r\n  card_toc: 目次\r\n  card_post_series: シリーズ記事\r\n\r\ndate_suffix:\r\n  just: たった今\r\n  min: 分前\r\n  hour: 時間前\r\n  day: 日前\r\n  month: ヶ月前\r\n\r\ndonate: 寄付\r\nshare: 共有\r\n\r\nrightside:\r\n  readmode_title: 読書モード\r\n  translate_title: 簡体字と繁体字の切り替え\r\n  night_mode_title: ライトモード/ダークモード切り替え\r\n  back_to_top: トップに戻る\r\n  toc: 目次\r\n  scroll_to_comment: コメントへ移動\r\n  setting: 設定\r\n  aside: シングルカラムとダブルカラムの切り替え\r\n  chat: チャット\r\n\r\ncopy_copyright:\r\n  author: 著者\r\n  link: リンク\r\n  source: ソース\r\n  info: 著作権は著者に帰属します。商業的利用の場合は著者に連絡して許可を得てください。非商業的利用の場合は出典を明記してください。\r\n\r\nSnackbar:\r\n  chs_to_cht: 繁体字に切り替えました\r\n  cht_to_chs: 簡体字に切り替えました\r\n  day_to_night: ダークモードに切り替えました\r\n  night_to_day: ライトモードに切り替えました\r\n\r\nloading: ローディング中...\r\nload_more: もっと見る\r\n\r\nerror404: ページが見つかりません\r\n"
  },
  {
    "path": "languages/ko.yml",
    "content": "footer:\r\n  framework: 프레임워크\r\n  theme: 테마\r\n\r\ncopy:\r\n  success: 복사 성공\r\n  error: 복사 실패\r\n  noSupport: 브라우저가 지원되지 않음\r\n\r\npage:\r\n  articles: 모든 글\r\n  tag: 태그\r\n  category: 카테고리\r\n  archives: 아카이브\r\n\r\ncard_post_count: 댓글 수\r\n\r\nno_title: 제목 없음\r\n\r\npost:\r\n  created: 작성일\r\n  updated: 수정일\r\n  wordcount: 총 글자 수\r\n  min2read: 읽기 시간\r\n  min2read_unit: 분\r\n  page_pv: 조회수\r\n  comments: 댓글\r\n  copyright:\r\n    author: 작성자\r\n    link: 링크\r\n    copyright_notice: 저작권 고지\r\n    copyright_content: '이 블로그의 모든 글은 <a href=\"%s\">%s</a> 라이선스를 따르며, 별도로 명시되지 않는 한 모든 권리를 보유합니다. 재배포 시 출처를 명시해 주세요: <a href=\"%s\">%s</a>.'\r\n  recommend: 관련 글\r\n  edit: 편집\r\n  back_to_home: 홈으로 돌아가기\r\n\r\nsearch:\r\n  title: 검색\r\n  load_data: 데이터베이스 로드 중\r\n  input_placeholder: 글 검색\r\n  algolia_search:\r\n    hits_empty: '${query}에 대한 결과를 찾을 수 없습니다.'\r\n    hits_stats: '${hits}개의 결과를 ${time}ms 만에 찾음'\r\n  local_search:\r\n    hits_empty: '${query}에 대한 결과를 찾을 수 없습니다.'\r\n    hits_stats: '${hits}개의 글을 찾음'\r\n\r\npagination:\r\n  prev: 이전\r\n  next: 다음\r\n  page_info: '${current} 페이지 / 총 ${total} 페이지'\r\n\r\ncomment: 댓글\r\n\r\naside:\r\n  articles: 글\r\n  tags: 태그\r\n  categories: 카테고리\r\n  card_announcement: 공지\r\n  card_categories: 카테고리\r\n  card_tags: 태그\r\n  card_archives: 아카이브\r\n  card_recent_post: 최근 글\r\n  card_webinfo:\r\n    headline: 사이트 정보\r\n    article_name: 글 수\r\n    runtime:\r\n      name: 운영 시간\r\n      unit: 일\r\n    last_push_date:\r\n      name: 마지막 업데이트\r\n    site_wordcount: 총 글자 수\r\n    site_uv_name: 방문자 수\r\n    site_pv_name: 총 조회수\r\n  more_button: 더 보기\r\n  card_newest_comments:\r\n    headline: 최신 댓글\r\n    loading_text: 로딩 중...\r\n    error: 댓글을 가져올 수 없습니다. 설정을 확인해 주세요.\r\n    zero: 댓글 없음\r\n    image: 이미지\r\n    link: 링크\r\n    code: 코드\r\n  card_toc: 목차\r\n  card_post_series: 시리즈 글\r\n\r\ndate_suffix:\r\n  just: 방금\r\n  min: 분 전\r\n  hour: 시간 전\r\n  day: 일 전\r\n  month: 달 전\r\n\r\ndonate: 후원\r\nshare: 공유\r\n\r\nrightside:\r\n  readmode_title: 읽기 모드\r\n  translate_title: 번체와 간체 전환\r\n  night_mode_title: 라이트/다크 모드 전환\r\n  back_to_top: 맨 위로\r\n  toc: 목차\r\n  scroll_to_comment: 댓글로 이동\r\n  setting: 설정\r\n  aside: 단일/이중 열 전환\r\n  chat: 채팅\r\n\r\ncopy_copyright:\r\n  author: 작성자\r\n  link: 링크\r\n  source: 출처\r\n  info: 저작권은 작성자에게 있습니다. 상업적 사용을 위해서는 작성자의 허가를 받아야 하며, 비상업적 사용 시에는 출처를 명시해 주세요.\r\n\r\nSnackbar:\r\n  chs_to_cht: 번체로 전환되었습니다.\r\n  cht_to_chs: 간체로 전환되었습니다.\r\n  day_to_night: 다크 모드로 전환되었습니다.\r\n  night_to_day: 라이트 모드로 전환되었습니다.\r\n\r\nloading: 로딩 중...\r\nload_more: 더 보기\r\n\r\nerror404: 페이지를 찾을 수 없습니다.\r\n"
  },
  {
    "path": "languages/zh-CN.yml",
    "content": "footer:\r\n  framework: 框架\r\n  theme: 主题\r\n\r\ncopy:\r\n  success: 复制成功\r\n  error: 复制失败\r\n  noSupport: 浏览器不支持\r\n\r\npage:\r\n  articles: 全部文章\r\n  tag: 标签\r\n  category: 分类\r\n  archives: 归档\r\n\r\ncard_post_count: 条评论\r\n\r\nno_title: 无标题\r\n\r\npost:\r\n  created: 发表于\r\n  updated: 更新于\r\n  wordcount: 总字数\r\n  min2read: 阅读时长\r\n  min2read_unit: 分钟\r\n  page_pv: 浏览量\r\n  comments: 评论数\r\n  copyright:\r\n    author: 文章作者\r\n    link: 文章链接\r\n    copyright_notice: 版权声明\r\n    copyright_content: '本博客所有文章除特别声明外，均采用\r\n      <a href=\"%s\" target=\"_blank\">%s</a> 许可协议。转载请注明来源 <a href=\"%s\" target=\"_blank\">%s</a>！'\r\n  recommend: 相关推荐\r\n  edit: 编辑\r\n  back_to_home: 返回首页\r\n\r\nsearch:\r\n  title: 搜索\r\n  load_data: 数据加载中\r\n  input_placeholder: 搜索文章\r\n  algolia_search:\r\n    hits_empty: '未找到符合您查询的内容：${query}'\r\n    hits_stats: '找到 ${hits} 条结果，耗时 ${time} 毫秒'\r\n  local_search:\r\n    hits_empty: '未找到符合您查询的内容：${query}'\r\n    hits_stats: '共找到 ${hits} 篇文章'\r\n\r\npagination:\r\n  prev: 上一篇\r\n  next: 下一篇\r\n  page_info: '第 ${current} 页 / 共 ${total} 页'\r\n\r\ncomment: 评论\r\n\r\naside:\r\n  articles: 文章\r\n  tags: 标签\r\n  categories: 分类\r\n  card_announcement: 公告\r\n  card_categories: 分类\r\n  card_tags: 标签\r\n  card_archives: 归档\r\n  card_recent_post: 最新文章\r\n  card_webinfo:\r\n    headline: 网站信息\r\n    article_name: 文章数目\r\n    runtime:\r\n      name: 运行时间\r\n      unit: 天\r\n    last_push_date:\r\n      name: 最后更新时间\r\n    site_wordcount: 本站总字数\r\n    site_uv_name: 本站访客数\r\n    site_pv_name: 本站总浏览量\r\n  more_button: 查看更多\r\n  card_newest_comments:\r\n    headline: 最新评论\r\n    loading_text: 加载中...\r\n    error: 无法获取评论，请确认相关配置是否正确\r\n    zero: 暂无评论\r\n    image: 图片\r\n    link: 链接\r\n    code: 代码\r\n  card_toc: 目录\r\n  card_post_series: 系列文章\r\n\r\ndate_suffix:\r\n  just: 刚刚\r\n  min: 分钟前\r\n  hour: 小时前\r\n  day: 天前\r\n  month: 个月前\r\n\r\ndonate: 赞助\r\nshare: 分享\r\n\r\nrightside:\r\n  readmode_title: 阅读模式\r\n  translate_title: 简繁转换\r\n  night_mode_title: 日间和夜间模式切换\r\n  back_to_top: 回到顶部\r\n  toc: 目录\r\n  scroll_to_comment: 前往评论\r\n  setting: 设置\r\n  aside: 单栏和双栏切换\r\n  chat: 聊天\r\n\r\ncopy_copyright:\r\n  author: 作者\r\n  link: 链接\r\n  source: 来源\r\n  info: 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。\r\n\r\nSnackbar:\r\n  chs_to_cht: 已切换为繁体中文\r\n  cht_to_chs: 已切换为简体中文\r\n  day_to_night: 已切换为深色模式\r\n  night_to_day: 已切换为浅色模式\r\n\r\nloading: 加载中...\r\nload_more: 加载更多\r\n\r\nerror404: 页面未找到\r\n"
  },
  {
    "path": "languages/zh-HK.yml",
    "content": "footer:\r\n  framework: 框架\r\n  theme: 主題\r\n\r\ncopy:\r\n  success: 複製成功\r\n  error: 複製失敗\r\n  noSupport: 瀏覽器不支援\r\n\r\npage:\r\n  articles: 全部文章\r\n  tag: 標籤\r\n  category: 分類\r\n  archives: 歸檔\r\n\r\ncard_post_count: 條評論\r\n\r\nno_title: 無標題\r\n\r\npost:\r\n  created: 發表於\r\n  updated: 更新於\r\n  wordcount: 字數統計\r\n  min2read: 閱讀時間\r\n  min2read_unit: 分鐘\r\n  page_pv: 瀏覽量\r\n  comments: 評論數\r\n  copyright:\r\n    author: 文章作者\r\n    link: 文章連結\r\n    copyright_notice: 版權聲明\r\n    copyright_content: '除特別聲明外，本博客所有文章均採用<a href=\"%s\">%s</a> 授權協議。轉載請註明出處：<a href=\"%s\">%s</a>。'\r\n  recommend: 相關文章\r\n  edit: 編輯\r\n  back_to_home: 返回首頁\r\n\r\nsearch:\r\n  title: 搜尋\r\n  load_data: 正在加載數據庫\r\n  input_placeholder: 搜尋文章\r\n  algolia_search:\r\n    hits_empty: '未找到相關內容：${query}'\r\n    hits_stats: '找到 ${hits} 條結果，耗時 ${time} 毫秒'\r\n  local_search:\r\n    hits_empty: '未找到相關內容：${query}'\r\n    hits_stats: '找到 ${hits} 篇文章'\r\n\r\npagination:\r\n  prev: 上一頁\r\n  next: 下一頁\r\n  page_info: '第 ${current} 頁 / 共 ${total} 頁'\r\n\r\ncomment: 評論\r\n\r\naside:\r\n  articles: 文章\r\n  tags: 標籤\r\n  categories: 分類\r\n  card_announcement: 公告\r\n  card_categories: 分類\r\n  card_tags: 標籤\r\n  card_archives: 歸檔\r\n  card_recent_post: 最新文章\r\n  card_webinfo:\r\n    headline: 網站資訊\r\n    article_name: 文章數目\r\n    runtime:\r\n      name: 運行時間\r\n      unit: 天\r\n    last_push_date:\r\n      name: 最後更新時間\r\n    site_wordcount: 總字數\r\n    site_uv_name: 訪客數\r\n    site_pv_name: 總瀏覽量\r\n  more_button: 查看更多\r\n  card_newest_comments:\r\n    headline: 最新評論\r\n    loading_text: 正在加載...\r\n    error: 無法取得評論，請確認配置是否正確\r\n    zero: 暫無評論\r\n    image: 圖片\r\n    link: 連結\r\n    code: 代碼\r\n  card_toc: 目錄\r\n  card_post_series: 系列文章\r\n\r\ndate_suffix:\r\n  just: 剛剛\r\n  min: 分鐘前\r\n  hour: 小時前\r\n  day: 天前\r\n  month: 個月前\r\n\r\ndonate: 贊助\r\nshare: 分享\r\n\r\nrightside:\r\n  readmode_title: 閱讀模式\r\n  translate_title: 簡繁轉換\r\n  night_mode_title: 切換日夜模式\r\n  back_to_top: 回到頂部\r\n  toc: 目錄\r\n  scroll_to_comment: 前往評論\r\n  setting: 設定\r\n  aside: 單欄與雙欄切換\r\n  chat: 聊天\r\n\r\ncopy_copyright:\r\n  author: 作者\r\n  link: 連結\r\n  source: 來源\r\n  info: 版權屬於作者所有。商業用途請聯絡作者獲得授權，非商業用途請註明出處。\r\n\r\nSnackbar:\r\n  chs_to_cht: 已切換為繁體中文\r\n  cht_to_chs: 已切換為簡體中文\r\n  day_to_night: 已切換為深色模式\r\n  night_to_day: 已切換為淺色模式\r\n\r\nloading: 正在加載...\r\nload_more: 加載更多\r\n\r\nerror404: 未找到頁面\r\n"
  },
  {
    "path": "languages/zh-TW.yml",
    "content": "footer:\r\n  framework: 框架\r\n  theme: 主題\r\n\r\ncopy:\r\n  success: 複製成功\r\n  error: 複製失敗\r\n  noSupport: 瀏覽器不支援\r\n\r\npage:\r\n  articles: 所有文章\r\n  tag: 標籤\r\n  category: 分類\r\n  archives: 歸檔\r\n\r\ncard_post_count: 則評論\r\n\r\nno_title: 無標題\r\n\r\npost:\r\n  created: 發表於\r\n  updated: 更新於\r\n  wordcount: 總字數\r\n  min2read: 閱讀時間\r\n  min2read_unit: 分鐘\r\n  page_pv: 瀏覽量\r\n  comments: 評論數\r\n  copyright:\r\n    author: 文章作者\r\n    link: 文章連結\r\n    copyright_notice: 版權聲明\r\n    copyright_content: '本部落格所有文章除特別聲明外，均採用<a href=\"%s\" target=\"_blank\">%s</a> 授權協議。轉載請註明來源 <a href=\"%s\" target=\"_blank\">%s</a>！'\r\n  recommend: 相關推薦\r\n  edit: 編輯\r\n  back_to_home: 返回首頁\r\n\r\nsearch:\r\n  title: 搜尋\r\n  load_data: 資料載入中\r\n  input_placeholder: 搜尋文章\r\n  algolia_search:\r\n    hits_empty: '找不到符合您查詢的內容：${query}'\r\n    hits_stats: '找到 ${hits} 筆結果，耗時 ${time} 毫秒'\r\n  local_search:\r\n    hits_empty: '找不到符合您查詢的內容：${query}'\r\n    hits_stats: '共找到 ${hits} 篇文章'\r\n\r\npagination:\r\n  prev: 上一篇\r\n  next: 下一篇\r\n  page_info: '第 ${current} 頁 / 共 ${total} 頁'\r\n\r\ncomment: 評論\r\n\r\naside:\r\n  articles: 文章\r\n  tags: 標籤\r\n  categories: 分類\r\n  card_announcement: 公告\r\n  card_categories: 分類\r\n  card_tags: 標籤\r\n  card_archives: 歸檔\r\n  card_recent_post: 最新文章\r\n  card_webinfo:\r\n    headline: 網站資訊\r\n    article_name: 文章數量\r\n    runtime:\r\n      name: 運行時間\r\n      unit: 天\r\n    last_push_date:\r\n      name: 最後更新時間\r\n    site_wordcount: 總字數\r\n    site_uv_name: 訪客數\r\n    site_pv_name: 總瀏覽量\r\n  more_button: 檢視更多\r\n  card_newest_comments:\r\n    headline: 最新評論\r\n    loading_text: 載入中...\r\n    error: 無法獲取評論，請確認相關配置是否正確\r\n    zero: 尚無評論\r\n    image: 圖片\r\n    link: 連結\r\n    code: 程式碼\r\n  card_toc: 目錄\r\n  card_post_series: 系列文章\r\n\r\ndate_suffix:\r\n  just: 剛剛\r\n  min: 分鐘前\r\n  hour: 小時前\r\n  day: 天前\r\n  month: 個月前\r\n\r\ndonate: 贊助\r\nshare: 分享\r\n\r\nrightside:\r\n  readmode_title: 閱讀模式\r\n  translate_title: 繁簡轉換\r\n  night_mode_title: 日夜模式切換\r\n  back_to_top: 回到頂端\r\n  toc: 目錄\r\n  scroll_to_comment: 前往評論\r\n  setting: 設定\r\n  aside: 單欄和雙欄切換\r\n  chat: 聊天\r\n\r\ncopy_copyright:\r\n  author: 作者\r\n  link: 連結\r\n  source: 來源\r\n  info: 著作權歸作者所有。如需商業轉載，請聯絡作者獲得授權，非商業轉載請註明出處。\r\n\r\nSnackbar:\r\n  chs_to_cht: 已切換為繁體中文\r\n  cht_to_chs: 已切換為簡體中文\r\n  day_to_night: 已切換為深色模式\r\n  night_to_day: 已切換為淺色模式\r\n\r\nloading: 載入中...\r\nload_more: 載入更多\r\n\r\nerror404: 找不到頁面\r\n"
  },
  {
    "path": "layout/archive.pug",
    "content": "extends includes/layout.pug\r\n\r\nblock content\r\n  include ./includes/mixins/article-sort.pug\r\n  #archive\r\n    .article-sort-title= `${_p('page.articles')} - ${getArchiveLength()}`\r\n    +articleSort(page.posts)\r\n    include includes/pagination.pug"
  },
  {
    "path": "layout/category.pug",
    "content": "extends includes/layout.pug\r\n\r\nblock content\r\n  if theme.category_ui == 'index'\r\n    include ./includes/mixins/indexPostUI.pug\r\n    +indexPostUI\r\n  else\r\n    include ./includes/mixins/article-sort.pug\r\n    #category\r\n      .article-sort-title= _p('page.category') + ' - ' + page.category\r\n      +articleSort(page.posts)\r\n      include includes/pagination.pug"
  },
  {
    "path": "layout/includes/additional-js.pug",
    "content": "div\r\n  script(src=url_for(theme.asset.utils))\r\n  script(src=url_for(theme.asset.main))\r\n\r\n  if theme.translate.enable\r\n    script(src=url_for(theme.asset.translate))\r\n\r\n  if theme.lightbox\r\n    script(src=url_for(theme.asset[theme.lightbox]))\r\n\r\n  if theme.instantpage\r\n    script(src=url_for(theme.asset.instantpage), type='module')\r\n\r\n  if theme.lazyload.enable && !theme.lazyload.native\r\n    script(src=url_for(theme.asset.lazyload))\r\n\r\n  if theme.snackbar.enable\r\n    script(src=url_for(theme.asset.snackbar))\r\n\r\n  .js-pjax\r\n    if needLoadCountJs\r\n      != partial(\"includes/third-party/card-post-count/index\", {}, { cache: true })\r\n\r\n    if loadSubJs\r\n      include ./third-party/subtitle.pug\r\n\r\n    include ./third-party/math/index.pug\r\n    include ./third-party/abcjs/index.pug\r\n\r\n    if commentsJsLoad\r\n      include ./third-party/comments/js.pug\r\n\r\n  != partial(\"includes/third-party/prismjs\", {}, { cache: true })\r\n\r\n  if theme.aside.enable && theme.aside.card_newest_comments.enable\r\n    if theme.pjax.enable || (globalPageType !== 'post' && page.aside !== false)\r\n      != partial(\"includes/third-party/newest-comments/index\", {}, { cache: true })\r\n\r\n  != fragment_cache('injectBottom', function(){return injectHtml(theme.inject.bottom)})\r\n\r\n  != partial(\"includes/third-party/effect\", {}, { cache: true })\r\n  != partial(\"includes/third-party/chat/index\", {}, { cache: true })\r\n\r\n  if theme.aplayerInject && theme.aplayerInject.enable\r\n    if theme.pjax.enable || theme.aplayerInject.per_page || page.aplayer\r\n      include ./third-party/aplayer.pug\r\n\r\n  if theme.pjax.enable\r\n    != partial(\"includes/third-party/pjax\", {}, { cache: true })\r\n\r\n  if theme.umami_analytics.enable\r\n    != partial(\"includes/third-party/umami_analytics\", {}, { cache: true })\r\n\r\n  if theme.busuanzi.site_uv || theme.busuanzi.site_pv || theme.busuanzi.page_pv\r\n    script(async data-pjax src=theme.asset.busuanzi ? url_for(theme.asset.busuanzi) : '//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js')\r\n\r\n  != partial('includes/third-party/search/index', {}, { cache: true })\r\n\r\n  if theme.google_tag_manager && theme.google_tag_manager.tag_id\r\n    noscript\r\n      iframe(src=`${theme.google_tag_manager.domain ? theme.google_tag_manager.domain : 'https://www.googletagmanager.com'}/ns.html?id=${theme.google_tag_manager.tag_id}` height=\"0\" width=\"0\" style=\"display:none;visibility:hidden\")"
  },
  {
    "path": "layout/includes/footer.pug",
    "content": "- const { nav, owner, copyright, custom_text } = theme.footer\r\n\r\nif nav\r\n  .footer-flex\r\n    for block in nav\r\n      .footer-flex-items(style=`${ block.width ? 'flex-grow:' + block.width : '' }`)\r\n        for blockItem in block.content\r\n          .footer-flex-item\r\n            .footer-flex-title= blockItem.title\r\n            .footer-flex-content\r\n              for subitem in blockItem.item\r\n                if subitem.html\r\n                  div!= subitem.html\r\n                else if subitem.url\r\n                  a(href=url_for(subitem.url), target='_blank' title=subitem.title)= subitem.title\r\n                else if subitem.title\r\n                  div!= subitem.title\r\n.footer-other\r\n  .footer-copyright\r\n    if owner.enable\r\n      - const currentYear = new Date().getFullYear()\r\n      - const sinceYear = owner.since\r\n      span.copyright\r\n        if sinceYear && sinceYear != currentYear\r\n          != `&copy;&nbsp;${sinceYear} - ${currentYear} By ${config.author}`\r\n        else\r\n          != `&copy;&nbsp;${currentYear} By ${config.author}`\r\n    if copyright.enable\r\n      - const v = copyright.version ? getVersion() : false\r\n      span.framework-info\r\n        if owner.enable && nav\r\n          span.footer-separator |\r\n        span= _p('footer.framework') + ' '\r\n        a(href='https://hexo.io')= `Hexo${ v ? ' ' + v.hexo : '' }`\r\n        span.footer-separator |\r\n        span= _p('footer.theme') + ' '\r\n        a(href='https://github.com/jerryc127/hexo-theme-butterfly')= `Butterfly${ v ? ' ' + v.theme : '' }`\r\n  if theme.footer.custom_text\r\n    .footer_custom_text!= theme.footer.custom_text\r\n"
  },
  {
    "path": "layout/includes/head/Open_Graph.pug",
    "content": "if theme.Open_Graph_meta.enable\r\n  -\r\n    const coverVal = page.cover_type === 'img' ? page.cover : theme.avatar.img\r\n    let ogOption = Object.assign({\r\n      type: globalPageType === 'post' ? 'article' : 'website',\r\n      image: coverVal ? full_url_for(coverVal) : '',\r\n      fb_admins: theme.facebook_comments.user_id || '',\r\n      fb_app_id: theme.facebook_comments.app_id || '',\r\n    }, theme.Open_Graph_meta.option)\r\n  -\r\n  != open_graph(ogOption)\r\nelse\r\n  - const description = page.description || page.content || page.title || config.description\r\n  if description\r\n    meta(name=\"description\" content=truncate(description, 150))\r\n\r\n"
  },
  {
    "path": "layout/includes/head/analytics.pug",
    "content": "if theme.baidu_analytics\r\n  script.\r\n    var _hmt = _hmt || [];\r\n    (function() {\r\n      var hm = document.createElement(\"script\");\r\n      hm.src = \"https://hm.baidu.com/hm.js?!{theme.baidu_analytics}\";\r\n      var s = document.getElementsByTagName(\"script\")[0];\r\n      s.parentNode.insertBefore(hm, s);\r\n    })();\r\n    btf.addGlobalFn('pjaxComplete', () => {\r\n      _hmt.push(['_trackPageview',window.location.pathname])\r\n    }, 'baidu_analytics')\r\n\r\nif theme.google_analytics\r\n  script(async src=`https://www.googletagmanager.com/gtag/js?id=${theme.google_analytics}`)\r\n  script.\r\n    window.dataLayer = window.dataLayer || []\r\n    function gtag(){dataLayer.push(arguments)}\r\n    gtag('js', new Date())\r\n    gtag('config', '!{theme.google_analytics}')\r\n    btf.addGlobalFn('pjaxComplete', () => {\r\n      gtag('config', '!{theme.google_analytics}', {'page_path': window.location.pathname})\r\n    }, 'google_analytics')\r\n\r\nif theme.cloudflare_analytics\r\n  script(defer data-pjax src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon=`{\"token\": \"${theme.cloudflare_analytics}\"}`)\r\n\r\nif theme.microsoft_clarity\r\n  script.\r\n    (function(c,l,a,r,i,t,y){\r\n        c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};\r\n        t=l.createElement(r);t.async=1;t.src=\"https://www.clarity.ms/tag/\"+i;\r\n        y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);\r\n    })(window, document, \"clarity\", \"script\", \"!{theme.microsoft_clarity}\");\r\n\r\nif (theme.google_tag_manager && theme.google_tag_manager.tag_id)\r\n  script.\r\n    (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':\r\n    new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],\r\n    j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=\r\n    \"!{theme.google_tag_manager.domain ? theme.google_tag_manager.domain : 'https://www.googletagmanager.com'}/gtm.js?id=\"+i+dl;f.parentNode.insertBefore(j,f);\r\n    })(window,document,'script','dataLayer','!{theme.google_tag_manager.tag_id}');\r\n    btf.addGlobalFn('pjaxComplete', () => {\r\n      dataLayer.push({'event': 'pjaxComplete', 'page_title': document.title, 'page_location': location.href, 'page_path': window.location.pathname})\r\n    }, 'google_tag_manager')"
  },
  {
    "path": "layout/includes/head/config.pug",
    "content": "-\r\n  let algolia = 'undefined'\r\n  if (theme.search.use === 'algolia_search') {\r\n    const { ALGOLIA_APP_ID, ALGOLIA_API_KEY, ALGOLIA_INDEX_NAME } = process.env\r\n    const { appId, applicationID, apiKey, indexName } = config.algolia\r\n    algolia = JSON.stringify({\r\n      appId: ALGOLIA_APP_ID || appId || applicationID,\r\n      apiKey: ALGOLIA_API_KEY || apiKey,\r\n      indexName: ALGOLIA_INDEX_NAME || indexName,\r\n      hitsPerPage: theme.search.algolia_search.hitsPerPage,\r\n      // search languages\r\n      languages: {\r\n        hits_empty: _p(\"search.algolia_search.hits_empty\"),\r\n        hits_stats: _p(\"search.algolia_search.hits_stats\"),\r\n      }\r\n    })\r\n  }\r\n\r\n  let localSearch = 'undefined'\r\n  if (theme.search.use === 'local_search') {\r\n    const { CDN, preload, top_n_per_article, pagination, unescape } = theme.search.local_search\r\n    localSearch = JSON.stringify({\r\n      path: CDN || config.root + config.search.path,\r\n      preload,\r\n      top_n_per_article,\r\n      unescape,\r\n      pagination: {\r\n        enable: pagination.enable,\r\n        hitsPerPage: pagination.hitsPerPage\r\n      },\r\n      languages: {\r\n        // search languages\r\n        hits_empty: _p(\"search.local_search.hits_empty\"),\r\n        hits_stats: _p(\"search.local_search.hits_stats\"),\r\n      }\r\n    })\r\n  }\r\n\r\n  let translate = 'undefined'\r\n  if (theme.translate && theme.translate.enable){\r\n    translate = JSON.stringify({\r\n      defaultEncoding: theme.translate.defaultEncoding,\r\n      translateDelay: theme.translate.translateDelay,\r\n      msgToTraditionalChinese: theme.translate.msgToTraditionalChinese,\r\n      msgToSimplifiedChinese: theme.translate.msgToSimplifiedChinese\r\n    })\r\n  }\r\n\r\n  let copyright = 'undefined'\r\n  if (theme.copy.enable && theme.copy.copyright.enable){\r\n    copyright = JSON.stringify({\r\n      limitCount: theme.copy.copyright.limit_count,\r\n      languages: {\r\n        author: _p(\"copy_copyright.author\") + ': ' + config.author,\r\n        link: _p(\"copy_copyright.link\") + ': ',\r\n        source: _p(\"copy_copyright.source\") + ': ' + config.title,\r\n        info: _p(\"copy_copyright.info\")\r\n      }\r\n    })\r\n  }\r\n\r\n  let Snackbar = 'undefined'\r\n  if (theme.snackbar && theme.snackbar.enable) {\r\n    Snackbar = JSON.stringify({\r\n      chs_to_cht: _p(\"Snackbar.chs_to_cht\"),\r\n      cht_to_chs: _p(\"Snackbar.cht_to_chs\"),\r\n      day_to_night: _p(\"Snackbar.day_to_night\"),\r\n      night_to_day: _p(\"Snackbar.night_to_day\"),\r\n      bgLight: theme.snackbar.bg_light,\r\n      bgDark: theme.snackbar.bg_dark,\r\n      position: theme.snackbar.position,\r\n    })\r\n  }\r\n\r\n  let highlightProvider = config.syntax_highlighter || (config.highlight.enable ? 'highlight.js' : config.prismjs.enable ? 'prismjs' : null)\r\n  const { copy, language, height_limit, fullpage, macStyle, shrink } = theme.code_blocks\r\n  let highlight = JSON.stringify({\r\n    plugin: highlightProvider,\r\n    highlightCopy: copy,\r\n    highlightLang: language,\r\n    highlightHeightLimit: height_limit,\r\n    highlightFullpage: fullpage,\r\n    highlightMacStyle: macStyle\r\n  })\r\n\r\nscript.\r\n  const GLOBAL_CONFIG = {\r\n    root: '!{config.root}',\r\n    algolia: !{algolia},\r\n    localSearch: !{localSearch},\r\n    translate: !{translate},\r\n    highlight: !{highlight},\r\n    copy: {\r\n      success: '!{_p(\"copy.success\")}',\r\n      error: '!{_p(\"copy.error\")}',\r\n      noSupport: '!{_p(\"copy.noSupport\")}'\r\n    },\r\n    relativeDate: {\r\n      homepage: !{theme.post_meta.page.date_format === 'relative'},\r\n      post: !{theme.post_meta.post.date_format === 'relative'}\r\n    },\r\n    runtime: '!{theme.aside.card_webinfo.runtime_date ? _p(\"aside.card_webinfo.runtime.unit\") : \"\"}',\r\n    dateSuffix: {\r\n      just: '!{_p(\"date_suffix.just\")}',\r\n      min: '!{_p(\"date_suffix.min\")}',\r\n      hour: '!{_p(\"date_suffix.hour\")}',\r\n      day: '!{_p(\"date_suffix.day\")}',\r\n      month: '!{_p(\"date_suffix.month\")}'\r\n    },\r\n    copyright: !{copyright},\r\n    lightbox: '!{ theme.lightbox || 'null' }',\r\n    Snackbar: !{Snackbar},\r\n    infinitegrid: {\r\n      js: '!{url_for(theme.asset.egjs_infinitegrid)}',\r\n      buttonText: '!{_p(\"load_more\")}'\r\n    },\r\n    isPhotoFigcaption: !{theme.photofigcaption},\r\n    islazyloadPlugin: !{theme.lazyload.enable && !theme.lazyload.native},\r\n    isAnchor: !{theme.anchor.auto_update || false},\r\n    percent: {\r\n      toc: !{theme.toc.scroll_percent},\r\n      rightside: !{theme.rightside_scroll_percent},\r\n    },\r\n    autoDarkmode: !{theme.darkmode.enable && theme.darkmode.autoChangeMode === 1}\r\n  }\r\n"
  },
  {
    "path": "layout/includes/head/config_site.pug",
    "content": "-\r\n  const titleVal = pageTitle.replace(/'/ig,\"\\\\'\")\r\n\r\n  let isHighlightShrink\r\n  if (theme.code_blocks.shrink == 'none') isHighlightShrink = 'undefined'\r\n  else if (typeof page.highlight_shrink == 'boolean') isHighlightShrink = page.highlight_shrink\r\n  else isHighlightShrink = theme.code_blocks.shrink\r\n\r\n  var showToc = false\r\n  if (theme.aside.enable && page.aside !== false) {\r\n    let tocEnable = false\r\n    if (globalPageType === 'post' && theme.toc.post) tocEnable = true\r\n    else if (globalPageType === 'page' && theme.toc.page) tocEnable = true\r\n    const pageToc = typeof page.toc === 'boolean' ? page.toc : tocEnable\r\n    showToc = pageToc && (toc(page.content) !== '' || page.encrypt === true)\r\n  }\r\n-\r\n\r\nscript#config-diff.\r\n  var GLOBAL_CONFIG_SITE = {\r\n    title: '!{titleVal}',\r\n    isHighlightShrink: !{isHighlightShrink},\r\n    isToc: !{showToc},\r\n    pageType: '!{page.type == 'shuoshuo' ? 'shuoshuo' : globalPageType}'\r\n  }\r\n"
  },
  {
    "path": "layout/includes/head/google_adsense.pug",
    "content": "if (theme.google_adsense && theme.google_adsense.enable)\r\n  script(async src=theme.google_adsense.js)\r\n\r\n  if theme.google_adsense.auto_ads\r\n    script.\r\n      (adsbygoogle = window.adsbygoogle || []).push({\r\n        google_ad_client: '!{theme.google_adsense.client}',\r\n        enable_page_level_ads: '!{theme.google_adsense.enable_page_level_ads}'\r\n      });"
  },
  {
    "path": "layout/includes/head/preconnect.pug",
    "content": "-\r\n  const { internal_provider, third_party_provider, custom_format } = theme.CDN\r\n  const providers = {\r\n    'jsdelivr': '//cdn.jsdelivr.net',\r\n    'cdnjs': '//cdnjs.cloudflare.com',\r\n    'unpkg': '//unpkg.com',\r\n    'custom': custom_format && custom_format.match(/^((https?:)?(\\/\\/[^/]+)|([^/]+))(\\/|$)/)[1]\r\n  }\r\n-\r\n\r\nif internal_provider === third_party_provider && internal_provider !== 'local'\r\n  link(rel=\"preconnect\" href=providers[internal_provider])\r\nelse\r\n  if internal_provider !== 'local'\r\n    link(rel=\"preconnect\" href=providers[internal_provider])\r\n  if third_party_provider !== 'local'\r\n  link(rel=\"preconnect\" href=providers[third_party_provider])\r\n\r\nif theme.google_analytics\r\n  link(rel=\"preconnect\" href=\"//www.google-analytics.com\" crossorigin='')\r\n\r\nif theme.baidu_analytics\r\n  link(rel=\"preconnect\" href=\"//hm.baidu.com\")\r\n\r\nif theme.cloudflare_analytics\r\n  link(rel=\"preconnect\" href=\"//static.cloudflareinsights.com\")\r\n\r\nif theme.microsoft_clarity\r\n  link(rel=\"preconnect\" href=\"//www.clarity.ms\")\r\n\r\nif theme.blog_title_font && theme.blog_title_font.font_link && theme.blog_title_font.font_link.indexOf('//fonts.googleapis.com') != -1\r\n  link(rel=\"preconnect\" href=\"//fonts.googleapis.com\" crossorigin='')\r\n\r\nif !theme.asset.busuanzi && (theme.busuanzi.site_uv || theme.busuanzi.site_pv || theme.busuanzi.page_pv)\r\n  link(rel=\"preconnect\" href=\"//busuanzi.ibruce.info\")"
  },
  {
    "path": "layout/includes/head/pwa.pug",
    "content": "- const { manifest, theme_color, apple_touch_icon, favicon_32_32, favicon_16_16, mask_icon } = theme.pwa\r\n\r\nlink(rel=\"manifest\" href=url_for(manifest))\r\nif theme_color\r\n  meta(name=\"msapplication-TileColor\" content=theme_color)\r\nif apple_touch_icon\r\n  link(rel=\"apple-touch-icon\" sizes=\"180x180\" href=url_for(apple_touch_icon))\r\nif favicon_32_32\r\n  link(rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=url_for(favicon_32_32))\r\nif favicon_16_16\r\n  link(rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=url_for(favicon_16_16))\r\nif mask_icon\r\n  link(rel=\"mask-icon\" href=url_for(mask_icon) color=\"#5bbad5\")\r\n"
  },
  {
    "path": "layout/includes/head/site_verification.pug",
    "content": "if theme.site_verification\r\n  each item in theme.site_verification\r\n    meta(name=item.name content=item.content)"
  },
  {
    "path": "layout/includes/head/structured_data.pug",
    "content": "if theme.structured_data\r\n  if page.layout === 'post'\r\n    -\r\n      // https://developers.google.com/search/docs/appearance/structured-data/article\r\n\r\n      const title = page.title\r\n      const url = page.permalink\r\n      const imageVal = page.cover_type === 'img' ? page.cover : theme.avatar.img\r\n      const image = imageVal ? full_url_for(imageVal) : ''\r\n      const datePublished = page.date.toISOString()\r\n      const dateModified = (page.updated || page.date).toISOString()\r\n      const author = page.copyright_author || config.author\r\n      const authorHrefVal = page.copyright_author_href || theme.post_copyright.author_href || config.url\r\n      const authorHref = full_url_for(authorHrefVal)\r\n\r\n      const jsonLd = {\r\n        \"@context\": \"https://schema.org\",\r\n        \"@type\": \"BlogPosting\",\r\n        \"headline\": title,\r\n        \"url\": url,\r\n        \"image\": image,\r\n        \"datePublished\": datePublished,\r\n        \"dateModified\": dateModified,\r\n        \"author\": [{\r\n          \"@type\": \"Person\",\r\n          \"name\": author,\r\n          \"url\": authorHref\r\n        }]\r\n      }\r\n\r\n      jsonLdScript = JSON.stringify(jsonLd, null, 2)\r\n    -\r\n\r\n  else if is_home() && (!page.current || page.current === 1)\r\n    -\r\n      // https://developers.google.com/search/docs/appearance/site-names#website\r\n\r\n      const baseUrl = config.url;\r\n      const currentPath = url_for('/');\r\n      const isRootOrSubdomain = currentPath.split('/').filter(Boolean).length === 0;\r\n\r\n      if (isRootOrSubdomain) {\r\n        const domain = new URL(config.url).hostname;\r\n        const alternateNames = theme.structured_data.alternate_name || [];\r\n\r\n        if (config.subtitle) {\r\n          alternateNames.push(config.subtitle);\r\n        }\r\n\r\n        if (domain) {\r\n          alternateNames.push(domain);\r\n        }\r\n\r\n        const jsonLd = {\r\n          \"@context\": \"https://schema.org\",\r\n          \"@type\": \"WebSite\",\r\n          \"name\": config.title,\r\n          \"alternateName\": alternateNames,\r\n          \"url\": full_url_for('/'),\r\n        }\r\n\r\n        jsonLdScript = JSON.stringify(jsonLd, null, 2)\r\n      }\r\n    -\r\n\r\n  script(type=\"application/ld+json\").\r\n    !{jsonLdScript}\r\n"
  },
  {
    "path": "layout/includes/head.pug",
    "content": "- var pageTitle\r\n- globalPageType === 'archive' ? page.title = findArchivesTitle(page, theme.menu, date) : ''\r\ncase globalPageType\r\n  when 'tag'\r\n    - pageTitle = _p('page.tag') + ': ' + page.tag\r\n  when 'category'\r\n    - pageTitle = _p('page.category') + ': ' + page.category\r\n  when '404'\r\n    - pageTitle = _p('error404')\r\n  default\r\n    - pageTitle = page.title || config.title || ''\r\n\r\n\r\n- var isSubtitle = config.subtitle ? ' - ' + config.subtitle : ''\r\n- var tabTitle = globalPageType === 'home' || !pageTitle ? config.title + isSubtitle : pageTitle + ' | ' + config.title\r\n- var pageAuthor = config.email ? config.author + ',' + config.email : config.author\r\n- var pageCopyright = config.copyright || config.author\r\n- var themeColorLight = theme.theme_color && theme.theme_color.enable && theme.theme_color.meta_theme_color_light || '#ffffff'\r\n- var themeColorDark = theme.theme_color && theme.theme_color.enable && theme.theme_color.meta_theme_color_dark || '#0d0d0d'\r\n- var themeColor = theme.display_mode === 'dark' ? themeColorDark : themeColorLight\r\n\r\nmeta(charset='UTF-8')\r\nmeta(http-equiv=\"X-UA-Compatible\" content=\"IE=edge\")\r\nmeta(name=\"viewport\" content=\"width=device-width, initial-scale=1.0,viewport-fit=cover\")\r\ntitle= tabTitle\r\nmeta(name=\"author\" content=pageAuthor)\r\nmeta(name=\"copyright\" content=pageCopyright)\r\nmeta(name =\"format-detection\" content=\"telephone=no\")\r\nmeta(name=\"theme-color\" content=themeColor)\r\n\r\n//- Open_Graph\r\ninclude ./head/Open_Graph.pug\r\n\r\n//- Structured Data\r\ninclude ./head/structured_data.pug\r\n\r\n!=favicon_tag(theme.favicon || config.favicon)\r\nlink(rel=\"canonical\" href=urlNoIndex(null,config.pretty_urls.trailing_index,config.pretty_urls.trailing_html))\r\n\r\n//- 預解析\r\n!=partial('includes/head/preconnect', {}, {cache: true})\r\n\r\n//- 網站驗證\r\n!=partial('includes/head/site_verification', {}, {cache: true})\r\n\r\n//- PWA\r\nif (theme.pwa && theme.pwa.enable)\r\n  !=partial('includes/head/pwa', {}, {cache: true})\r\n\r\n//- main css\r\nlink(rel='stylesheet', href=url_for(theme.asset.main_css))\r\nlink(rel='stylesheet', href=url_for(theme.asset.fontawesome))\r\n\r\nif (theme.snackbar && theme.snackbar.enable)\r\n  link(rel='stylesheet', href=url_for(theme.asset.snackbar_css) media=\"print\" onload=\"this.media='all'\")\r\n\r\nif theme.lightbox === 'fancybox'\r\n  link(rel='stylesheet' href=url_for(theme.asset.fancybox_css) media=\"print\" onload=\"this.media='all'\")\r\n\r\n!=fragment_cache('injectHeadJs', function(){return inject_head_js()})\r\n\r\n//- google_adsense\r\n!=partial('includes/head/google_adsense', {}, {cache: true})\r\n\r\n//- analytics\r\n!=partial('includes/head/analytics', {}, {cache: true})\r\n\r\n//- font\r\nif theme.blog_title_font && theme.blog_title_font.font_link\r\n  link(rel='stylesheet' href=url_for(theme.blog_title_font.font_link) media=\"print\" onload=\"this.media='all'\")\r\n\r\n//- global config\r\n!=partial('includes/head/config', {}, {cache: true})\r\n\r\ninclude ./head/config_site.pug\r\n\r\n!=fragment_cache('injectHead', function(){return injectHtml(theme.inject.head)})\r\n"
  },
  {
    "path": "layout/includes/header/index.pug",
    "content": "-\r\n  const returnTopImg = img => img !== false ? img || theme.default_top_img : false\r\n  const isFixedClass = theme.nav.fixed ? ' fixed' : ''\r\n  var top_img = false\r\n  let headerClassName = 'not-top-img'\r\n  var bg_img = ''\r\n\r\nif !theme.disable_top_img && page.top_img !== false\r\n  case globalPageType\r\n    when 'post'\r\n      - top_img = page.top_img || page.cover || theme.default_top_img\r\n    when 'page'\r\n      - top_img = page.top_img || theme.default_top_img\r\n    when 'tag'\r\n      - top_img = theme.tag_per_img && theme.tag_per_img[page.tag] || returnTopImg(theme.tag_img)\r\n    when 'category'\r\n      - top_img = theme.category_per_img && theme.category_per_img[page.category] || returnTopImg(theme.category_img)\r\n    when 'home'\r\n      - top_img = returnTopImg(theme.index_img)\r\n    when 'archive'\r\n      - top_img = returnTopImg(theme.archive_img)\r\n    default\r\n      - top_img = page.top_img || theme.default_top_img\r\n\r\n  if top_img !== false\r\n    - bg_img = getBgPath(top_img)\r\n    - headerClassName = globalPageType === 'home' ? 'full_page' : globalPageType === 'post' ? 'post-bg' : 'not-home-page'\r\n\r\nheader#page-header(class=`${headerClassName + isFixedClass}` style=bg_img)\r\n  include ./nav.pug\r\n  if top_img !== false\r\n    if globalPageType === 'post'\r\n      include ./post-info.pug\r\n    else if globalPageType === 'home'\r\n      #site-info\r\n        h1#site-title=config.title\r\n        if theme.subtitle.enable\r\n          - var loadSubJs = true\r\n          #site-subtitle\r\n            span#subtitle\r\n        if theme.social\r\n          #site_social_icons\r\n            !=partial('includes/header/social', {}, {cache: true})\r\n      #scroll-down\r\n        i.fas.fa-angle-down.scroll-down-effects\r\n    else\r\n      #page-site-info\r\n        h1#site-title=page.title || page.tag || page.category\r\n  else\r\n    //- improve seo\r\n    if globalPageType !== 'post'\r\n      h1.title-seo=page.title || page.tag || page.category || config.title"
  },
  {
    "path": "layout/includes/header/menu_item.pug",
    "content": "if theme.menu\r\n  .menus_items\r\n    each value, label in theme.menu\r\n      if typeof value !== 'object'\r\n        .menus_item\r\n          - const [link, icon] = value.split('||').map(part => trim(part))\r\n          a.site-page(href=url_for(link))\r\n            if icon\r\n              i.fa-fw(class=icon)\r\n            span= ' ' + label\r\n      else\r\n        .menus_item\r\n          - const [groupLabel, groupIcon, groupClass] = label.split('||').map(part => trim(part))\r\n          - const hideClass = groupClass === 'hide' ? 'hide' : ''\r\n          span.site-page.group(class=hideClass)\r\n            if groupIcon\r\n              i.fa-fw(class=groupIcon)\r\n            span= ' ' + groupLabel\r\n            i.fas.fa-chevron-down\r\n          ul.menus_item_child\r\n            each val, lab in value\r\n              - const [childLink, childIcon] = val.split('||').map(part => trim(part))\r\n              li\r\n                a.site-page.child(href=url_for(childLink))\r\n                  if childIcon\r\n                    i.fa-fw(class=childIcon)\r\n                  span= ' ' + lab"
  },
  {
    "path": "layout/includes/header/nav.pug",
    "content": "nav#nav\r\n  span#blog-info\r\n    a.nav-site-title(href=url_for('/'))\r\n      if theme.nav.logo\r\n        img.site-icon(src=url_for(theme.nav.logo) alt='Logo')\r\n      if theme.nav.display_title\r\n        span.site-name=config.title\r\n    if globalPageType === 'post' && theme.nav.display_post_title\r\n      a.nav-page-title(href=url_for('/'))\r\n        span.site-name=(page.title || config.title)\r\n        span.site-name\r\n          i.fa-solid.fa-circle-arrow-left\r\n          span= '  ' + _p('post.back_to_home')\r\n\r\n  #menus\r\n    if theme.search.use\r\n      #search-button\r\n        span.site-page.social-icon.search\r\n          i.fas.fa-search.fa-fw\r\n          span= ' ' + _p('search.title')\r\n    if theme.menu\r\n      != partial('includes/header/menu_item', {}, {cache: true})\r\n\r\n      #toggle-menu\r\n        span.site-page\r\n          i.fas.fa-bars.fa-fw"
  },
  {
    "path": "layout/includes/header/post-info.pug",
    "content": "- let comments = theme.comments\r\n#post-info\r\n  h1.post-title= page.title || _p('no_title')\r\n    if theme.post_edit.enable\r\n      a.post-edit-link(href=theme.post_edit.url + page.source title=_p('post.edit') target=\"_blank\")\r\n        i.fas.fa-pencil-alt\r\n\r\n  #post-meta\r\n    .meta-firstline\r\n      if theme.post_meta.post.date_type\r\n        span.post-meta-date\r\n          if theme.post_meta.post.date_type === 'both'\r\n            i.far.fa-calendar-alt.fa-fw.post-meta-icon\r\n            span.post-meta-label= _p('post.created')\r\n            time.post-meta-date-created(datetime=date_xml(page.date) title=_p('post.created') + ' ' + full_date(page.date))= date(page.date, config.date_format)\r\n            span.post-meta-separator |\r\n            i.fas.fa-history.fa-fw.post-meta-icon\r\n            span.post-meta-label= _p('post.updated')\r\n            time.post-meta-date-updated(datetime=date_xml(page.updated) title=_p('post.updated') + ' ' + full_date(page.updated))= date(page.updated, config.date_format)\r\n          else\r\n            - let data_type_update = theme.post_meta.post.date_type === 'updated'\r\n            - let date_type = data_type_update ? 'updated' : 'date'\r\n            - let date_icon = data_type_update ? 'fas fa-history' : 'far fa-calendar-alt'\r\n            - let date_title = data_type_update ? _p('post.updated') : _p('post.created')\r\n            i.fa-fw.post-meta-icon(class=date_icon)\r\n            span.post-meta-label= date_title\r\n            time(datetime=date_xml(page[date_type]) title=date_title + ' ' + full_date(page[date_type]))= date(page[date_type], config.date_format)\r\n      if theme.post_meta.post.categories && page.categories.data.length > 0\r\n        span.post-meta-categories\r\n          if theme.post_meta.post.date_type\r\n            span.post-meta-separator |\r\n          each item, index in page.categories.data\r\n            i.fas.fa-inbox.fa-fw.post-meta-icon\r\n            a(href=url_for(item.path)).post-meta-categories #[=item.name]\r\n            if index < page.categories.data.length - 1\r\n              i.fas.fa-angle-right.post-meta-separator\r\n\r\n    .meta-secondline\r\n      - let postWordcount = theme.wordcount.enable && (theme.wordcount.post_wordcount || theme.wordcount.min2read)\r\n      if postWordcount\r\n        span.post-meta-separator |\r\n        span.post-meta-wordcount\r\n          if theme.wordcount.post_wordcount\r\n            i.far.fa-file-word.fa-fw.post-meta-icon\r\n            span.post-meta-label= _p('post.wordcount') + ':'\r\n            span.word-count= wordcount(page.content)\r\n            if theme.wordcount.min2read\r\n              span.post-meta-separator |\r\n          if theme.wordcount.min2read\r\n            i.far.fa-clock.fa-fw.post-meta-icon\r\n            span.post-meta-label= _p('post.min2read') + ':'\r\n            span= min2read(page.content, {cn: 350, en: 160}) + _p('post.min2read_unit')\r\n\r\n      //- for pv and count\r\n      mixin pvBlock(parent_id, parent_class, parent_title)\r\n        span.post-meta-separator |\r\n        span(class=parent_class id=parent_id data-flag-title=parent_title)\r\n          i.far.fa-eye.fa-fw.post-meta-icon\r\n          span.post-meta-label= _p('post.page_pv') + ':'\r\n          if block\r\n            block\r\n\r\n      mixin otherPV()\r\n        if theme.umami_analytics.enable && theme.umami_analytics.UV_PV.page_pv\r\n          +pvBlock('', '', '')\r\n            span#umamiPV(data-path=url_for(page.path))\r\n              i.fa-solid.fa-spinner.fa-spin\r\n        else if theme.busuanzi.page_pv\r\n          +pvBlock('', 'post-meta-pv-cv', '')\r\n            span#busuanzi_value_page_pv\r\n              i.fa-solid.fa-spinner.fa-spin\r\n\r\n      - const commentUse = comments.use && comments.use[0]\r\n      if page.comments !== false && commentUse && !comments.lazyload\r\n        if commentUse === 'Valine' && theme.valine.visitor\r\n          +pvBlock(url_for(page.path), 'leancloud_visitors', page.title)\r\n            span.leancloud-visitors-count\r\n              i.fa-solid.fa-spinner.fa-spin\r\n        else if commentUse === 'Waline' && theme.waline.pageview\r\n          +pvBlock('', '', '')\r\n            span.waline-pageview-count(data-path=url_for(page.path))\r\n              i.fa-solid.fa-spinner.fa-spin\r\n        else if commentUse === 'Twikoo' && theme.twikoo.visitor\r\n          +pvBlock('', '', '')\r\n            span#twikoo_visitors\r\n              i.fa-solid.fa-spinner.fa-spin\r\n        else if commentUse === 'Artalk' && theme.artalk.visitor\r\n          +pvBlock('', '', '')\r\n            span#ArtalkPV\r\n              i.fa-solid.fa-spinner.fa-spin\r\n        else\r\n          +otherPV()\r\n      else\r\n        +otherPV()\r\n\r\n      if comments.count && !comments.lazyload && page.comments !== false && comments.use\r\n        - var whichCount = comments.use[0]\r\n\r\n        mixin countBlock\r\n          span.post-meta-separator |\r\n          span.post-meta-commentcount\r\n            i.far.fa-comments.fa-fw.post-meta-icon\r\n            span.post-meta-label= _p('post.comments') + ':'\r\n            if block\r\n              block\r\n\r\n        case whichCount\r\n          when 'Disqus'\r\n            +countBlock\r\n              a.disqus-comment-count(href=full_url_for(page.path) + '#post-comment')\r\n                i.fa-solid.fa-spinner.fa-spin\r\n          when 'Disqusjs'\r\n            +countBlock\r\n              a.disqusjs-comment-count(href=full_url_for(page.path) + '#post-comment')\r\n                i.fa-solid.fa-spinner.fa-spin\r\n          when 'Valine'\r\n            +countBlock\r\n              a(href=url_for(page.path) + '#post-comment' itemprop=\"discussionUrl\")\r\n                span.valine-comment-count(data-xid=url_for(page.path) itemprop=\"commentCount\")\r\n                  i.fa-solid.fa-spinner.fa-spin\r\n          when 'Waline'\r\n            +countBlock\r\n              a(href=url_for(page.path) + '#post-comment')\r\n                span.waline-comment-count(data-path=url_for(page.path))\r\n                  i.fa-solid.fa-spinner.fa-spin\r\n          when 'Gitalk'\r\n            +countBlock\r\n              a(href=url_for(page.path) + '#post-comment')\r\n                span.gitalk-comment-count\r\n                  i.fa-solid.fa-spinner.fa-spin\r\n          when 'Twikoo'\r\n            +countBlock\r\n              a(href=url_for(page.path) + '#post-comment')\r\n                span#twikoo-count\r\n                  i.fa-solid.fa-spinner.fa-spin\r\n          when 'Facebook Comments'\r\n            +countBlock\r\n              a(href=url_for(page.path) + '#post-comment')\r\n                span.fb-comments-count(data-href=urlNoIndex())\r\n          when 'Remark42'\r\n            +countBlock\r\n              a(href=url_for(page.path) + '#post-comment')\r\n                span.remark42__counter(data-url=urlNoIndex())\r\n                  i.fa-solid.fa-spinner.fa-spin\r\n          when 'Artalk'\r\n            +countBlock\r\n              a(href=url_for(page.path) + '#post-comment')\r\n                span#ArtalkCount\r\n                  i.fa-solid.fa-spinner.fa-spin"
  },
  {
    "path": "layout/includes/header/social.pug",
    "content": "each url, icon in theme.social\r\n  -\r\n    const [link, title, color] = url.split('||').map(i => trim(i))\r\n    const href = url_for(link)\r\n    const iconStyle = color ? `color: ${color.replace(/[\\'\\\"]/g, '')};` : ''\r\n    const iconTitle = title || ''\r\n  a.social-icon(href=href target=\"_blank\" title=iconTitle)\r\n    i(class=icon style=iconStyle)"
  },
  {
    "path": "layout/includes/layout.pug",
    "content": "- var globalPageType = getPageType(page, is_home)\r\n- var htmlClassHideAside = theme.aside.enable && theme.aside.hide ? 'hide-aside' : ''\r\n- page.aside = globalPageType === 'archive' ? theme.aside.display.archive: globalPageType === 'category' ? theme.aside.display.category : globalPageType === 'tag' ? theme.aside.display.tag : page.aside\r\n- var hideAside = !theme.aside.enable || page.aside === false ? 'hide-aside' : ''\r\n- var pageType = globalPageType === 'post' ? 'post' : 'page'\r\n- pageType = page.type ? pageType + ' type-' + page.type : pageType\r\n\r\ndoctype html\r\nhtml(lang=config.language data-theme=theme.display_mode class=htmlClassHideAside)\r\n  head\r\n    include ./head.pug\r\n  body\r\n    !=partial('includes/loading/index', {}, {cache: true})\r\n\r\n    if theme.background\r\n      if !Array.isArray(theme.background)\r\n        #web_bg.bg-animation(style=getBgPath(theme.background))\r\n      else\r\n        #web_bg.bg-animation\r\n        - const bgStyleArr = theme.background.map(getBgPath)\r\n        script.\r\n          (() => {\r\n            const arr = !{JSON.stringify(bgStyleArr)}\r\n            const webBgDiv = document.getElementById('web_bg')\r\n\r\n            const setRandomBg = () => {\r\n              webBgDiv.style = arr[Math.floor(Math.random() * arr.length)]\r\n              requestAnimationFrame(() => webBgDiv.classList.add('bg-animation'))\r\n            }\r\n\r\n            document.addEventListener('pjax:send', () => {\r\n              webBgDiv.style = ''\r\n              webBgDiv.classList.remove('bg-animation')\r\n            })\r\n\r\n            document.addEventListener('pjax:complete', setRandomBg)\r\n            document.addEventListener('DOMContentLoaded', setRandomBg)\r\n          })()\r\n\r\n    !=partial('includes/sidebar', {}, {cache: true})\r\n\r\n    #body-wrap(class=pageType)\r\n      include ./header/index.pug\r\n\r\n      main#content-inner.layout(class=hideAside)\r\n        if body\r\n          div!= body\r\n        else\r\n          block content\r\n          if theme.aside.enable && page.aside !== false\r\n            include widget/index.pug\r\n\r\n      - const footerBg = theme.footer_img\r\n      - const footer_bg = footerBg ? footerBg === true ? bg_img : getBgPath(footerBg) : ''\r\n      footer#footer(style=footer_bg)\r\n        !=partial('includes/footer', {}, {cache: true})\r\n\r\n    include ./rightside.pug\r\n    include ./additional-js.pug"
  },
  {
    "path": "layout/includes/loading/fullpage-loading.pug",
    "content": "#loading-box\r\n  .loading-left-bg\r\n  .loading-right-bg\r\n  .spinner-box\r\n    .configure-border-1\r\n      .configure-core\r\n    .configure-border-2\r\n      .configure-core\r\n    .loading-word= _p('loading')\r\n\r\nscript.\r\n  (()=>{\r\n    const $loadingBox = document.getElementById('loading-box')\r\n    const $body = document.body\r\n    const preloader = {\r\n      endLoading: () => {\r\n        if ($loadingBox.classList.contains('loaded')) return\r\n        $body.style.overflow = ''\r\n        $loadingBox.classList.add('loaded')\r\n      },\r\n      initLoading: () => {\r\n        $body.style.overflow = 'hidden'\r\n        $loadingBox.classList.remove('loaded')\r\n      }\r\n    }\r\n\r\n    preloader.initLoading()\r\n\r\n    if (document.readyState === 'complete') {\r\n      preloader.endLoading()\r\n    } else {\r\n      window.addEventListener('load', preloader.endLoading)\r\n      document.addEventListener('DOMContentLoaded', preloader.endLoading)\r\n      // Add timeout protection: force end after 7 seconds\r\n      setTimeout(preloader.endLoading, 7000)\r\n    }\r\n\r\n    if (!{theme.pjax && theme.pjax.enable}) {\r\n      btf.addGlobalFn('pjaxSend', preloader.initLoading, 'preloader_init')\r\n      btf.addGlobalFn('pjaxComplete', preloader.endLoading, 'preloader_end')\r\n    }\r\n  })()"
  },
  {
    "path": "layout/includes/loading/index.pug",
    "content": "if theme.preloader.enable\r\n  if theme.preloader.source === 1\r\n    include ./fullpage-loading.pug\r\n  else\r\n    include ./pace.pug"
  },
  {
    "path": "layout/includes/loading/pace.pug",
    "content": "script.\r\n  window.paceOptions = {\r\n    restartOnPushState: false\r\n  }\r\n\r\n  btf.addGlobalFn('pjaxSend', () => {\r\n    Pace.restart()\r\n  }, 'pace_restart')\r\n\r\n\r\nlink(rel=\"stylesheet\", href=url_for(theme.preloader.pace_css_url || theme.asset.pace_default_css))\r\nscript(src=url_for(theme.asset.pace_js))"
  },
  {
    "path": "layout/includes/mixins/article-sort.pug",
    "content": "mixin articleSort(posts)\r\n  .article-sort\r\n    - let year\r\n    - posts.forEach(article => {\r\n      - const tempYear = date(article.date, 'YYYY')\r\n      - const noCoverClass = article.cover === false || !theme.cover.archives_enable ? 'no-article-cover' : ''\r\n      - const title = article.title || _p('no_title')\r\n      if tempYear !== year\r\n        - year = tempYear\r\n        .article-sort-item.year= year\r\n      .article-sort-item(class=noCoverClass)\r\n        if article.cover && theme.cover.archives_enable\r\n          a.article-sort-item-img(href=url_for(article.path) title=title)\r\n            if article.cover_type === 'img'\r\n              img(src=url_for(article.cover) alt=title onerror=`this.onerror=null;this.src='${url_for(theme.error_img.post_page)}'`)\r\n            else\r\n              div(style=`background: ${article.cover}`)\r\n        .article-sort-item-info\r\n          .article-sort-item-time\r\n            i.far.fa-calendar-alt\r\n            time.post-meta-date-created(datetime=date_xml(article.date) title=_p('post.created') + ' ' + full_date(article.date))= date(article.date, config.date_format)\r\n          a.article-sort-item-title(href=url_for(article.path) title=title)= title\r\n    - })"
  },
  {
    "path": "layout/includes/mixins/indexPostUI.pug",
    "content": "mixin indexPostUI()\r\n  - const indexLayout = theme.index_layout\r\n  - const masonryLayoutClass = [6, 7].includes(indexLayout) ? 'masonry' : ''\r\n  #recent-posts.recent-posts.nc(class=masonryLayoutClass)\r\n    .recent-post-items\r\n      each article, index in page.posts.data\r\n        .recent-post-item\r\n          - const link = article.link || article.path\r\n          - const title = article.title || _p('no_title')\r\n          - const leftOrRight = indexLayout === 3 ? (index % 2 === 0 ? 'left' : 'right') : (indexLayout === 2 ? 'right' : '')\r\n          - const postCover = article.cover\r\n          - const noCover = article.cover === false || !theme.cover.index_enable ? 'no-cover' : ''\r\n\r\n          if postCover && theme.cover.index_enable\r\n            .post_cover(class=leftOrRight)\r\n              a(href=url_for(link) title=title)\r\n                if article.cover_type === 'img'\r\n                  img.post-bg(src=url_for(postCover) onerror=`this.onerror=null;this.src='${url_for(theme.error_img.post_page)}'` alt=title)\r\n                else\r\n                  div.post-bg(style=`background: ${postCover}`)\r\n          .recent-post-info(class=noCover)\r\n            a.article-title(href=url_for(link) title=title)\r\n              if globalPageType === 'home' && (article.top || article.sticky > 0)\r\n                i.fas.fa-thumbtack.sticky\r\n              = title\r\n            .article-meta-wrap\r\n              if theme.post_meta.page.date_type\r\n                span.post-meta-date\r\n                  if theme.post_meta.page.date_type === 'both'\r\n                    i.far.fa-calendar-alt\r\n                    span.article-meta-label=_p('post.created')\r\n                    time.post-meta-date-created(datetime=date_xml(article.date) title=_p('post.created') + ' ' + full_date(article.date))= date(article.date, config.date_format)\r\n                    span.article-meta-separator |\r\n                    i.fas.fa-history\r\n                    span.article-meta-label=_p('post.updated')\r\n                    time.post-meta-date-updated(datetime=date_xml(article.updated) title=_p('post.updated') + ' ' + full_date(article.updated))= date(article.updated, config.date_format)\r\n                  else\r\n                    - const isUpdatedType = theme.post_meta.page.date_type === 'updated'\r\n                    - const dateType = isUpdatedType ? 'updated' : 'date'\r\n                    - const dateIcon = isUpdatedType ? 'fas fa-history' : 'far fa-calendar-alt'\r\n                    - const dateTitle = isUpdatedType ? _p('post.updated') : _p('post.created')\r\n                    i(class=dateIcon)\r\n                    span.article-meta-label= dateTitle\r\n                    time(datetime=date_xml(article[dateType]) title=dateTitle + ' ' + full_date(article[dateType]))= date(article[dateType], config.date_format)\r\n              if theme.post_meta.page.categories && article.categories.data.length > 0\r\n                span.article-meta\r\n                  span.article-meta-separator |\r\n                  each item, index in article.categories.data\r\n                    i.fas.fa-inbox\r\n                    a(href=url_for(item.path)).article-meta__categories #[=item.name]\r\n                    if index < article.categories.data.length - 1\r\n                      i.fas.fa-angle-right.article-meta-link\r\n              if theme.post_meta.page.tags && article.tags.length > 0\r\n                span.article-meta.tags\r\n                  span.article-meta-separator |\r\n                  each item, index in article.tags.data\r\n                    i.fas.fa-tag\r\n                    a(href=url_for(item.path)).article-meta__tags #[=item.name]\r\n                    if index < article.tags.data.length - 1\r\n                      span.article-meta-link #[='•']\r\n\r\n              mixin countBlockInIndex\r\n                - needLoadCountJs = true\r\n                span.article-meta\r\n                  span.article-meta-separator |\r\n                  i.fas.fa-comments\r\n                  if block\r\n                    block\r\n                  span.article-meta-label= ' ' + _p('card_post_count')\r\n\r\n              if theme.comments.card_post_count && theme.comments.use\r\n                - const commentSystem = theme.comments.use[0]\r\n                - const commentLink = url_for(link) + '#post-comment'\r\n\r\n                case commentSystem\r\n                  when 'Disqus'\r\n                  when 'Disqusjs'\r\n                    +countBlockInIndex\r\n                      a.disqus-count(href=full_url_for(link) + '#post-comment')\r\n                        i.fa-solid.fa-spinner.fa-spin\r\n                  when 'Valine'\r\n                    +countBlockInIndex\r\n                      a(href=commentLink)\r\n                        span.valine-comment-count(data-xid=url_for(link))\r\n                          i.fa-solid.fa-spinner.fa-spin\r\n                  when 'Waline'\r\n                    +countBlockInIndex\r\n                      a(href=commentLink)\r\n                        span.waline-comment-count(data-path=url_for(link))\r\n                          i.fa-solid.fa-spinner.fa-spin\r\n                  when 'Twikoo'\r\n                    +countBlockInIndex\r\n                      a.twikoo-count(href=commentLink)\r\n                        i.fa-solid.fa-spinner.fa-spin\r\n                  when 'Facebook Comments'\r\n                    +countBlockInIndex\r\n                      a(href=commentLink)\r\n                        span.fb-comments-count(data-href=urlNoIndex(article.permalink))\r\n                  when 'Remark42'\r\n                    +countBlockInIndex\r\n                      a(href=commentLink)\r\n                        span.remark42__counter(data-url=urlNoIndex(article.permalink))\r\n                          i.fa-solid.fa-spinner.fa-spin\r\n                  when 'Artalk'\r\n                    +countBlockInIndex\r\n                      a(href=commentLink)\r\n                        span.artalk-count(data-page-key=url_for(link))\r\n                          i.fa-solid.fa-spinner.fa-spin\r\n\r\n            //- Display the article introduction on homepage\r\n            - const content = postDesc(article)\r\n            if content\r\n              .content!=content\r\n\r\n        if theme.ad && theme.ad.index\r\n          if (index + 1) % 3 === 0\r\n            .recent-post-item.ads-wrap!= theme.ad.index\r\n\r\n    include ../pagination.pug"
  },
  {
    "path": "layout/includes/page/404.pug",
    "content": "- var top_img_404 = theme.error_404.background || theme.default_top_img\r\n\r\n.error-content\r\n  .error-img\r\n    img(src=url_for(top_img_404) alt='Page not found')\r\n  .error-info\r\n    h1.error_title= '404'\r\n    .error_subtitle= theme.error_404.subtitle || _p('error404')\r\n"
  },
  {
    "path": "layout/includes/page/categories.pug",
    "content": ".category-lists!= list_categories()"
  },
  {
    "path": "layout/includes/page/default-page.pug",
    "content": "#article-container.container\r\n  != page.content"
  },
  {
    "path": "layout/includes/page/flink.pug",
    "content": "#article-container.container\r\n  .flink\r\n    - let { content, random, flink_url } = page\r\n    - let pageContent = content\r\n\r\n    if flink_url || random\r\n      - const linkData = flink_url ? false : site.data.link || false\r\n      script.\r\n        (()=>{\r\n          const replaceSymbol = (str) => {\r\n            return str.replace(/[\\p{P}\\p{S}]/gu, \"-\")\r\n          }\r\n\r\n          let result = \"\"\r\n          const add = (str) => {\r\n            for(let i = 0; i < str.length; i++){\r\n              const replaceClassName = replaceSymbol(str[i].class_name)\r\n              const className = str[i].class_name ? `<h2 id=\"${replaceClassName}\"><a href=\"#${replaceClassName}\" class=\"headerlink\" title=\"${str[i].class_name}\"></a>${str[i].class_name}</h2>` : \"\"\r\n              const classDesc = str[i].class_desc ? `<div class=\"flink-desc\">${str[i].class_desc}</div>` : \"\"\r\n\r\n              let listResult = \"\"\r\n              const lists = str[i].link_list\r\n              if (!{random === true}) {\r\n                lists.sort(() => Math.random() - 0.5)\r\n              }\r\n              for(let j = 0; j < lists.length; j++){\r\n                listResult += `\r\n                  <div class=\"flink-list-item\">\r\n                    <a href=\"${lists[j].link}\" title=\"${lists[j].name}\" target=\"_blank\">\r\n                      <div class=\"flink-item-icon\">\r\n                        <img class=\"no-lightbox\" src=\"${lists[j].avatar}\" onerror='this.onerror=null;this.src=\"!{url_for(theme.error_img.flink)}\"' alt=\"${lists[j].name}\" />\r\n                      </div>\r\n                      <div class=\"flink-item-name\">${lists[j].name}</div>\r\n                      <div class=\"flink-item-desc\" title=\"${lists[j].descr}\">${lists[j].descr}</div>\r\n                    </a>\r\n                  </div>`\r\n              }\r\n\r\n              result += `${className}${classDesc} <div class=\"flink-list\">${listResult}</div>`\r\n            }\r\n\r\n            document.querySelector(\".flink\").insertAdjacentHTML(\"afterbegin\", result)\r\n            window.lazyLoadInstance && window.lazyLoadInstance.update()\r\n          }\r\n\r\n          const linkData = !{JSON.stringify(linkData)}\r\n          if (!{Boolean(flink_url)}) {\r\n            fetch(\"!{url_for(flink_url)}\")\r\n              .then(response => response.json())\r\n              .then(add)\r\n          } else if (linkData) {\r\n            add(linkData)\r\n          }\r\n        })()\r\n\r\n    else\r\n      if site.data.link\r\n        - let result = \"\"\r\n        each i in site.data.link\r\n          - let className = i.class_name ? markdown(`## ${i.class_name}`) : \"\"\r\n          - let classDesc = i.class_desc ? `<div class=\"flink-desc\">${i.class_desc}</div>` : \"\"\r\n\r\n          - let listResult = \"\"\r\n\r\n          each j in i.link_list\r\n            -\r\n              listResult += `\r\n                <div class=\"flink-list-item\">\r\n                  <a href=\"${j.link}\" title=\"${j.name}\" target=\"_blank\">\r\n                    <div class=\"flink-item-icon\">\r\n                      <img class=\"no-lightbox\" src=\"${j.avatar}\" onerror='this.onerror=null;this.src=\"${url_for(theme.error_img.flink)}\"' alt=\"${j.name}\" />\r\n                    </div>\r\n                    <div class=\"flink-item-name\">${j.name}</div>\r\n                    <div class=\"flink-item-desc\" title=\"${j.descr}\">${j.descr}</div>\r\n                  </a>\r\n                </div>`\r\n            -\r\n\r\n          - result += `${className}${classDesc} <div class=\"flink-list\">${listResult}</div>`\r\n\r\n        - pageContent = result + pageContent\r\n    != pageContent\r\n"
  },
  {
    "path": "layout/includes/page/shuoshuo.pug",
    "content": "//- - author:\r\n//-   avatar:\r\n//-   date:\r\n//-   content:\r\n//-   tags:\r\n//-     - tag1\r\n//-     - tag2\r\n\r\n- page.toc = false\r\n\r\n#article-container\r\n  if page.shuoshuo_url || (site.data.shuoshuo && site.data.shuoshuo.length)\r\n    if page.comments !== false && theme.comments.use\r\n      - commentsJsLoad = true\r\n\r\n      script.\r\n        (() => {\r\n          const commentDiv = `!{partial('includes/third-party/comments/index', {}, {cache: true})}`\r\n\r\n          const runDestroy = (shuoshuoComment) => {\r\n            if (!shuoshuoComment) return\r\n\r\n            for (const [key, fn] of Object.entries(shuoshuoComment)) {\r\n              if (key.startsWith('destroy')) fn()\r\n            }\r\n          }\r\n\r\n          window.addCommentToShuoshuo = e => {\r\n            const btn = e.target.closest('.shuoshuo-comment-btn')\r\n            if (!btn) return\r\n\r\n            const ele = btn.closest('.container').nextElementSibling\r\n            const { shuoshuoComment } = window\r\n            const isInclude = ele.classList.contains('no-comment')\r\n            runDestroy(shuoshuoComment)\r\n            if (isInclude) {\r\n              ele.classList.remove('no-comment')\r\n              ele.innerHTML = commentDiv\r\n              const key = `${location.pathname.replace(/\\/$/, '')}?key=${ele.getAttribute('data-key')}`\r\n              btf.switchComments(ele, key)\r\n              shuoshuoComment.loadComment && shuoshuoComment.loadComment(ele, key)\r\n            }\r\n          }\r\n        })()\r\n\r\n\r\n    - const localDate = page.shuoshuo_url ? [] : shuoshuoFN(site.data.shuoshuo, page)\r\n\r\n    if !page.shuoshuo_url\r\n      script(type='application/json' id='shuoshuo-data')!= safeJSON(localDate)\r\n\r\n    - const { enable, native, placeholder, field } = theme.lazyload\r\n    script.\r\n      (() => {\r\n        const limitConfig = !{ JSON.stringify(page.limit || {}) }\r\n\r\n        const sortDataByDate = data => data.sort((a, b) => new Date(b.date) - new Date(a.date))\r\n\r\n        const filterDataByLimit = (data, limit) => {\r\n          if (!limit || !limit.type) return data\r\n          if (limit.type === 'num') return data.slice(0, limit.value)\r\n          if (limit.type === 'date') {\r\n            const limitDate = new Date(limit.value)\r\n            return data.filter(item => new Date(item.date) >= limitDate)\r\n          }\r\n          return data\r\n        };\r\n\r\n        const formatToTimeZone = (date) => {\r\n          const fullDate = date.length === 10 ? `${date} 00:00:00` : date\r\n          const visitorTimeZone = '#{config.timezone}' || Intl.DateTimeFormat().resolvedOptions().timeZone\r\n          const options = {\r\n            timeZone: visitorTimeZone,\r\n            year: 'numeric',\r\n            month: '2-digit',\r\n            day: '2-digit',\r\n            hour: '2-digit',\r\n            minute: '2-digit',\r\n            second: '2-digit',\r\n            hour12: false\r\n          }\r\n          const [day, month, year, hour, minute, second] = new Intl.DateTimeFormat('en-GB', options)\r\n            .format(new Date(fullDate))\r\n            .match(/\\d+/g)\r\n          return `${year}-${month}-${day} ${hour}:${minute}:${second}`\r\n        }\r\n\r\n        const addLazyload = str => {\r\n          const config = {\r\n            enable: !{Boolean(enable)},\r\n            native: !{Boolean(native)},\r\n            field: '!{field}',\r\n            placeholder: '!{url_for(placeholder)}',\r\n          }\r\n\r\n          if (!config.enable || config.field !== 'site') return str\r\n          const parser = new DOMParser()\r\n          const doc = parser.parseFromString(str, 'text/html')\r\n          const images = doc.querySelectorAll('img')\r\n\r\n          images.forEach(img => {\r\n            if (config.native) {\r\n              img.setAttribute('loading', 'lazy')\r\n            } else {\r\n              const src = img.getAttribute('src')\r\n              img.setAttribute('data-lazy-src', src)\r\n\r\n              if (config.placeholder) {\r\n                img.setAttribute('src', config.placeholder)\r\n              } else {\r\n                img.removeAttribute('src')\r\n              }\r\n            }\r\n          })\r\n          return doc.body.innerHTML\r\n        }\r\n\r\n        let currentPage = 1\r\n        const itemsPerPage = 8\r\n        let totalPages = 0\r\n        let data = []\r\n        let inputEventsAttached = false // Flag to mark if input event listeners have been added\r\n\r\n        const renderData = (dataSlice) => {\r\n          const content = dataSlice.map(item => {\r\n            const formattedDate = formatToTimeZone(item.date)\r\n            const tags = item.tags && item.tags.map(tag => `<span class=\"shuoshuo-tag\">${tag}</span>`).join('') || ''\r\n            const commentButton = item.key && !{commentsJsLoad}\r\n              ? `<div class=\"shuoshuo-comment-btn\" onclick=\"addCommentToShuoshuo(event)\">\r\n                  <i class=\"fa-solid fa-comments\"></i>\r\n                </div>`\r\n              : ''\r\n            const commentContainer = item.key\r\n              ? `<div class=\"shuoshuo-comment no-comment\" data-key=\"${item.key}\"></div>`\r\n              : ''\r\n\r\n            return `\r\n              <div class=\"shuoshuo-item\">\r\n                <div class=\"container\">\r\n                  <div class=\"shuoshuo-item-header\">\r\n                    <div class=\"shuoshuo-avatar\">\r\n                      <img class=\"no-lightbox\" src=\"${item.avatar || '!{url_for(theme.avatar.img)}'}\">\r\n                    </div>\r\n                    <div class=\"shuoshuo-info\">\r\n                      <div class=\"shuoshuo-author\">${item.author || '!{config.author}'}</div>\r\n                      <time class=\"shuoshuo-date\" title=\"${formattedDate}\">\r\n                        ${btf.diffDate(formattedDate, true)}\r\n                      </time>\r\n                    </div>\r\n                  </div>\r\n                  <div class=\"shuoshuo-content\">${addLazyload(item.content)}</div>\r\n                  <div class=\"shuoshuo-footer ${tags ? 'flex-between' : 'flex-end'}\">\r\n                    ${tags ? `<div class=\"shuoshuo-tags\">${tags}</div>` : ''}\r\n                    ${commentButton}\r\n                  </div>\r\n                </div>\r\n                ${commentContainer}\r\n              </div>`\r\n          }).join('')\r\n\r\n          const container = document.getElementById('article-container')\r\n          container.innerHTML = content\r\n\r\n          window.lazyLoadInstance && window.lazyLoadInstance.update()\r\n          btf.loadLightbox(document.querySelectorAll('#article-container img:not(.no-lightbox)'))\r\n        }\r\n\r\n        const renderNavigation = () => {\r\n          const container = document.getElementById('article-container')\r\n          const existingNav = container.nextElementSibling\r\n          if (existingNav && existingNav.classList.contains('shuoshuo-navigation')) {\r\n            existingNav.remove()\r\n          }\r\n\r\n          const pageInfoTemplate = '#{__('pagination.page_info')}'\r\n          const pageInfoText = pageInfoTemplate\r\n            .replace(/\\$\\{current}/g, currentPage)\r\n            .replace(/\\$\\{total}/g, totalPages)\r\n\r\n          const navHtml = `\r\n            <div class=\"shuoshuo-navigation\">\r\n              <button onclick=\"window.shuoshuoPrevPage()\" ${currentPage === 1 ? 'disabled' : ''}><i class=\"fa-solid fa-chevron-left\"></i></button>\r\n              <span class=\"shuoshuo-page-info\">${pageInfoText}</span>\r\n              <input type=\"number\" class=\"shuoshuo-page-input\" min=\"1\" max=\"${totalPages}\" placeholder=\"${currentPage}\" onkeydown=\"window.shuoshuoHandleKeyDown(event)\">\r\n              <button onclick=\"window.shuoshuoNextPage()\" ${currentPage === totalPages ? 'disabled' : ''}><i class=\"fa-solid fa-chevron-right\"></i></button>\r\n            </div>\r\n          `\r\n          container.insertAdjacentHTML('afterend', navHtml)\r\n\r\n          // Add input validation event listeners (only once)\r\n          if (!inputEventsAttached) {\r\n            setTimeout(() => {\r\n              const input = document.querySelector('.shuoshuo-page-input')\r\n              if (input) {\r\n                // Clear placeholder when clicking the input box\r\n                input.addEventListener('focus', (event) => {\r\n                  event.target.placeholder = ''\r\n                })\r\n\r\n                // Restore placeholder if no content when losing focus\r\n                input.addEventListener('blur', (event) => {\r\n                  if (!event.target.value.trim()) {\r\n                    event.target.placeholder = currentPage\r\n                  }\r\n                })\r\n\r\n                input.addEventListener('input', (event) => {\r\n                  const value = parseInt(event.target.value) || 0\r\n                  let wasInvalid = false\r\n\r\n                  if (value > totalPages) {\r\n                    event.target.value = totalPages\r\n                    wasInvalid = true\r\n                  } else if (value < 1 && event.target.value !== '') {\r\n                    event.target.value = 1\r\n                    wasInvalid = true\r\n                  }\r\n\r\n                  // If value is corrected, show red and shake effect\r\n                  if (wasInvalid) {\r\n                    event.target.classList.add('invalid')\r\n                    setTimeout(() => {\r\n                      event.target.classList.remove('invalid')\r\n                    }, 500)\r\n                  }\r\n                })\r\n\r\n                inputEventsAttached = true // Mark that event listeners have been added\r\n              }\r\n            }, 0)\r\n          }\r\n        }\r\n\r\n        const renderPage = (page) => {\r\n          const start = (page - 1) * itemsPerPage\r\n          const end = start + itemsPerPage\r\n          const pageData = data.slice(start, end)\r\n          renderData(pageData)\r\n          renderNavigation()\r\n        }\r\n\r\n        window.shuoshuoPrevPage = () => {\r\n          if (currentPage > 1) {\r\n            currentPage--\r\n            renderPage(currentPage)\r\n          }\r\n        }\r\n\r\n        window.shuoshuoNextPage = () => {\r\n          if (currentPage < totalPages) {\r\n            currentPage++\r\n            renderPage(currentPage)\r\n          }\r\n        }\r\n\r\n        window.shuoshuoGoToPage = (page) => {\r\n          if (typeof page === 'number') {\r\n            // Directly jump to the specified page\r\n            if (page >= 1 && page <= totalPages && page !== currentPage) {\r\n              currentPage = page\r\n              renderPage(currentPage)\r\n            }\r\n          } else {\r\n            // Get page from input box\r\n            const input = document.querySelector('.shuoshuo-page-input')\r\n            const inputValue = input.value.trim()\r\n            const inputPage = inputValue === '' ? currentPage : parseInt(inputValue)\r\n            if (inputPage >= 1 && inputPage <= totalPages && inputPage !== currentPage) {\r\n              currentPage = inputPage\r\n              renderPage(currentPage)\r\n            } else if (inputValue === '') {\r\n              // If input box is empty, re-render current page (update placeholder)\r\n              renderPage(currentPage)\r\n            }\r\n          }\r\n        }\r\n\r\n        window.shuoshuoHandleKeyDown = (event) => {\r\n          const input = event.target\r\n          const value = input.value + event.key\r\n\r\n          // Allow delete, arrow keys, backspace, etc.\r\n          if (event.key === 'Enter' || event.key === 'Backspace' || event.key === 'Delete' ||\r\n              event.key === 'ArrowLeft' || event.key === 'ArrowRight' ||\r\n              event.key === 'Tab' || event.ctrlKey || event.metaKey) {\r\n            if (event.key === 'Enter') {\r\n              window.shuoshuoGoToPage()\r\n            }\r\n            return\r\n          }\r\n\r\n          // Only allow numbers\r\n          if (!/^\\d$/.test(event.key)) {\r\n            event.preventDefault()\r\n            return\r\n          }\r\n\r\n          // Check if the value after input exceeds the range\r\n          const newValue = parseInt(value) || 0\r\n          if (newValue > totalPages || (value.length > 1 && newValue === 0)) {\r\n            event.preventDefault()\r\n            // Add red and shake effect\r\n            input.classList.add('invalid')\r\n            setTimeout(() => {\r\n              input.classList.remove('invalid')\r\n            }, 500)\r\n          }\r\n        }\r\n\r\n        const loadShuoshuo = async () => {\r\n          try {\r\n            let originData = []\r\n            if (!{Boolean(page.shuoshuo_url)}) {\r\n              const response = await fetch('!{url_for(page.shuoshuo_url)}')\r\n              originData = await response.json()\r\n            } else {\r\n              const dataElement = document.getElementById('shuoshuo-data')\r\n              originData = dataElement ? JSON.parse(dataElement.textContent) : []\r\n            }\r\n\r\n            data = filterDataByLimit(sortDataByDate(originData), limitConfig)\r\n\r\n            totalPages = Math.ceil(data.length / itemsPerPage)\r\n\r\n            renderPage(currentPage)\r\n          } catch (error) {\r\n            console.error(error)\r\n          }\r\n        };\r\n\r\n        window.pjax ? loadShuoshuo() : window.addEventListener('load', loadShuoshuo)\r\n      })()"
  },
  {
    "path": "layout/includes/page/tags.pug",
    "content": ".tag-cloud-list.text-center\r\n  !=cloudTags({source: site.tags, orderby: page.orderby || 'random', order: page.order || 1, minfontsize: 1.2, maxfontsize: 1.5, limit: 0, unit: 'em', custom_colors: page.custom_colors})\r\n"
  },
  {
    "path": "layout/includes/pagination.pug",
    "content": "if page.total !== 1\r\n  -\r\n    var options = {\r\n      prev_text: '<i class=\"fas fa-chevron-left fa-fw\"></i>',\r\n      next_text: '<i class=\"fas fa-chevron-right fa-fw\"></i>',\r\n      mid_size: 1,\r\n      escape: false\r\n    }\r\n\r\n  if globalPageType === 'post'\r\n    - let paginationOrder = theme.post_pagination === 2 ? { prev: page.prev, next: page.next } : { prev: page.next, next: page.prev }\r\n\r\n    nav#pagination.pagination-post\r\n      each direction, key in paginationOrder\r\n        if direction\r\n          - const getPostDesc = direction.postDesc || postDesc(direction)\r\n          - let className = key === 'prev' ? (paginationOrder.next ? '' : 'full-width') : (paginationOrder.prev ? '' : 'full-width')\r\n          - className = getPostDesc ? className : className + ' no-desc'\r\n\r\n          a.pagination-related(class=className href=url_for(direction.path) title=direction.title)\r\n            if direction.cover_type === 'img'\r\n              img.cover(src=url_for(direction.pagination_cover || direction.cover) onerror=`onerror=null;src='${url_for(theme.error_img.post_page)}'` alt=`cover of ${key === 'prev' ? 'previous' : 'next'} post`)\r\n            else\r\n              .cover(style=`background: ${direction.cover || 'var(--default-bg-color)'}`)\r\n\r\n            .info(class=key === 'prev' ? '' : 'text-right')\r\n              .info-1\r\n                .info-item-1=_p(`pagination.${key}`)\r\n                .info-item-2!=direction.title\r\n              if getPostDesc\r\n                .info-2\r\n                  .info-item-1!=getPostDesc\r\n  else\r\n    nav#pagination\r\n      .pagination\r\n        if globalPageType === 'home'\r\n          - options.format = 'page/%d/#content-inner'\r\n        !=paginator(options)"
  },
  {
    "path": "layout/includes/post/outdate-notice.pug",
    "content": "- const { limit_day, message_prev, message_next, position} = theme.noticeOutdate\r\n- const notice_data = { limitDay: limit_day, messagePrev: message_prev, messageNext: message_next, postUpdate: full_date(page.updated)}\r\nif position === 'top'\r\n  #post-outdate-notice(data=notice_data hidden)\r\n  !=page.content\r\nelse\r\n  !=page.content\r\n  #post-outdate-notice(data=notice_data hidden)"
  },
  {
    "path": "layout/includes/post/post-copyright.pug",
    "content": "if theme.post_copyright.enable && page.copyright !== false\r\n  - const author = page.copyright_author || config.author\r\n  - const authorHref = page.copyright_author_href || theme.post_copyright.author_href || config.url\r\n  - const url = page.copyright_url || page.permalink\r\n  - const info = page.copyright_info || _p('post.copyright.copyright_content', theme.post_copyright.license_url, theme.post_copyright.license, config.url, config.title)\r\n  .post-copyright\r\n    .post-copyright__author\r\n      span.post-copyright-meta\r\n        i.fas.fa-circle-user.fa-fw\r\n        = _p('post.copyright.author') + \": \"\r\n      span.post-copyright-info\r\n        a(href=authorHref)= author\r\n    .post-copyright__type\r\n      span.post-copyright-meta\r\n        i.fas.fa-square-arrow-up-right.fa-fw\r\n        = _p('post.copyright.link') + \": \"\r\n      span.post-copyright-info\r\n        a(href=url_for(url))= theme.post_copyright.decode ? decodeURI(url) : url\r\n    .post-copyright__notice\r\n      span.post-copyright-meta\r\n        i.fas.fa-circle-exclamation.fa-fw\r\n        = _p('post.copyright.copyright_notice') + \": \"\r\n      span.post-copyright-info!= info"
  },
  {
    "path": "layout/includes/post/reward.pug",
    "content": ".post-reward\r\n  .reward-button\r\n    i.fas.fa-qrcode\r\n    = theme.reward.text || _p('donate')\r\n  .reward-main\r\n    ul.reward-all\r\n      each item in theme.reward.QR_code\r\n        - const clickTo = item.link || item.img\r\n        li.reward-item\r\n          a(href=url_for(clickTo) target='_blank')\r\n            img.post-qr-code-img(src=url_for(item.img) alt=item.text)\r\n          .post-qr-code-desc=item.text"
  },
  {
    "path": "layout/includes/rightside.pug",
    "content": "- const { readmode, translate, darkmode, aside, chat } = theme\r\n\r\nmixin rightsideItem(array)\r\n  each item in array\r\n    case item\r\n      when 'readmode'\r\n        if globalPageType === 'post' && readmode\r\n          button#readmode(type=\"button\" title=_p('rightside.readmode_title'))\r\n            i.fas.fa-book-open\r\n      when 'translate'\r\n        if translate.enable\r\n          button#translateLink(type=\"button\" title=_p('rightside.translate_title'))= translate.default\r\n      when 'darkmode'\r\n        if darkmode.enable && darkmode.button\r\n          button#darkmode(type=\"button\" title=_p('rightside.night_mode_title'))\r\n            i.fas.fa-adjust\r\n      when 'hideAside'\r\n        if aside.enable && aside.button && page.aside !== false\r\n          button#hide-aside-btn(type=\"button\" title=_p('rightside.aside'))\r\n            i.fas.fa-arrows-alt-h\r\n      when 'toc'\r\n        if showToc\r\n          button#mobile-toc-button.close(type=\"button\" title=_p(\"rightside.toc\"))\r\n            i.fas.fa-list-ul\r\n      when 'chat'\r\n        if chat.rightside_button && chat.use\r\n          button#chat-btn(type=\"button\" title=_p(\"rightside.chat\") style=\"display:none\")\r\n            i.fas.fa-message\r\n      when 'comment'\r\n        if commentsJsLoad\r\n          a#to_comment(href=\"#post-comment\" title=_p(\"rightside.scroll_to_comment\"))\r\n            i.fas.fa-comments\r\n\r\n- const { enable, hide, show } = theme.rightside_item_order\r\n- const hideArray = enable && hide ? hide.split(',') : ['readmode','translate','darkmode','hideAside']\r\n- const showArray = enable && show ? show.split(',') : ['toc','chat','comment']\r\n- const needCogBtn = (enable && hide) || (!enable && ((globalPageType === 'post' && (readmode || translate.enable || (darkmode.enable && darkmode.button))) || (translate.enable || (darkmode.enable && darkmode.button))))\r\n\r\n#rightside\r\n  #rightside-config-hide\r\n    if hideArray.length\r\n      +rightsideItem(hideArray)\r\n\r\n  #rightside-config-show\r\n    if needCogBtn\r\n      button#rightside-config(type=\"button\" title=_p(\"rightside.setting\"))\r\n        i.fas.fa-cog(class=theme.rightside_config_animation ? 'fa-spin' : '')\r\n\r\n    if showArray.length\r\n      +rightsideItem(showArray)\r\n\r\n    button#go-up(type=\"button\" title=_p(\"rightside.back_to_top\"))\r\n      span.scroll-percent\r\n      i.fas.fa-arrow-up"
  },
  {
    "path": "layout/includes/sidebar.pug",
    "content": "if theme.menu\r\n  #sidebar\r\n    #menu-mask\r\n    #sidebar-menus\r\n      .avatar-img.text-center\r\n        img(src=url_for(theme.avatar.img) onerror=`this.onerror=null;this.src='${url_for(theme.error_img.flink)}'` alt=\"avatar\")\r\n      .site-data.text-center\r\n        a(href=`${url_for(config.archive_dir)}/`)\r\n          .headline= _p('aside.articles')\r\n          .length-num= site.posts.length\r\n        a(href=`${url_for(config.tag_dir)}/`)\r\n          .headline= _p('aside.tags')\r\n          .length-num= site.tags.length\r\n        a(href=`${url_for(config.category_dir)}/`)\r\n          .headline= _p('aside.categories')\r\n          .length-num= site.categories.length\r\n\r\n      != partial('includes/header/menu_item', {}, { cache: true })"
  },
  {
    "path": "layout/includes/third-party/abcjs/abcjs.pug",
    "content": "script.\r\n  (() => {\r\n    const abcjsInit = () => {\r\n      const abcjsFn = () => {\r\n        setTimeout(() => {\r\n          const sheets = document.querySelectorAll(\".abc-music-sheet\")\r\n          for (let i = 0; i < sheets.length; i++) {\r\n            const ele = sheets[i]\r\n            if (ele.children.length > 0) continue\r\n\r\n            // Parse parameters from data-params attribute\r\n            let params = {}\r\n            const dp = ele.getAttribute(\"data-params\")\r\n            if (dp) {\r\n              try {\r\n                params = JSON.parse(dp)\r\n              } catch (e) {\r\n                console.error(\"Failed to parse data-params:\", e)\r\n              }\r\n            }\r\n\r\n            // Merge parsed parameters with the responsive option\r\n            // Ensures params content appears before responsive\r\n            const options = { ...params, responsive: \"resize\" }\r\n\r\n            // Render the music score using ABCJS.renderAbc\r\n            ABCJS.renderAbc(ele, ele.textContent, options)\r\n          }\r\n        }, 100)\r\n      }\r\n\r\n      if (typeof ABCJS === \"object\") {\r\n        abcjsFn()\r\n      } else {\r\n        btf.getScript(\"!{url_for(theme.asset.abcjs_basic_js)}\").then(abcjsFn)\r\n      }\r\n    }\r\n\r\n    if (window.pjax) {\r\n      abcjsInit()\r\n    } else {\r\n      window.addEventListener(\"load\", abcjsInit)\r\n    }\r\n\r\n    btf.addGlobalFn(\"encrypt\", abcjsInit, \"abcjs\")\r\n  })()\r\n"
  },
  {
    "path": "layout/includes/third-party/abcjs/index.pug",
    "content": "if theme.abcjs.enable\r\n  if theme.abcjs.per_page && (['post','page'].includes(globalPageType)) || page.abcjs\r\n    include ./abcjs.pug\r\n"
  },
  {
    "path": "layout/includes/third-party/aplayer.pug",
    "content": "link(rel='stylesheet' href=url_for(theme.asset.aplayer_css) media=\"print\" onload=\"this.media='all'\")\r\nscript(src=url_for(theme.asset.aplayer_js))\r\nscript(src=url_for(theme.asset.meting_js))\r\nif theme.pjax.enable\r\n  script.\r\n    (() => {\r\n      const destroyAplayer = () => {\r\n        if (window.aplayers) {\r\n          for (let i = 0; i < window.aplayers.length; i++) {\r\n            if (!window.aplayers[i].options.fixed) {\r\n              window.aplayers[i].destroy()\r\n            }\r\n          }\r\n        }\r\n      }\r\n\r\n      const runMetingJS = () => {\r\n        typeof loadMeting === 'function' && document.getElementsByClassName('aplayer').length && loadMeting()\r\n      }\r\n\r\n      btf.addGlobalFn('pjaxSend', destroyAplayer, 'destroyAplayer')\r\n      btf.addGlobalFn('pjaxComplete', loadMeting, 'runMetingJS')\r\n    })()\r\n"
  },
  {
    "path": "layout/includes/third-party/card-post-count/artalk.pug",
    "content": "- const { server, site } = theme.artalk\r\n\r\nscript.\r\n  (() => {\r\n    const getArtalkCount = async() => {\r\n      try {\r\n        const eleGroup = document.querySelectorAll('#recent-posts .artalk-count')\r\n        const keyArray = Array.from(eleGroup).map(i => i.getAttribute('data-page-key'))\r\n\r\n        const headerList = {\r\n          method: 'GET',\r\n        }\r\n\r\n        const searchParams = new URLSearchParams({\r\n          'site_name': '!{site}',\r\n          'page_keys': keyArray\r\n        })\r\n\r\n        const res = await fetch(`!{server}/api/v2/stats/page_comment?${searchParams}`, headerList)\r\n        const result = await res.json()\r\n\r\n        keyArray.forEach((key, index) => {\r\n          eleGroup[index].textContent = result.data[key] || 0\r\n        })\r\n      } catch (err) {\r\n        console.error(err)\r\n      }\r\n    }\r\n\r\n    window.pjax ? getArtalkCount() : window.addEventListener('load', getArtalkCount)\r\n  })()"
  },
  {
    "path": "layout/includes/third-party/card-post-count/disqus.pug",
    "content": "- const { shortname, apikey } = theme.disqus\r\nscript.\r\n  (() => {\r\n    const getCount = async () => {\r\n      try {\r\n        const eleGroup = document.querySelectorAll('#recent-posts .disqus-count')\r\n        const cleanedLinks = Array.from(eleGroup).map(i => `thread:link=${i.href.replace(/#post-comment$/, '')}`);\r\n\r\n        const res = await fetch(`https://disqus.com/api/3.0/threads/set.json?forum=!{shortname}&api_key=!{apikey}&${cleanedLinks.join('&')}`,{\r\n          method: 'GET'\r\n        })\r\n        const result = await res.json()\r\n\r\n        eleGroup.forEach(i => {\r\n          const cleanedLink = i.href.replace(/#post-comment$/, '')\r\n          const urlData = result.response.find(data => data.link === cleanedLink) || { posts: 0 }\r\n          i.textContent = urlData.posts\r\n        })\r\n      } catch (err) {\r\n        console.error(err)\r\n      }\r\n    }\r\n\r\n    window.pjax ? getCount() : window.addEventListener('load', getCount)\r\n  })()\r\n"
  },
  {
    "path": "layout/includes/third-party/card-post-count/fb.pug",
    "content": "- const fbSDKVer = 'v20.0'\r\n- const fbSDK = `https://connect.facebook.net/${theme.facebook_comments.lang}/sdk.js#xfbml=1&version=${fbSDKVer}`\r\n\r\nscript.\r\n  (()=>{\r\n    function loadFBComment () {\r\n      if (typeof FB === 'object') FB.XFBML.parse(document.getElementById('recent-posts'))\r\n      else {\r\n        let ele = document.createElement('script')\r\n        ele.setAttribute('src','!{fbSDK}')\r\n        ele.setAttribute('async', 'true')\r\n        ele.setAttribute('defer', 'true')\r\n        ele.setAttribute('crossorigin', 'anonymous')\r\n        document.body.appendChild(ele)\r\n      }\r\n    }\r\n    window.pjax ? loadFBComment() : window.addEventListener('load', loadFBComment)\r\n  })()\r\n"
  },
  {
    "path": "layout/includes/third-party/card-post-count/index.pug",
    "content": "case theme.comments.use[0]\r\n  when 'Twikoo'\r\n    include ./twikoo.pug\r\n  when 'Disqus'\r\n  when 'Disqusjs'\r\n    include ./disqus.pug\r\n  when 'Valine'\r\n    include ./valine.pug\r\n  when 'Waline'\r\n    include ./waline.pug\r\n  when 'Facebook Comments'\r\n    include ./fb.pug\r\n  when 'Remark42'\r\n    include ./remark42.pug\r\n  when 'Artalk'\r\n    include ./artalk.pug"
  },
  {
    "path": "layout/includes/third-party/card-post-count/remark42.pug",
    "content": "- const { host, siteId, option } = theme.remark42\r\n\r\nscript.\r\n  (()=>{\r\n    window.remark_config = Object.assign({\r\n      host: '!{host}',\r\n      site_id: '!{siteId}',\r\n    },!{JSON.stringify(option)})\r\n\r\n    function getCount () {\r\n      const s = document.createElement('script')\r\n      s.src = remark_config.host + '/web/counter.js'\r\n      s.defer = true\r\n      document.head.appendChild(s)\r\n    }\r\n\r\n    window.pjax ? getCount() : window.addEventListener('load', getCount)\r\n  })()"
  },
  {
    "path": "layout/includes/third-party/card-post-count/twikoo.pug",
    "content": "script.\r\n  (() => {\r\n    const getCommentUrl = () => {\r\n      const eleGroup = document.querySelectorAll('#recent-posts .article-title')\r\n      let urlArray = []\r\n      eleGroup.forEach(i=>{\r\n        urlArray.push(i.getAttribute('href'))\r\n      })\r\n      return urlArray\r\n    }\r\n\r\n    const getCount = () => {\r\n      const runTwikoo = () => {\r\n        twikoo.getCommentsCount({\r\n          envId: '!{theme.twikoo.envId}',\r\n          region: '!{theme.twikoo.region}',\r\n          urls: getCommentUrl(),\r\n          includeReply: false\r\n        }).then(function (res) {\r\n          document.querySelectorAll('#recent-posts .twikoo-count').forEach((item,index) => {\r\n            if (res[index]) {\r\n              item.textContent = res[index].count\r\n            }\r\n          })\r\n        }).catch(function (err) {\r\n          console.log(err)\r\n        })\r\n      }\r\n\r\n        if (typeof twikoo === 'object') {\r\n          runTwikoo()\r\n        } else {\r\n          btf.getScript('!{url_for(theme.asset.twikoo)}').then(runTwikoo)\r\n        }\r\n    }\r\n\r\n    window.pjax ? getCount() : window.addEventListener('load', getCount)\r\n\r\n  })()"
  },
  {
    "path": "layout/includes/third-party/card-post-count/valine.pug",
    "content": "script.\r\n  (() => {\r\n    function loadValine () {\r\n      function initValine () {\r\n        let initData = {\r\n          el: '#vcomment',\r\n          appId: '#{theme.valine.appId}',\r\n          appKey: '#{theme.valine.appKey}',\r\n          serverURLs: '#{theme.valine.serverURLs}'\r\n        }\r\n        \r\n        const valine = new Valine(initData)\r\n      }\r\n\r\n      if (typeof Valine === 'function') initValine() \r\n      else btf.getScript('!{url_for(theme.asset.valine)}').then(initValine)\r\n    }\r\n\r\n    window.pjax ? loadValine() : window.addEventListener('load', loadValine)\r\n  })()\r\n"
  },
  {
    "path": "layout/includes/third-party/card-post-count/waline.pug",
    "content": "- const serverURL = theme.waline.serverURL.replace(/\\/$/, '')\r\nscript.\r\n  (() => {\r\n    async function loadWaline () {\r\n      try {\r\n        const eleGroup = document.querySelectorAll('#recent-posts .waline-comment-count')\r\n        const keyArray = Array.from(eleGroup).map(i => i.getAttribute('data-path'))\r\n\r\n        const res = await fetch(`!{serverURL}/api/comment?type=count&url=${keyArray}`, { method: 'GET' })\r\n        const result = await res.json()\r\n        \r\n        result.data.forEach((count, index) => {\r\n          eleGroup[index].textContent = count\r\n        })\r\n      } catch (err) {\r\n        console.error(err)\r\n      }\r\n    }\r\n\r\n    window.pjax ? loadWaline() : window.addEventListener('load', loadWaline)\r\n  })()\r\n"
  },
  {
    "path": "layout/includes/third-party/chat/chatra.pug",
    "content": "//- https://chatra.io/help/api/\r\nscript.\r\n  (() => {\r\n    window.ChatraID = '#{theme.chatra.id}'\r\n    window.Chatra = window.Chatra || function() {\r\n      (window.Chatra.q = window.Chatra.q || []).push(arguments)\r\n    }\r\n\r\n    btf.getScript('https://call.chatra.io/chatra.js').then(() => {\r\n      const isChatBtn = !{theme.chat.rightside_button}\r\n      const isChatHideShow = !{theme.chat.button_hide_show}\r\n\r\n      if (isChatBtn) {\r\n        const close = () => {\r\n          Chatra('minimizeWidget')\r\n          Chatra('hide')\r\n        }\r\n\r\n        const open = () => {\r\n          Chatra('openChat', true)\r\n          Chatra('show')\r\n        }\r\n\r\n        window.ChatraSetup = { startHidden: true }\r\n      \r\n        window.chatBtnFn = () => document.getElementById('chatra').classList.contains('chatra--expanded') ? close() : open()\r\n\r\n        document.getElementById('chat-btn').style.display = 'block'\r\n      } else if (isChatHideShow) {\r\n        window.chatBtn = {\r\n          hide: () => Chatra('hide'),\r\n          show: () => Chatra('show')\r\n        }\r\n      }\r\n    })\r\n  })()\r\n\r\n\r\n"
  },
  {
    "path": "layout/includes/third-party/chat/crisp.pug",
    "content": "script.\r\n  (() => {\r\n    window.$crisp = ['safe', true]\r\n    window.CRISP_WEBSITE_ID = \"!{theme.crisp.website_id}\"\r\n\r\n    btf.getScript('https://client.crisp.chat/l.js').then(() => {\r\n      const isChatBtn = !{theme.chat.rightside_button}\r\n      const isChatHideShow = !{theme.chat.button_hide_show}\r\n\r\n      if (isChatBtn) {\r\n        const open = () => {\r\n          $crisp.push([\"do\", \"chat:show\"])\r\n          $crisp.push([\"do\", \"chat:open\"])\r\n        }\r\n\r\n        const close = () => $crisp.push([\"do\", \"chat:hide\"])\r\n\r\n        close()\r\n\r\n        $crisp.push([\"on\", \"chat:closed\", close])\r\n\r\n        window.chatBtnFn = () => $crisp.is(\"chat:visible\") ? close() : open()\r\n\r\n        document.getElementById('chat-btn').style.display = 'block'\r\n      } else if (isChatHideShow) {\r\n        window.chatBtn = {\r\n          hide: () => $crisp.push([\"do\", \"chat:hide\"]),\r\n          show: () => $crisp.push([\"do\", \"chat:show\"])\r\n        }\r\n      }\r\n    })\r\n  })()"
  },
  {
    "path": "layout/includes/third-party/chat/index.pug",
    "content": "case theme.chat.use\r\n  when 'chatra'\r\n    include ./chatra.pug\r\n  when 'tidio'\r\n    include ./tidio.pug\r\n  when 'crisp'\r\n    include ./crisp.pug"
  },
  {
    "path": "layout/includes/third-party/chat/tidio.pug",
    "content": "script.\r\n  (() => {\r\n    btf.getScript('//code.tidio.co/!{theme.tidio.public_key}.js').then(() => {\r\n      const isChatBtn = !{theme.chat.rightside_button}\r\n      const isChatHideShow = !{theme.chat.button_hide_show}\r\n\r\n      if (isChatBtn) {\r\n        let isShow = false\r\n        const close = () => {\r\n          window.tidioChatApi.hide()\r\n          isShow = false\r\n        }\r\n        \r\n        const open = () => {\r\n          window.tidioChatApi.open()\r\n          window.tidioChatApi.show()\r\n          isShow = true\r\n        }\r\n\r\n        const onTidioChatApiReady = () => {\r\n          window.tidioChatApi.hide()\r\n          window.tidioChatApi.on(\"close\", close)\r\n        }\r\n        if (window.tidioChatApi) {\r\n          window.tidioChatApi.on(\"ready\", onTidioChatApiReady)\r\n        } else {\r\n          document.addEventListener(\"tidioChat-ready\", onTidioChatApiReady)\r\n        }\r\n\r\n        window.chatBtnFn = () => {\r\n          if (!window.tidioChatApi) return\r\n          isShow ? close() : open()\r\n        }\r\n\r\n        document.getElementById('chat-btn').style.display = 'block'\r\n\r\n      } else if (isChatHideShow) {\r\n        window.chatBtn = {\r\n          hide: () => window.tidioChatApi && window.tidioChatApi.hide(),\r\n          show: () => window.tidioChatApi && window.tidioChatApi.show()\r\n        }\r\n      }\r\n    })\r\n  })()\r\n\r\n"
  },
  {
    "path": "layout/includes/third-party/comments/artalk.pug",
    "content": "- const { server, site, option } = theme.artalk\r\n- const { use, lazyload } = theme.comments\r\n\r\nscript.\r\n  (() => {\r\n    let artalkItem = null\r\n    const option = !{JSON.stringify(option)}\r\n    const isShuoshuo = GLOBAL_CONFIG_SITE.pageType === 'shuoshuo'\r\n\r\n    const destroyArtalk = () => {\r\n      if (artalkItem) {\r\n        artalkItem.destroy()\r\n        artalkItem = null\r\n      }\r\n    }\r\n\r\n    const artalkChangeMode = theme => artalkItem && artalkItem.setDarkMode(theme === 'dark')\r\n\r\n    const initArtalk = (el = document, pageKey = location.pathname) => {\r\n      artalkItem = Artalk.init({\r\n        el: el.querySelector('#artalk-wrap'),\r\n        server: '!{server}',\r\n        site: '!{site}',\r\n        darkMode: document.documentElement.getAttribute('data-theme') === 'dark',\r\n        ...option,\r\n        pageKey: isShuoshuo ? pageKey : (option && option.pageKey) || pageKey\r\n      })\r\n\r\n      if (GLOBAL_CONFIG.lightbox === 'null') return\r\n      artalkItem.on('list-loaded', () => {\r\n        artalkItem.ctx.get('list').getCommentNodes().forEach(comment => {\r\n          const $content = comment.getRender().$content\r\n          btf.loadLightbox($content.querySelectorAll('img:not([atk-emoticon])'))\r\n        })\r\n      })\r\n\r\n      if (isShuoshuo) {\r\n        window.shuoshuoComment.destroyArtalk = () => {\r\n          destroyArtalk()\r\n          if (el.children.length) {\r\n            el.innerHTML = ''\r\n            el.classList.add('no-comment')\r\n          }\r\n        }\r\n      }\r\n\r\n      btf.addGlobalFn('pjaxSendOnce', destroyArtalk, 'destroyArtalk')\r\n      btf.addGlobalFn('themeChange', artalkChangeMode, 'artalk')\r\n    }\r\n\r\n    const loadArtalk = async (el, pageKey) => {\r\n      if (typeof Artalk === 'object') initArtalk(el, pageKey)\r\n      else {\r\n        await btf.getCSS('!{url_for(theme.asset.artalk_css)}')\r\n        await btf.getScript('!{url_for(theme.asset.artalk_js)}')\r\n        initArtalk(el, pageKey)\r\n      }\r\n    }\r\n\r\n    if (isShuoshuo) {\r\n      '!{use[0]}' === 'Artalk'\r\n        ? window.shuoshuoComment = { loadComment: loadArtalk }\r\n        : window.loadOtherComment = loadArtalk\r\n      return\r\n    }\r\n\r\n    if ('!{use[0]}' === 'Artalk' || !!{lazyload}) {\r\n      if (!{lazyload}) btf.loadComment(document.getElementById('artalk-wrap'), loadArtalk)\r\n      else setTimeout(loadArtalk, 100)\r\n    } else {\r\n      window.loadOtherComment = loadArtalk\r\n    }\r\n  })()"
  },
  {
    "path": "layout/includes/third-party/comments/disqus.pug",
    "content": "- const disqusPageTitle = page.title.replace(/'/ig,\"\\\\'\")\r\n- const { shortname, apikey } = theme.disqus\r\n- const { use, lazyload, count } = theme.comments\r\n\r\nscript.\r\n  (() => {\r\n    const isShuoshuo = GLOBAL_CONFIG_SITE.pageType === 'shuoshuo'\r\n\r\n    const disqusReset = conf => {\r\n      window.DISQUS && window.DISQUS.reset({\r\n        reload: true,\r\n        config: conf\r\n      })\r\n    }\r\n\r\n    const loadDisqus = (el, path) => {\r\n      if (isShuoshuo) {\r\n        window.shuoshuoComment.destroyDisqus = () => {\r\n          if (el.children.length) {\r\n            el.innerHTML = ''\r\n            el.classList.add('no-comment')\r\n          }\r\n        }\r\n      }\r\n\r\n      window.disqus_identifier = isShuoshuo ? path : '!{ url_for(page.path) }'\r\n      window.disqus_url = isShuoshuo ? location.origin + path : '!{ page.permalink }'\r\n\r\n      const disqus_config = function () {\r\n        this.page.url = disqus_url\r\n        this.page.identifier = disqus_identifier\r\n        this.page.title = '!{ disqusPageTitle }'\r\n      }\r\n\r\n      if (window.DISQUS) disqusReset(disqus_config)\r\n      else {\r\n        const script = document.createElement('script')\r\n        script.src = 'https://!{shortname}.disqus.com/embed.js'\r\n        script.setAttribute('data-timestamp', +new Date())\r\n        document.head.appendChild(script)\r\n      }\r\n\r\n      btf.addGlobalFn('themeChange', () => disqusReset(disqus_config), 'disqus')\r\n    }\r\n\r\n    const getCount = async() => {\r\n      try {\r\n        const eleGroup = document.querySelector('#post-meta .disqus-comment-count')\r\n        if (!eleGroup) return\r\n        const cleanedLinks = eleGroup.href.replace(/#post-comment$/, '')\r\n\r\n        const res = await fetch(`https://disqus.com/api/3.0/threads/set.json?forum=!{shortname}&api_key=!{apikey}&thread:link=${cleanedLinks}`,{\r\n          method: 'GET'\r\n        })\r\n        const result = await res.json()\r\n\r\n        const count = result.response.length ? result.response[0].posts : 0\r\n        eleGroup.textContent = count\r\n      } catch (err) {\r\n        console.error(err)\r\n      }\r\n    }\r\n\r\n    if (isShuoshuo) {\r\n      '!{use[0]}' === 'Disqus'\r\n        ? window.shuoshuoComment = { loadComment: loadDisqus }\r\n        : window.loadOtherComment = loadDisqus\r\n      return\r\n    }\r\n\r\n    if ('!{use[0]}' === 'Disqus' || !!{lazyload}) {\r\n      if (!{lazyload}) btf.loadComment(document.getElementById('disqus_thread'), loadDisqus)\r\n      else {\r\n        loadDisqus()\r\n        !{ count ? `GLOBAL_CONFIG_SITE.pageType === 'post' && getCount()` : '' }\r\n      }\r\n    } else {\r\n      window.loadOtherComment = loadDisqus\r\n    }\r\n  })()\r\n"
  },
  {
    "path": "layout/includes/third-party/comments/disqusjs.pug",
    "content": "- let disqusjsPageTitle = page.title && page.title.replace(/'/ig,\"\\\\'\")\r\n- const { shortname:dqShortname, apikey:dqApikey, option:dqOption } = theme.disqusjs\r\n\r\nscript.\r\n  (() => {\r\n    const isShuoshuo = GLOBAL_CONFIG_SITE.pageType === 'shuoshuo'== 'shuoshuo'\r\n    const dqOption = !{JSON.stringify(dqOption)}\r\n\r\n    const destroyDisqusjs = () => {\r\n      disqusjs.destroy()\r\n      window.disqusjs = null\r\n    }\r\n\r\n    const themeChange = (el, path) => {\r\n      destroyDisqusjs()\r\n      initDisqusjs(el, path)\r\n    }\r\n\r\n    const initDisqusjs = (el = document, path) => {\r\n      if (isShuoshuo) {\r\n        window.shuoshuoComment.destroyDisqusjs = () => {\r\n          destroyDisqusjs()\r\n          if (el.children.length) {\r\n            el.innerHTML = ''\r\n            el.classList.add('no-comment')\r\n          }\r\n        }\r\n      }\r\n\r\n      disqusjs = new DisqusJS({\r\n        shortname: '!{dqShortname}',\r\n        title: '!{ disqusjsPageTitle }',\r\n        apikey: '!{dqApikey}',\r\n        ...dqOption,\r\n        identifier: isShuoshuo ? path : (dqOption && dqOption.identifier) || '!{ url_for(page.path) }',\r\n        url: isShuoshuo ? location.origin + path : (dqOption && dqOption.url) || '!{ page.permalink }'\r\n      })\r\n\r\n      disqusjs.render(el.querySelector('#disqusjs-wrap'))\r\n\r\n      btf.addGlobalFn('themeChange', () => themeChange(el, path), 'disqusjs')\r\n    }\r\n\r\n    const loadDisqusjs = async(el, path) => {\r\n      if (window.disqusJsLoad) initDisqusjs(el, path)\r\n      else {\r\n        await btf.getCSS('!{url_for(theme.asset.disqusjs_css)}')\r\n        await btf.getScript('!{url_for(theme.asset.disqusjs)}')\r\n        initDisqusjs(el, path)\r\n        window.disqusJsLoad = true\r\n      }\r\n    }\r\n\r\n    const getCount = async() => {\r\n      try {\r\n        const eleGroup = document.querySelector('#post-meta .disqusjs-comment-count')\r\n        if (!eleGroup) return\r\n        const cleanedLinks = eleGroup.href.replace(/#post-comment$/, '')\r\n\r\n        const res = await fetch(`https://disqus.com/api/3.0/threads/set.json?forum=!{dqShortname}&api_key=!{dqApikey}&thread:link=${cleanedLinks}`,{\r\n          method: 'GET'\r\n        })\r\n        const result = await res.json()\r\n        const count = result.response.length ? result.response[0].posts : 0\r\n        eleGroup.textContent = count\r\n      } catch (err) {\r\n        console.error(err)\r\n      }\r\n    }\r\n\r\n    if (isShuoshuo) {\r\n      '!{theme.comments.use[0]}' === 'Disqusjs'\r\n        ? window.shuoshuoComment = { loadComment: loadDisqusjs }\r\n        : window.loadOtherComment = loadDisqusjs\r\n      return\r\n    }\r\n\r\n    if ('!{theme.comments.use[0]}' === 'Disqusjs' || !!{theme.comments.lazyload}) {\r\n      if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('disqusjs-wrap'), loadDisqusjs)\r\n      else {\r\n        loadDisqusjs()\r\n        !{ theme.comments.count ? `GLOBAL_CONFIG_SITE.pageType === 'post' && getCount()` : '' }\r\n      }\r\n    } else {\r\n      window.loadOtherComment = loadDisqusjs\r\n    }\r\n  })()"
  },
  {
    "path": "layout/includes/third-party/comments/facebook_comments.pug",
    "content": "- const fbSDKVer = 'v20.0'\r\n- const fbSDK = `https://connect.facebook.net/${theme.facebook_comments.lang}/sdk.js#xfbml=1&version=${fbSDKVer}`\r\n\r\nscript.\r\n  (()=>{\r\n    const isShuoshuo = GLOBAL_CONFIG_SITE.pageType === 'shuoshuo'== 'shuoshuo'\r\n\r\n    const loadFBComment = (el = document, path) => {\r\n      if (isShuoshuo) {\r\n        window.shuoshuoComment.destroyFB = () => {\r\n          if (el.children.length) {\r\n            el.innerHTML = ''\r\n            el.classList.add('no-comment')\r\n          }\r\n        }\r\n      }\r\n\r\n      document.getElementById('fb-root') ? '' : document.body.insertAdjacentHTML('afterend', '<div id=\"fb-root\"></div>')\r\n\r\n      const themeNow = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'\r\n      const $fbComment = el.getElementsByClassName('fb-comments')[0]\r\n      $fbComment.setAttribute('data-colorscheme',themeNow)\r\n      $fbComment.setAttribute('data-href', isShuoshuo ? '!{urlNoIndex(page.permalink)}' + '#' + path : '!{urlNoIndex(page.permalink)}')\r\n\r\n      if (typeof FB === 'object') {\r\n        FB.XFBML.parse(document.getElementsByClassName('post-meta-commentcount')[0])\r\n        FB.XFBML.parse(el.querySelector('#post-comment'))\r\n      }\r\n      else {\r\n        let ele = document.createElement('script')\r\n        ele.setAttribute('src','!{fbSDK}')\r\n        ele.setAttribute('async', 'true')\r\n        ele.setAttribute('defer', 'true')\r\n        ele.setAttribute('crossorigin', 'anonymous')\r\n        ele.setAttribute('id', 'facebook-jssdk')\r\n        document.getElementById('fb-root').insertAdjacentElement('afterbegin',ele)\r\n      }\r\n    }\r\n\r\n    const fbModeChange = theme => {\r\n      const $fbComment = document.getElementsByClassName('fb-comments')[0]\r\n      if ($fbComment && typeof FB === 'object') {\r\n        $fbComment.setAttribute('data-colorscheme',theme)\r\n        FB.XFBML.parse(document.getElementById('post-comment'))\r\n      }\r\n    }\r\n\r\n    btf.addGlobalFn('themeChange', fbModeChange, 'facebook_comments')\r\n\r\n    if (isShuoshuo) {\r\n      '!{theme.comments.use[0]}' === 'Facebook Comments'\r\n        ? window.shuoshuoComment = { loadComment: loadFBComment }\r\n        : window.loadOtherComment = loadFBComment\r\n      return\r\n    }\r\n\r\n    if ('!{theme.comments.use[0]}' === 'Facebook Comments' || !!{theme.comments.lazyload}) {\r\n      if (!{theme.comments.lazyload}) btf.loadComment(document.querySelector('#post-comment .fb-comments'), loadFBComment)\r\n      else loadFBComment()\r\n    } else {\r\n      window.loadOtherComment = loadFBComment\r\n    }\r\n  })()\r\n\r\n"
  },
  {
    "path": "layout/includes/third-party/comments/giscus.pug",
    "content": "- const { use, lazyload } = theme.comments\r\n- const { repo, repo_id, category_id, light_theme, dark_theme, js, option } = theme.giscus\r\n- const giscusUrl = js || 'https://giscus.app/client.js'\r\n- const giscusOriginUrl = new URL(giscusUrl).origin\r\n\r\nscript.\r\n  (() => {\r\n    const isShuoshuo = GLOBAL_CONFIG_SITE.pageType === 'shuoshuo'\r\n    const option = !{JSON.stringify(option)}\r\n\r\n    const getGiscusTheme = theme => theme === 'dark' ? '!{dark_theme}' : '!{light_theme}'\r\n\r\n    const createScriptElement = config => {\r\n      const ele = document.createElement('script')\r\n      Object.entries(config).forEach(([key, value]) => {\r\n        ele.setAttribute(key, value)\r\n      })\r\n      return ele\r\n    }\r\n\r\n    const loadGiscus = (el = document, key) => {\r\n      const mappingConfig = isShuoshuo\r\n        ? { 'data-mapping': 'specific', 'data-term': key }\r\n        : { 'data-mapping': (option && option['data-mapping']) || 'pathname' }\r\n\r\n      const giscusConfig = {\r\n        src: '!{giscusUrl}',\r\n        'data-repo': '!{repo}',\r\n        'data-repo-id': '!{repo_id}',\r\n        'data-category-id': '!{category_id}',\r\n        'data-theme': getGiscusTheme(document.documentElement.getAttribute('data-theme')),\r\n        'data-reactions-enabled': '1',\r\n        crossorigin: 'anonymous',\r\n        async: true,\r\n        ...option,\r\n        ...mappingConfig\r\n      }\r\n\r\n      const scriptElement = createScriptElement(giscusConfig)\r\n\r\n      el.querySelector('#giscus-wrap').appendChild(scriptElement)\r\n\r\n      if (isShuoshuo) {\r\n        window.shuoshuoComment.destroyGiscus = () => {\r\n          if (el.children.length) {\r\n            el.innerHTML = ''\r\n            el.classList.add('no-comment')\r\n          }\r\n        }\r\n      }\r\n    }\r\n\r\n    const changeGiscusTheme = theme => {\r\n      const iframe = document.querySelector('#giscus-wrap iframe')\r\n      if (iframe) {\r\n        const message = {\r\n          giscus: {\r\n            setConfig: {\r\n              theme: getGiscusTheme(theme)\r\n            }\r\n          }\r\n        }\r\n        iframe.contentWindow.postMessage(message, '!{giscusOriginUrl}')\r\n      }\r\n    }\r\n\r\n    btf.addGlobalFn('themeChange', changeGiscusTheme, 'giscus')\r\n\r\n    if (isShuoshuo) {\r\n      '!{use[0]}' === 'Giscus'\r\n        ? window.shuoshuoComment = { loadComment: loadGiscus }\r\n        : window.loadOtherComment = loadGiscus\r\n      return\r\n    }\r\n\r\n    if ('!{use[0]}' === 'Giscus' || !!{lazyload}) {\r\n      if (!{lazyload}) btf.loadComment(document.getElementById('giscus-wrap'), loadGiscus)\r\n      else loadGiscus()\r\n    } else {\r\n      window.loadOtherComment = loadGiscus\r\n    }\r\n  })()\r\n"
  },
  {
    "path": "layout/includes/third-party/comments/gitalk.pug",
    "content": "- const { client_id, client_secret, repo, owner, admin, option } = theme.gitalk\r\n\r\nscript.\r\n  (() => {\r\n    const isShuoshuo = GLOBAL_CONFIG_SITE.pageType === 'shuoshuo'\r\n    const option = !{JSON.stringify(option)}\r\n\r\n    const commentCount = n => {\r\n      const isCommentCount = document.querySelector('#post-meta .gitalk-comment-count')\r\n      if (isCommentCount) {\r\n        isCommentCount.textContent= n\r\n      }\r\n    }\r\n\r\n    const initGitalk = (el, path) => {\r\n      if (isShuoshuo) {\r\n        window.shuoshuoComment.destroyGitalk = () => {\r\n          if (el.children.length) {\r\n            el.innerHTML = ''\r\n            el.classList.add('no-comment')\r\n          }\r\n        }\r\n      }\r\n\r\n      const gitalk = new Gitalk({\r\n        clientID: '!{client_id}',\r\n        clientSecret: '!{client_secret}',\r\n        repo: '!{repo}',\r\n        owner: '!{owner}',\r\n        admin: ['!{admin}'],\r\n        updateCountCallback: commentCount,\r\n        ...option,\r\n        id: isShuoshuo ? path : (option && option.id) || '!{md5(page.path)}'\r\n      })\r\n\r\n      gitalk.render('gitalk-container')\r\n    }\r\n\r\n    const loadGitalk = async(el, path) => {\r\n      if (typeof Gitalk === 'function') initGitalk(el, path)\r\n      else {\r\n        await btf.getCSS('!{url_for(theme.asset.gitalk_css)}')\r\n        await btf.getScript('!{url_for(theme.asset.gitalk)}')\r\n        initGitalk(el, path)\r\n      }\r\n    }\r\n\r\n    if (isShuoshuo) {\r\n      '!{theme.comments.use[0]}' === 'Gitalk'\r\n        ? window.shuoshuoComment = { loadComment: loadGitalk }\r\n        : window.loadOtherComment = loadGitalk\r\n      return\r\n    }\r\n\r\n    if ('!{theme.comments.use[0]}' === 'Gitalk' || !!{theme.comments.lazyload}) {\r\n      if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('gitalk-container'), loadGitalk)\r\n      else loadGitalk()\r\n    } else {\r\n      window.loadOtherComment = loadGitalk\r\n    }\r\n  })()\r\n\r\n\r\n\r\n"
  },
  {
    "path": "layout/includes/third-party/comments/index.pug",
    "content": "- let defaultComment = theme.comments.use[0]\r\nhr.custom-hr\r\n#post-comment\r\n  .comment-head\r\n    .comment-headline\r\n      i.fas.fa-comments.fa-fw\r\n      span= ' ' + _p('comment')\r\n\r\n    if theme.comments.use.length > 1\r\n      .comment-switch\r\n        span.first-comment=defaultComment\r\n        span#switch-btn\r\n        span.second-comment=theme.comments.use[1]\r\n\r\n\r\n  .comment-wrap\r\n    each name in theme.comments.use\r\n      div\r\n        case name\r\n          when 'Disqus'\r\n            #disqus_thread\r\n          when 'Valine'\r\n            #vcomment.vcomment\r\n          when 'Disqusjs'\r\n            #disqusjs-wrap\r\n          when 'Livere'\r\n            #lv-container(data-id=\"city\" data-uid=theme.livere.uid)\r\n          when 'Gitalk'\r\n            #gitalk-container\r\n          when 'Utterances'\r\n            #utterances-wrap\r\n          when 'Twikoo'\r\n            #twikoo-wrap\r\n          when 'Waline'\r\n            #waline-wrap\r\n          when 'Giscus'\r\n            #giscus-wrap\r\n          when 'Facebook Comments'\r\n            .fb-comments(data-colorscheme = theme.display_mode === 'dark' ? 'dark' : 'light'\r\n                        data-numposts= theme.facebook_comments.pageSize || 10\r\n                        data-order-by= theme.facebook_comments.order_by || 'social'\r\n                        data-width=\"100%\")\r\n          when 'Remark42'\r\n            #remark42\r\n          when 'Artalk'\r\n            #artalk-wrap\r\n"
  },
  {
    "path": "layout/includes/third-party/comments/js.pug",
    "content": "each name in theme.comments.use\r\n  case name\r\n    when 'Valine'\r\n      !=partial('includes/third-party/comments/valine', {}, {cache: true})\r\n    when 'Disqus'\r\n      include ./disqus.pug\r\n    when 'Disqusjs'\r\n      include ./disqusjs.pug\r\n    when 'Livere'\r\n      !=partial('includes/third-party/comments/livere', {}, {cache: true})\r\n    when 'Gitalk'\r\n      include ./gitalk.pug\r\n    when 'Utterances'\r\n      !=partial('includes/third-party/comments/utterances', {}, {cache: true})\r\n    when 'Twikoo'\r\n      !=partial('includes/third-party/comments/twikoo', {}, {cache: true})\r\n    when 'Waline'\r\n      !=partial('includes/third-party/comments/waline', {}, {cache: true})\r\n    when 'Giscus'\r\n      !=partial('includes/third-party/comments/giscus', {}, {cache: true})\r\n    when 'Facebook Comments'\r\n      include ./facebook_comments.pug\r\n    when 'Remark42'\r\n      !=partial('includes/third-party/comments/remark42', {}, {cache: true})\r\n    when 'Artalk'\r\n      !=partial('includes/third-party/comments/artalk', {}, {cache: true})"
  },
  {
    "path": "layout/includes/third-party/comments/livere.pug",
    "content": "- const { use, lazyload } = theme.comments\r\n\r\nscript.\r\n  (() => {\r\n    const isShuoshuo = GLOBAL_CONFIG_SITE.pageType === 'shuoshuo'\r\n\r\n    const loadLivere = (el, path) => {\r\n      window.livereOptions = {\r\n        refer: path || location.pathname\r\n      }\r\n\r\n      if (isShuoshuo) {\r\n        window.shuoshuoComment.destroyLivere = () => {\r\n          if (el.children.length) {\r\n            el.innerHTML = ''\r\n            el.classList.add('no-comment')\r\n          }\r\n        }\r\n      }\r\n\r\n      if (typeof LivereTower === 'object') window.LivereTower.init()\r\n      else {\r\n        (function(d, s) {\r\n            var j, e = d.getElementsByTagName(s)[0];\r\n            if (typeof LivereTower === 'function') { return; }\r\n            j = d.createElement(s);\r\n            j.src = 'https://cdn-city.livere.com/js/embed.dist.js';\r\n            j.async = true;\r\n            e.parentNode.insertBefore(j, e);\r\n        })(document, 'script');\r\n      }\r\n    }\r\n\r\n    if (isShuoshuo) {\r\n      '!{use[0]}' === 'Livere'\r\n        ? window.shuoshuoComment = { loadComment: loadLivere }\r\n        : window.loadOtherComment = loadLivere\r\n      return\r\n    }\r\n\r\n    if ('!{use[0]}' === 'Livere' || !!{lazyload}) {\r\n      if (!{lazyload}) btf.loadComment(document.getElementById('lv-container'), loadLivere)\r\n      else loadLivere()\r\n    } else {\r\n      window.loadOtherComment = loadLivere\r\n    }\r\n  })()"
  },
  {
    "path": "layout/includes/third-party/comments/remark42.pug",
    "content": "- const { host, siteId, option } = theme.remark42\r\n\r\nscript.\r\n  (() => {\r\n    const isShuoshuo = GLOBAL_CONFIG_SITE.pageType === 'shuoshuo'\r\n    const options = !{JSON.stringify(option)}\r\n\r\n    const loadScript = src => {\r\n      const script = document.createElement('script')\r\n      script.src = src\r\n      script.defer = true\r\n      document.head.appendChild(script)\r\n    }\r\n\r\n    const addRemark42 = () => loadScript('!{host}/web/embed.js')\r\n\r\n    const getCount = () => document.querySelector('.remark42__counter') && loadScript('!{host}/web/count.js')\r\n\r\n    const destroyRemark42 = () => window.remark42Instance && window.remark42Instance.destroy()\r\n\r\n    const initRemark42 = remark_config => {\r\n      if (window.REMARK42) {\r\n        destroyRemark42()\r\n        window.remark42Instance = window.REMARK42.createInstance({\r\n          ...remark_config\r\n        })\r\n      }\r\n    }\r\n\r\n    const loadRemark42 = (el, path) => {\r\n      if (isShuoshuo) {\r\n        window.shuoshuoComment.destroyRemark42 = () => {\r\n          destroyRemark42()\r\n          if (el.children.length) {\r\n            el.innerHTML = ''\r\n            el.classList.add('no-comment')\r\n          }\r\n        }\r\n      }\r\n\r\n      window.remark_config = {\r\n        host: '!{host}',\r\n        site_id: '!{siteId}',\r\n        theme: document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light',\r\n        ...options,\r\n        url: isShuoshuo ? window.location.origin + path : (options && options.url) || window.location.origin + window.location.pathname\r\n      }\r\n\r\n      if (window.REMARK42) {\r\n        initRemark42(remark_config)\r\n        getCount()\r\n      } else {\r\n        addRemark42()\r\n        window.addEventListener('REMARK42::ready', () => {\r\n          initRemark42(remark_config)\r\n          getCount()\r\n        })\r\n      }\r\n    }\r\n\r\n    const remarkChangeMode = theme => window.REMARK42 && window.REMARK42.changeTheme(theme)\r\n\r\n    btf.addGlobalFn('themeChange', remarkChangeMode, 'remark42')\r\n\r\n    if (isShuoshuo) {\r\n      '!{theme.comments.use[0]}' === 'Remark42'\r\n        ? window.shuoshuoComment = { loadComment: loadRemark42 }\r\n        : window.loadOtherComment = loadRemark42\r\n      return\r\n    }\r\n\r\n    if ('!{theme.comments.use[0]}' === 'Remark42' || !!{theme.comments.lazyload}) {\r\n      if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('remark42'), loadRemark42)\r\n      else loadRemark42()\r\n    } else {\r\n      window.loadOtherComment = loadRemark42\r\n    }\r\n  })()"
  },
  {
    "path": "layout/includes/third-party/comments/twikoo.pug",
    "content": "- const { envId, region, option } = theme.twikoo\r\n- const { use, lazyload, count } = theme.comments\r\n\r\nscript.\r\n  (() => {\r\n    const isShuoshuo = GLOBAL_CONFIG_SITE.pageType === 'shuoshuo'\r\n    const option = !{JSON.stringify(option)}\r\n\r\n    const getCount = () => {\r\n      const countELement = document.getElementById('twikoo-count')\r\n      if(!countELement) return\r\n      twikoo.getCommentsCount({\r\n        envId: '!{envId}',\r\n        region: '!{region}',\r\n        urls: [window.location.pathname],\r\n        includeReply: false\r\n      }).then(res => {\r\n        countELement.textContent = res[0].count\r\n      }).catch(err => {\r\n        console.error(err)\r\n      })\r\n    }\r\n\r\n    const init = (el = document, path = location.pathname) => {\r\n      twikoo.init({\r\n        el: el.querySelector('#twikoo-wrap'),\r\n        envId: '!{envId}',\r\n        region: '!{region}',\r\n        onCommentLoaded: () => {\r\n          btf.loadLightbox(document.querySelectorAll('#twikoo .tk-content img:not(.tk-owo-emotion)'))\r\n        },\r\n        ...option,\r\n        path: isShuoshuo ? path : (option && option.path) || path\r\n      })\r\n\r\n      !{count ? `GLOBAL_CONFIG_SITE.pageType === 'post' && getCount()` : ''}\r\n\r\n      isShuoshuo && (window.shuoshuoComment.destroyTwikoo = () => {\r\n        if (el.children.length) {\r\n          el.innerHTML = ''\r\n          el.classList.add('no-comment')\r\n        }\r\n      })\r\n    }\r\n\r\n    const loadTwikoo = (el, path) => {\r\n      if (typeof twikoo === 'object') setTimeout(() => init(el, path), 0)\r\n      else btf.getScript('!{url_for(theme.asset.twikoo)}').then(() => init(el, path))\r\n    }\r\n\r\n    if (isShuoshuo) {\r\n      '!{use[0]}' === 'Twikoo'\r\n        ? window.shuoshuoComment = { loadComment: loadTwikoo }\r\n        : window.loadOtherComment = loadTwikoo\r\n      return\r\n    }\r\n\r\n    if ('!{use[0]}' === 'Twikoo' || !!{lazyload}) {\r\n      if (!{lazyload}) btf.loadComment(document.getElementById('twikoo-wrap'), loadTwikoo)\r\n      else loadTwikoo()\r\n    } else {\r\n      window.loadOtherComment = loadTwikoo\r\n    }\r\n  })()"
  },
  {
    "path": "layout/includes/third-party/comments/utterances.pug",
    "content": "- const { use, lazyload } = theme.comments\r\n- const { repo, issue_term, light_theme, dark_theme, js, option } = theme.utterances\r\n- const utterancesUrl = js || 'https://utteranc.es/client.js'\r\n- const utterancesOriginUrl = new URL(utterancesUrl).origin\r\n\r\nscript.\r\n  (() => {\r\n    const isShuoshuo = GLOBAL_CONFIG_SITE.pageType === 'shuoshuo'\r\n    const option = !{JSON.stringify(option)}\r\n    const getUtterancesTheme = theme => theme === 'dark' ? '#{dark_theme}' : '#{light_theme}'\r\n\r\n    const loadUtterances = (el = document, key) => {\r\n      if (isShuoshuo) {\r\n        window.shuoshuoComment.destroyUtterances = () => {\r\n          if (el.children.length) {\r\n            el.innerHTML = ''\r\n            el.classList.add('no-comment')\r\n          }\r\n        }\r\n      }\r\n\r\n      const config = {\r\n        src: '!{utterancesUrl}',\r\n        repo: '!{repo}',\r\n        theme: getUtterancesTheme(document.documentElement.getAttribute('data-theme')),\r\n        crossorigin: 'anonymous',\r\n        async: true,\r\n        ...option,\r\n        'issue-term': isShuoshuo ? key : (option && option['issue-term']) || '!{issue_term}'\r\n      }\r\n\r\n      const ele = document.createElement('script')\r\n      Object.entries(config).forEach(([key, value]) => ele.setAttribute(key, value))\r\n      el.querySelector('#utterances-wrap').appendChild(ele)\r\n    }\r\n\r\n    const changeUtterancesTheme = theme => {\r\n      const iframe = document.querySelector('#utterances-wrap iframe')\r\n      if (iframe) {\r\n        const message = {\r\n          type: 'set-theme',\r\n          theme: getUtterancesTheme(theme)\r\n        };\r\n        iframe.contentWindow.postMessage(message, '!{utterancesOriginUrl}')\r\n      }\r\n    }\r\n\r\n    btf.addGlobalFn('themeChange', changeUtterancesTheme, 'utterances')\r\n\r\n    if (isShuoshuo) {\r\n      '!{use[0]}' === 'Utterances'\r\n        ? window.shuoshuoComment = { loadComment: loadUtterances }\r\n        : window.loadOtherComment = loadUtterances\r\n      return\r\n    }\r\n    \r\n    if ('!{use[0]}' === 'Utterances' || !!{lazyload}) {\r\n      if (!{lazyload}) btf.loadComment(document.getElementById('utterances-wrap'), loadUtterances)\r\n      else loadUtterances()\r\n    } else {\r\n      window.loadOtherComment = loadUtterances\r\n    }\r\n  })()"
  },
  {
    "path": "layout/includes/third-party/comments/valine.pug",
    "content": "- const { use, lazyload } = theme.comments\r\n- const { appId, appKey, avatar, serverURLs, visitor, option } = theme.valine\r\n\r\n- let emojiMaps = '\"\"'\r\nif site.data.valine\r\n  - emojiMaps = JSON.stringify(site.data.valine)\r\n\r\nscript.\r\n  (() => {\r\n    const isShuoshuo = GLOBAL_CONFIG_SITE.pageType === 'shuoshuo'\r\n    const option = !{JSON.stringify(option)}\r\n\r\n    const initValine = (el, path) => {\r\n      if (isShuoshuo) {\r\n        window.shuoshuoComment.destroyValine = () => {\r\n          if (el.children.length) {\r\n            el.innerHTML = ''\r\n            el.classList.add('no-comment')\r\n          }\r\n        }\r\n      }\r\n\r\n      const valineConfig = {\r\n        el: '#vcomment',\r\n        appId: '#{appId}',\r\n        appKey: '#{appKey}',\r\n        avatar: '#{avatar}',\r\n        serverURLs: '#{serverURLs}',\r\n        emojiMaps: !{emojiMaps},\r\n        visitor: #{visitor},\r\n        ...option,\r\n        path: isShuoshuo ? path : (option && option.path) || window.location.pathname\r\n      }\r\n\r\n      new Valine(valineConfig)\r\n    }\r\n\r\n    const loadValine = async (el, path) => {\r\n      if (typeof Valine === 'function') {\r\n        initValine(el, path)\r\n      } else {\r\n        await btf.getScript('!{url_for(theme.asset.valine)}')\r\n        initValine(el, path)\r\n      }\r\n    }\r\n\r\n    if (isShuoshuo) {\r\n      '!{use[0]}' === 'Valine'\r\n        ? window.shuoshuoComment = { loadComment: loadValine }\r\n        : window.loadOtherComment = loadValine\r\n      return\r\n    }\r\n\r\n    if ('!{use[0]}' === 'Valine' || !!{lazyload}) {\r\n      if (!{lazyload}) btf.loadComment(document.getElementById('vcomment'),loadValine)\r\n      else setTimeout(loadValine, 0)\r\n    } else {\r\n      window.loadOtherComment = loadValine\r\n    }\r\n  })()"
  },
  {
    "path": "layout/includes/third-party/comments/waline.pug",
    "content": "- const { serverURL, option, pageview } = theme.waline\r\n- const { lazyload, count, use } = theme.comments\r\n\r\nscript.\r\n  (() => {\r\n    let initFn = window.walineFn || null\r\n    const isShuoshuo = GLOBAL_CONFIG_SITE.pageType === 'shuoshuo'\r\n    const option = !{JSON.stringify(option)}\r\n\r\n    const destroyWaline = ele => ele.destroy()\r\n\r\n    const initWaline = (Fn, el = document, path = window.location.pathname) => {\r\n      const waline = Fn({\r\n        el: el.querySelector('#waline-wrap'),\r\n        serverURL: '!{serverURL}',\r\n        pageview: !{lazyload ? false : pageview},\r\n        dark: 'html[data-theme=\"dark\"]',\r\n        comment: !{lazyload ? false : count},\r\n        ...option,\r\n        path: isShuoshuo ? path : (option && option.path) || path\r\n      })\r\n\r\n      if (isShuoshuo) {\r\n        window.shuoshuoComment.destroyWaline = () => {\r\n          destroyWaline(waline)\r\n          if (el.children.length) {\r\n            el.innerHTML = ''\r\n            el.classList.add('no-comment')\r\n          }\r\n        }\r\n      }\r\n    }\r\n\r\n    const loadWaline = (el, path) => {\r\n      if (initFn) initWaline(initFn, el, path)\r\n      else {\r\n        btf.getCSS('!{url_for(theme.asset.waline_css)}')\r\n          .then(() => import('!{url_for(theme.asset.waline_js)}'))\r\n          .then(({ init }) => {\r\n            initFn = init || Waline.init\r\n            initWaline(initFn, el, path)\r\n            window.walineFn = initFn\r\n          })\r\n      }\r\n    }\r\n\r\n    if (isShuoshuo) {\r\n      '!{use[0]}' === 'Waline'\r\n        ? window.shuoshuoComment = { loadComment: loadWaline } \r\n        : window.loadOtherComment = loadWaline\r\n      return\r\n    }\r\n\r\n    if ('!{use[0]}' === 'Waline' || !!{lazyload}) {\r\n      if (!{lazyload}) btf.loadComment(document.getElementById('waline-wrap'),loadWaline)\r\n      else setTimeout(loadWaline, 0)\r\n    } else {\r\n      window.loadOtherComment = loadWaline\r\n    }\r\n  })()\r\n\r\n"
  },
  {
    "path": "layout/includes/third-party/effect.pug",
    "content": "if theme.fireworks && theme.fireworks.enable\r\n  canvas.fireworks(mobile=`${theme.fireworks.mobile}`)\r\n  script(src=url_for(theme.asset.fireworks))\r\n\r\nif (theme.canvas_ribbon && theme.canvas_ribbon.enable)\r\n  script(defer id=\"ribbon\" src=url_for(theme.asset.canvas_ribbon) size=theme.canvas_ribbon.size\r\n  alpha=theme.canvas_ribbon.alpha zIndex=theme.canvas_ribbon.zIndex mobile=`${theme.canvas_ribbon.mobile}` data-click=`${theme.canvas_ribbon.click_to_change}`)\r\n\r\nif (theme.canvas_fluttering_ribbon && theme.canvas_fluttering_ribbon.enable)\r\n  script(defer id=\"fluttering_ribbon\" mobile=`${theme.canvas_fluttering_ribbon.mobile}` src=url_for(theme.asset.canvas_fluttering_ribbon))\r\n\r\nif (theme.canvas_nest && theme.canvas_nest.enable)\r\n  script#canvas_nest(defer color=theme.canvas_nest.color opacity=theme.canvas_nest.opacity zIndex=theme.canvas_nest.zIndex count=theme.canvas_nest.count mobile=`${theme.canvas_nest.mobile}` src=url_for(theme.asset.canvas_nest))\r\n\r\nif theme.activate_power_mode.enable\r\n  script(src=url_for(theme.asset.activate_power_mode))\r\n  script.\r\n    POWERMODE.colorful = !{theme.activate_power_mode.colorful};\r\n    POWERMODE.shake = !{theme.activate_power_mode.shake};\r\n    POWERMODE.mobile = !{theme.activate_power_mode.mobile};\r\n    document.body.addEventListener('input', POWERMODE);\r\n\r\n//- 鼠標特效\r\nif theme.click_heart && theme.click_heart.enable\r\n  script#click-heart(src=url_for(theme.asset.click_heart) async mobile=`${theme.click_heart.mobile}`)\r\n\r\nif theme.clickShowText && theme.clickShowText.enable\r\n  script#click-show-text(\r\n    src= url_for(theme.asset.clickShowText)\r\n    data-mobile= `${theme.clickShowText.mobile}`\r\n    data-text= theme.clickShowText.text.join(\",\")\r\n    data-fontsize= theme.clickShowText.fontSize\r\n    data-random= `${theme.clickShowText.random}`\r\n    async\r\n  )"
  },
  {
    "path": "layout/includes/third-party/math/chartjs.pug",
    "content": "- const { fontColor, borderColor, scale_ticks_backdropColor } = theme.chartjs\r\n\r\nscript.\r\n  (() => {\r\n    const applyThemeDefaultsConfig = theme => {\r\n      if (theme === 'dark-mode') {\r\n        Chart.defaults.color = \"!{fontColor.dark}\"\r\n        Chart.defaults.borderColor = \"!{borderColor.dark}\"\r\n        Chart.defaults.scale.ticks.backdropColor = \"!{scale_ticks_backdropColor.dark}\"\r\n      } else {\r\n        Chart.defaults.color = \"!{fontColor.light}\"\r\n        Chart.defaults.borderColor = \"!{borderColor.light}\"\r\n        Chart.defaults.scale.ticks.backdropColor = \"!{scale_ticks_backdropColor.light}\"\r\n      }\r\n    }\r\n\r\n    // Recursively traverse the config object and automatically apply theme-specific color schemes\r\n    const applyThemeConfig = (obj, theme) => {\r\n      if (typeof obj !== 'object' || obj === null) return\r\n\r\n      Object.keys(obj).forEach(key => {\r\n        const value = obj[key]\r\n        // If the property is an object and has theme-specific options, apply them\r\n        if (typeof value === 'object' && value !== null) {\r\n          if (value[theme]) {\r\n            obj[key] = value[theme] // Apply the value for the current theme\r\n          } else {\r\n            // Recursively process child objects\r\n            applyThemeConfig(value, theme)\r\n          }\r\n        }\r\n      })\r\n    }\r\n\r\n    const runChartJS = ele => {\r\n      window.loadChartJS = true\r\n\r\n      Array.from(ele).forEach((item, index) => {\r\n        const chartSrc = item.firstElementChild\r\n        const chartID = item.getAttribute('data-chartjs-id') || ('chartjs-' + index) // Use custom ID or default ID\r\n        const width = item.getAttribute('data-width')\r\n        const existingCanvas = document.getElementById(chartID)\r\n\r\n        // If a canvas already exists, remove it to avoid rendering duplicates\r\n        if (existingCanvas) {\r\n            existingCanvas.parentNode.remove()\r\n        }\r\n\r\n        const chartDefinition = chartSrc.textContent\r\n        const canvas = document.createElement('canvas')\r\n        canvas.id = chartID\r\n\r\n        const div = document.createElement('div')\r\n        div.className = 'chartjs-wrap'\r\n\r\n        if (width) {\r\n          div.style.width = width\r\n        }\r\n\r\n        div.appendChild(canvas)\r\n        chartSrc.insertAdjacentElement('afterend', div)\r\n\r\n        const ctx = document.getElementById(chartID).getContext('2d')\r\n\r\n        const config = JSON.parse(chartDefinition)\r\n\r\n        const theme = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark-mode' : 'light-mode'\r\n\r\n        // Set default styles (initial setup)\r\n        applyThemeDefaultsConfig(theme)\r\n\r\n        // Automatically traverse the config and apply dual-mode color schemes\r\n        applyThemeConfig(config, theme)\r\n\r\n        new Chart(ctx, config)\r\n      })\r\n    }\r\n\r\n    const loadChartJS = () => {\r\n      const chartJSEle = document.querySelectorAll('#article-container .chartjs-container')\r\n      if (chartJSEle.length === 0) return\r\n\r\n      window.loadChartJS ? runChartJS(chartJSEle) : btf.getScript('!{url_for(theme.asset.chartjs)}').then(() => runChartJS(chartJSEle))\r\n    }\r\n\r\n    // Listen for theme change events\r\n    btf.addGlobalFn('themeChange', loadChartJS, 'chartjs')\r\n    btf.addGlobalFn('encrypt', loadChartJS, 'chartjs')\r\n\r\n    window.pjax ? loadChartJS() : document.addEventListener('DOMContentLoaded', loadChartJS)\r\n  })()\r\n"
  },
  {
    "path": "layout/includes/third-party/math/index.pug",
    "content": "case theme.math.use\r\n  when 'mathjax'\r\n    if (theme.math.per_page && (['post','page'].includes(globalPageType))) || page.mathjax\r\n      include ./mathjax.pug\r\n\r\n  when 'katex'\r\n    if (theme.math.per_page && (['post','page'].includes(globalPageType))) || page.katex\r\n      include ./katex.pug\r\n\r\nif theme.mermaid.enable\r\n  include ./mermaid.pug\r\n\r\nif theme.chartjs.enable\r\n  include ./chartjs.pug"
  },
  {
    "path": "layout/includes/third-party/math/katex.pug",
    "content": "script.\r\n  (async () => {\r\n    const showKatex = () => {\r\n      document.querySelectorAll('#article-container .katex').forEach(el => el.classList.add('katex-show'))\r\n    }\r\n\r\n    if (!window.katex_js_css) {\r\n      window.katex_js_css = true\r\n      await btf.getCSS('!{url_for(theme.asset.katex)}')\r\n      if (!{theme.math.katex.copy_tex}) {\r\n        await btf.getScript('!{url_for(theme.asset.katex_copytex)}')\r\n      }\r\n    }\r\n\r\n    showKatex()\r\n  })()"
  },
  {
    "path": "layout/includes/third-party/math/mathjax.pug",
    "content": "//- Mathjax 4/5\r\n- const { tags, enableMenu } = theme.math.mathjax\r\nscript.\r\n  (() => {\r\n    const loadMathjax = () => {\r\n      if (!window.MathJax) {\r\n        window.MathJax = {\r\n          loader: {\r\n            load: [\r\n              // Four font extension packages (optional)\r\n              //- '[tex]/bbm',\r\n              //- '[tex]/bboldx',\r\n              //- '[tex]/dsfont',\r\n              '[tex]/mhchem'\r\n            ],\r\n            paths: {\r\n              'mathjax-newcm': '[mathjax]/../@mathjax/mathjax-newcm-font',\r\n\r\n              //- // Four font extension packages (optional)\r\n              //- 'mathjax-bbm-extension': '[mathjax]/../@mathjax/mathjax-bbm-font-extension',\r\n              //- 'mathjax-bboldx-extension': '[mathjax]/../@mathjax/mathjax-bboldx-font-extension',\r\n              //- 'mathjax-dsfont-extension': '[mathjax]/../@mathjax/mathjax-dsfont-font-extension',\r\n              'mathjax-mhchem-extension': '[mathjax]/../@mathjax/mathjax-mhchem-font-extension'\r\n            }\r\n          },\r\n          output: {\r\n            font: 'mathjax-newcm',\r\n          },\r\n          tex: {\r\n            inlineMath: [['$', '$'], ['\\\\(', '\\\\)']],\r\n            tags: '!{tags}',\r\n            packages: {\r\n              '[+]': [\r\n                'mhchem'\r\n              ]\r\n            }\r\n          },\r\n          chtml: {\r\n            scale: 1.1\r\n          },\r\n          options: {\r\n            enableMenu: !{enableMenu},\r\n            menuOptions: {\r\n              settings: {\r\n                enrich: false  // Turn off Braille and voice narration text automatic generation\r\n              }\r\n            },\r\n            renderActions: {\r\n              findScript: [10, doc => {\r\n                for (const node of document.querySelectorAll('script[type^=\"math/tex\"]')) {\r\n                  const display = !!node.type.match(/; *mode=display/)\r\n                  const math = new doc.options.MathItem(node.textContent, doc.inputJax[0], display)\r\n                  const text = document.createTextNode('')\r\n                  node.parentNode.replaceChild(text, node)\r\n                  math.start = {node: text, delim: '', n: 0}\r\n                  math.end = {node: text, delim: '', n: 0}\r\n                  doc.math.push(math)\r\n                }\r\n              }, '']\r\n            }\r\n          }\r\n        }\r\n\r\n        const script = document.createElement('script')\r\n        script.src = '!{url_for(theme.asset.mathjax)}'\r\n        script.id = 'MathJax-script'\r\n        script.async = true\r\n        document.head.appendChild(script)\r\n      } else {\r\n        MathJax.startup.document.state(0)\r\n        MathJax.texReset()\r\n        MathJax.typesetPromise()\r\n      }\r\n    }\r\n\r\n    btf.addGlobalFn('encrypt', loadMathjax, 'mathjax')\r\n    window.pjax ? loadMathjax() : window.addEventListener('load', loadMathjax)\r\n  })()"
  },
  {
    "path": "layout/includes/third-party/math/mermaid.pug",
    "content": "script.\r\n  (() => {\r\n    const parseViewBox = viewBox => {\r\n      if (!viewBox) return null\r\n      const parts = viewBox.trim().split(/[\\s,]+/).map(n => Number(n))\r\n      if (parts.length !== 4 || parts.some(n => Number.isNaN(n))) return null\r\n      return parts\r\n    }\r\n\r\n    const getSvgViewBox = svg => {\r\n      const attr = parseViewBox(svg.getAttribute('viewBox'))\r\n      if (attr) return attr\r\n\r\n      // Fallback: use bbox to build a viewBox\r\n      try {\r\n        const bbox = svg.getBBox()\r\n        if (bbox && bbox.width && bbox.height) return [bbox.x, bbox.y, bbox.width, bbox.height]\r\n      } catch (e) {\r\n        // getBBox may fail on some edge cases; ignore\r\n      }\r\n\r\n      const w = Number(svg.getAttribute('width')) || 0\r\n      const h = Number(svg.getAttribute('height')) || 0\r\n      if (w > 0 && h > 0) return [0, 0, w, h]\r\n      return [0, 0, 100, 100]\r\n    }\r\n\r\n    const setSvgViewBox = (svg, vb) => {\r\n      svg.setAttribute('viewBox', `${vb[0]} ${vb[1]} ${vb[2]} ${vb[3]}`)\r\n    }\r\n\r\n    const clamp = (v, min, max) => Math.max(min, Math.min(max, v))\r\n\r\n    const openSvgInNewTab = ({ source, initViewBox }) => {\r\n      const getClonedSvg = () => {\r\n        if (typeof source === 'string') {\r\n          const template = document.createElement('template')\r\n          template.innerHTML = source.trim()\r\n          const svg = template.content.querySelector('svg')\r\n          return svg ? svg.cloneNode(true) : null\r\n        }\r\n        if (source && typeof source.cloneNode === 'function') {\r\n          return source.cloneNode(true)\r\n        }\r\n        return null\r\n      }\r\n\r\n      const clone = getClonedSvg()\r\n      if (!clone) return\r\n      if (initViewBox && initViewBox.length === 4) {\r\n        clone.setAttribute('viewBox', initViewBox.join(' '))\r\n      }\r\n      if (!clone.getAttribute('xmlns')) clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg')\r\n      if (!clone.getAttribute('xmlns:xlink') && clone.outerHTML.includes('xlink:')) {\r\n        clone.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink')\r\n      }\r\n      // inject background to match current theme\r\n      const isDark = document.documentElement.getAttribute('data-theme') === 'dark'\r\n      const bg = getComputedStyle(document.body).backgroundColor || (isDark ? '#1e1e1e' : '#ffffff')\r\n      if (!clone.style.background) clone.style.background = bg\r\n\r\n      const serializer = new XMLSerializer()\r\n      const svgSource = serializer.serializeToString(clone)\r\n      const htmlSource = `<!doctype html><html><head><meta charset=\"utf-8\" />\r\n        <style>\r\n          html, body { width: 100%; height: 100%; margin: 0; display: flex; align-items: center; justify-content: center; background: ${bg}; }\r\n          svg { max-width: 100%; max-height: 100%; height: auto; width: auto; }\r\n        </style>\r\n        </head><body>${svgSource}</body></html>`\r\n      const blob = new Blob([htmlSource], { type: 'text/html;charset=utf-8' })\r\n      const url = URL.createObjectURL(blob)\r\n      window.open(url, '_blank', 'noopener')\r\n      setTimeout(() => URL.revokeObjectURL(url), 30000)\r\n    }\r\n\r\n    const attachMermaidViewerButton = wrap => {\r\n      let btn = wrap.querySelector('.mermaid-open-btn')\r\n      if (!btn) {\r\n        btn = document.createElement('button')\r\n        btn.type = 'button'\r\n        btn.className = 'mermaid-open-btn'\r\n        wrap.appendChild(btn)\r\n      }\r\n\r\n      btn.innerHTML = '<i class=\"fa fa-search fa-fw\" aria-hidden=\"true\"></i>'\r\n\r\n      if (!btn.__mermaidViewerBound) {\r\n        btn.addEventListener('click', e => {\r\n          e.preventDefault()\r\n          e.stopPropagation()\r\n          const svg = wrap.__mermaidOriginalSvg || wrap.querySelector('svg')\r\n          if (!svg) return\r\n          const initViewBox = wrap.__mermaidInitViewBox\r\n          if (typeof svg === 'string') {\r\n            openSvgInNewTab({ source: svg, initViewBox })\r\n            return\r\n          }\r\n          openSvgInNewTab({ source: svg, initViewBox })\r\n        })\r\n        btn.__mermaidViewerBound = true\r\n      }\r\n    }\r\n\r\n    // Zoom around a point (px, py) in the SVG viewport (in viewBox coordinates)\r\n    const zoomAtPoint = (vb, factor, px, py) => {\r\n      const w = vb[2] * factor\r\n      const h = vb[3] * factor\r\n      const nx = px - (px - vb[0]) * factor\r\n      const ny = py - (py - vb[1]) * factor\r\n      return [nx, ny, w, h]\r\n    }\r\n\r\n    const initMermaidGestures = wrap => {\r\n      const svg = wrap.querySelector('svg')\r\n      if (!svg) return\r\n\r\n      // Ensure viewBox exists so gestures always work\r\n      const initVb = getSvgViewBox(svg)\r\n      wrap.__mermaidInitViewBox = initVb\r\n      wrap.__mermaidCurViewBox = initVb.slice()\r\n      setSvgViewBox(svg, initVb)\r\n\r\n      // Avoid binding multiple times on themeChange/pjax\r\n      if (wrap.__mermaidGestureBound) return\r\n      wrap.__mermaidGestureBound = true\r\n\r\n      // Helper: map client (viewport) coordinate -> viewBox coordinate\r\n      const clientToViewBox = (clientX, clientY) => {\r\n        const rect = svg.getBoundingClientRect()\r\n        const vb = wrap.__mermaidCurViewBox || getSvgViewBox(svg)\r\n        const x = vb[0] + (clientX - rect.left) * (vb[2] / rect.width)\r\n        const y = vb[1] + (clientY - rect.top) * (vb[3] / rect.height)\r\n        return { x, y, rect, vb }\r\n      }\r\n\r\n      const state = {\r\n        pointers: new Map(),\r\n        startVb: null,\r\n        startDist: 0,\r\n        startCenter: null\r\n      }\r\n\r\n      const clampVb = vb => {\r\n        const init = wrap.__mermaidInitViewBox || vb\r\n        const minW = init[2] * 0.1\r\n        const maxW = init[2] * 10\r\n        const minH = init[3] * 0.1\r\n        const maxH = init[3] * 10\r\n        vb[2] = clamp(vb[2], minW, maxW)\r\n        vb[3] = clamp(vb[3], minH, maxH)\r\n        return vb\r\n      }\r\n\r\n      const setCurVb = vb => {\r\n        vb = clampVb(vb)\r\n        wrap.__mermaidCurViewBox = vb\r\n        setSvgViewBox(svg, vb)\r\n      }\r\n\r\n      const onPointerDown = e => {\r\n        // Allow only primary button for mouse\r\n        if (e.pointerType === 'mouse' && e.button !== 0) return\r\n        svg.setPointerCapture(e.pointerId)\r\n        state.pointers.set(e.pointerId, { x: e.clientX, y: e.clientY })\r\n\r\n        if (state.pointers.size === 1) {\r\n          state.startVb = (wrap.__mermaidCurViewBox || getSvgViewBox(svg)).slice()\r\n        } else if (state.pointers.size === 2) {\r\n          const pts = [...state.pointers.values()]\r\n          const dx = pts[0].x - pts[1].x\r\n          const dy = pts[0].y - pts[1].y\r\n          state.startDist = Math.hypot(dx, dy)\r\n          state.startVb = (wrap.__mermaidCurViewBox || getSvgViewBox(svg)).slice()\r\n          state.startCenter = { x: (pts[0].x + pts[1].x) / 2, y: (pts[0].y + pts[1].y) / 2 }\r\n        }\r\n      }\r\n\r\n      const onPointerMove = e => {\r\n        if (!state.pointers.has(e.pointerId)) return\r\n        state.pointers.set(e.pointerId, { x: e.clientX, y: e.clientY })\r\n\r\n        // Pan with 1 pointer\r\n        if (state.pointers.size === 1 && state.startVb) {\r\n          const p = [...state.pointers.values()][0]\r\n          const prev = { x: e.clientX - e.movementX, y: e.clientY - e.movementY }\r\n          // movementX/Y unreliable on touch, compute from stored last position\r\n          const last = wrap.__mermaidLastSinglePointer || p\r\n          const dxClient = p.x - last.x\r\n          const dyClient = p.y - last.y\r\n          wrap.__mermaidLastSinglePointer = p\r\n\r\n          const { rect } = clientToViewBox(p.x, p.y)\r\n          const vb = (wrap.__mermaidCurViewBox || getSvgViewBox(svg)).slice()\r\n          const dx = dxClient * (vb[2] / rect.width)\r\n          const dy = dyClient * (vb[3] / rect.height)\r\n          setCurVb([vb[0] - dx, vb[1] - dy, vb[2], vb[3]])\r\n          return\r\n        }\r\n\r\n        // Pinch zoom with 2 pointers\r\n        if (state.pointers.size === 2 && state.startVb && state.startDist > 0) {\r\n          const pts = [...state.pointers.values()]\r\n          const dx = pts[0].x - pts[1].x\r\n          const dy = pts[0].y - pts[1].y\r\n          const dist = Math.hypot(dx, dy)\r\n          if (!dist) return\r\n          const factor = state.startDist / dist // dist bigger => zoom in (viewBox smaller)\r\n\r\n          const cx = (pts[0].x + pts[1].x) / 2\r\n          const cy = (pts[0].y + pts[1].y) / 2\r\n          const centerClient = { x: cx, y: cy }\r\n\r\n          const pxy = clientToViewBox(centerClient.x, centerClient.y)\r\n          const cpx = pxy.x\r\n          const cpy = pxy.y\r\n\r\n          const vb = zoomAtPoint(state.startVb, factor, cpx, cpy)\r\n          setCurVb(vb)\r\n        }\r\n      }\r\n\r\n      const onPointerUpOrCancel = e => {\r\n        state.pointers.delete(e.pointerId)\r\n        if (state.pointers.size === 0) {\r\n          state.startVb = null\r\n          state.startDist = 0\r\n          state.startCenter = null\r\n          wrap.__mermaidLastSinglePointer = null\r\n        } else if (state.pointers.size === 1) {\r\n          // reset single pointer baseline to avoid jump\r\n          wrap.__mermaidLastSinglePointer = [...state.pointers.values()][0]\r\n        }\r\n      }\r\n\r\n      // Wheel zoom (mouse/trackpad)\r\n      const onWheel = e => {\r\n        // ctrlKey on mac trackpad pinch; we treat both as zoom\r\n        e.preventDefault()\r\n        const delta = e.deltaY\r\n        const zoomFactor = delta > 0 ? 1.1 : 0.9\r\n        const { x, y } = clientToViewBox(e.clientX, e.clientY)\r\n        const vb = (wrap.__mermaidCurViewBox || getSvgViewBox(svg)).slice()\r\n        setCurVb(zoomAtPoint(vb, zoomFactor, x, y))\r\n      }\r\n\r\n      const onDblClick = () => {\r\n        const init = wrap.__mermaidInitViewBox\r\n        if (!init) return\r\n        wrap.__mermaidCurViewBox = init.slice()\r\n        setSvgViewBox(svg, init)\r\n      }\r\n\r\n      svg.addEventListener('pointerdown', onPointerDown)\r\n      svg.addEventListener('pointermove', onPointerMove)\r\n      svg.addEventListener('pointerup', onPointerUpOrCancel)\r\n      svg.addEventListener('pointercancel', onPointerUpOrCancel)\r\n      svg.addEventListener('wheel', onWheel, { passive: false })\r\n      svg.addEventListener('dblclick', onDblClick)\r\n    }\r\n\r\n    const runMermaid = ele => {\r\n      window.loadMermaid = true\r\n      const theme = document.documentElement.getAttribute('data-theme') === 'dark' ? '!{theme.mermaid.theme.dark}' : '!{theme.mermaid.theme.light}'\r\n\r\n      ele.forEach((item, index) => {\r\n        const mermaidSrc = item.firstElementChild\r\n\r\n        // Clear old render (themeChange/pjax will rerun)\r\n        const oldSvg = item.querySelector('svg')\r\n        if (oldSvg) oldSvg.remove()\r\n        item.__mermaidGestureBound = false\r\n\r\n        const config = mermaidSrc.dataset.config ? JSON.parse(mermaidSrc.dataset.config) : {}\r\n        if (!config.theme) {\r\n          config.theme = theme\r\n        }\r\n        const mermaidThemeConfig = `%%{init: ${JSON.stringify(config)}}%%\\n`\r\n        const mermaidID = `mermaid-${index}`\r\n        const mermaidDefinition = mermaidThemeConfig + mermaidSrc.textContent\r\n\r\n        const renderFn = mermaid.render(mermaidID, mermaidDefinition)\r\n        const renderMermaid = svg => {\r\n          mermaidSrc.insertAdjacentHTML('afterend', svg)\r\n          if (!{theme.mermaid.zoom_pan}) initMermaidGestures(item)\r\n          item.__mermaidOriginalSvg = svg\r\n          if (!{theme.mermaid.open_in_new_tab}) attachMermaidViewerButton(item)\r\n        }\r\n\r\n\r\n        // mermaid v9 and v10 compatibility\r\n        typeof renderFn === 'string' ? renderMermaid(renderFn) : renderFn.then(({ svg }) => renderMermaid(svg))\r\n      })\r\n    }\r\n\r\n    const codeToMermaid = () => {\r\n      const codeMermaidEle = document.querySelectorAll('pre > code.mermaid')\r\n      if (codeMermaidEle.length === 0) return\r\n\r\n      codeMermaidEle.forEach(ele => {\r\n        const preEle = document.createElement('pre')\r\n        preEle.className = 'mermaid-src'\r\n        preEle.hidden = true\r\n        preEle.textContent = ele.textContent\r\n        const newEle = document.createElement('div')\r\n        newEle.className = 'mermaid-wrap'\r\n        newEle.appendChild(preEle)\r\n        ele.parentNode.replaceWith(newEle)\r\n      })\r\n    }\r\n\r\n    const loadMermaid = () => {\r\n      if (!{theme.mermaid.code_write}) codeToMermaid()\r\n      const $mermaid = document.querySelectorAll('#article-container .mermaid-wrap')\r\n      if ($mermaid.length === 0) return\r\n\r\n      const runMermaidFn = () => runMermaid($mermaid)\r\n      btf.addGlobalFn('themeChange', runMermaidFn, 'mermaid')\r\n      window.loadMermaid ? runMermaidFn() : btf.getScript('!{url_for(theme.asset.mermaid)}').then(runMermaidFn)\r\n    }\r\n\r\n    btf.addGlobalFn('encrypt', loadMermaid, 'mermaid')\r\n    window.pjax ? loadMermaid() : document.addEventListener('DOMContentLoaded', loadMermaid)\r\n  })()\r\n"
  },
  {
    "path": "layout/includes/third-party/newest-comments/artalk.pug",
    "content": "- const { server, site, option } = theme.artalk\r\n- const avatarCdn = (option !== null && option.gravatar && option.gravatar.mirror) || ''\r\n- const avatarDefault = (option !== null && option.gravatar && (option.gravatar.params || option.gravatar.default)) || ''\r\n\r\n!= partial(\"includes/third-party/newest-comments/common.pug\", {}, { cache: true })\r\n\r\nscript.\r\n  window.addEventListener('load', () => {\r\n    const keyName = 'artalk-newest-comments'\r\n    const { changeContent, generateHtml, run } = window.newestComments\r\n\r\n    const getAvatarValue = async () => {\r\n      const predefinedAvatarCdn = '!{avatarCdn}'\r\n      const predefinedAvatarDefault = '!{avatarDefault}'\r\n\r\n      const avatarDefaultFormat = e => e.startsWith('d=') ? e : `d=${e}`\r\n\r\n      if (predefinedAvatarCdn && predefinedAvatarDefault) {\r\n        return { avatarCdn: predefinedAvatarCdn, avatarDefault: avatarDefaultFormat(predefinedAvatarDefault) }\r\n      }\r\n\r\n      try {\r\n        const res = await fetch('!{server}/api/v2/conf')\r\n        const result = await res.json()\r\n        const { mirror, params, default: defaults } = result.frontend_conf.gravatar\r\n        const avatarCdn = predefinedAvatarCdn || mirror\r\n        let avatarDefault = avatarDefaultFormat(predefinedAvatarDefault || params || defaults)\r\n        return { avatarCdn, avatarDefault}\r\n      } catch (e) {\r\n        console.error(e)\r\n        return { avatarCdn: predefinedAvatarCdn, avatarDefault: avatarDefaultFormat(predefinedAvatarDefault) }\r\n      }\r\n    }\r\n\r\n    const searchParams = new URLSearchParams({\r\n      'site_name': '!{site}',\r\n      'limit': '!{newestCommentsLimit * 2}', // Fetch more comments to filter pending comments\r\n    })\r\n\r\n    const getComment = async (ele) => {\r\n      try {\r\n        const res = await fetch(`!{server}/api/v2/stats/latest_comments?${searchParams}`)\r\n        const result = await res.json()\r\n        const { avatarCdn, avatarDefault } = await getAvatarValue()\r\n        const artalk = result.data\r\n          .filter(e => !e.is_pending) // Filter pending comments\r\n          .slice(0, !{newestCommentsLimit}) // Limit the number of comments\r\n          .map(e => {\r\n            const avatar = avatarCdn && e.email_encrypted ? `${avatarCdn}${e.email_encrypted}?${avatarDefault}` : ''\r\n            return {\r\n              'avatar': avatar,\r\n              'content': changeContent(e.content_marked),\r\n              'nick': e.nick,\r\n              'url': e.page_url,\r\n              'date': e.date,\r\n            }\r\n          })\r\n        btf.saveToLocal.set(keyName, JSON.stringify(artalk), !{theme.aside.card_newest_comments.storage}/(60*24))\r\n        generateHtml(artalk, ele)\r\n      } catch (e) {\r\n        console.log(e)\r\n        ele.textContent= \"!{_p('aside.card_newest_comments.error')}\"\r\n      }\r\n    }\r\n\r\n    run(keyName, getComment)\r\n  })"
  },
  {
    "path": "layout/includes/third-party/newest-comments/common.pug",
    "content": "script.\r\n  window.newestComments = {\r\n    changeContent: content => {\r\n      if (content === '') return content\r\n\r\n      content = content.replace(/<img.*?src=\"(.*?)\"?[^\\>]+>/ig, '[!{_p(\"aside.card_newest_comments.image\")}]') // replace image link\r\n      content = content.replace(/<a[^>]+?href=[\"']?([^\"']+)[\"']?[^>]*>([^<]+)<\\/a>/gi, '[!{_p(\"aside.card_newest_comments.link\")}]') // replace url\r\n      content = content.replace(/<pre><code>.*?<\\/pre>/gi, '[!{_p(\"aside.card_newest_comments.code\")}]') // replace code\r\n      content = content.replace(/<code>.*?<\\/code>/gi, '[!{_p(\"aside.card_newest_comments.code\")}]') // replace code\r\n      content = content.replace(/<[^>]+>/g, \"\") // remove html tag\r\n\r\n      if (content.length > 150) {\r\n        content = content.substring(0, 150) + '...'\r\n      }\r\n      return content\r\n    },\r\n\r\n    generateHtml: (array, ele) => {\r\n      let result = ''\r\n\r\n      if (array.length) {\r\n        for (let i = 0; i < array.length; i++) {\r\n          result += '<div class=\"aside-list-item\">'\r\n\r\n          if (!{theme.aside.card_newest_comments.avatar} && array[i].avatar) {\r\n            const imgAttr = '!{theme.lazyload.enable && !theme.lazyload.native ? \"data-lazy-src\" : \"src\"}'\r\n            const lazyloadNative = '!{theme.lazyload.enable && theme.lazyload.native ? \"loading=\\\"lazy\\\"\" : \"\"}'\r\n            result += `<a href=\"${array[i].url}\" class=\"thumbnail\"><img ${imgAttr}=\"${array[i].avatar}\" alt=\"${array[i].nick}\" ${lazyloadNative}></a>`\r\n          }\r\n\r\n          result += `<div class=\"content\">\r\n          <a class=\"comment\" href=\"${array[i].url}\" title=\"${array[i].content}\">${array[i].content}</a>\r\n          <div class=\"name\"><span>${array[i].nick} / </span><time datetime=\"${array[i].date}\">${btf.diffDate(array[i].date, true)}</time></div>\r\n          </div></div>`\r\n        }\r\n      } else {\r\n        result += '!{_p(\"aside.card_newest_comments.zero\")}'\r\n      }\r\n\r\n      ele.innerHTML = result\r\n      window.lazyLoadInstance && window.lazyLoadInstance.update()\r\n      window.pjax && window.pjax.refresh(ele)\r\n    },\r\n\r\n    newestCommentInit: (name, getComment) => {\r\n      const $dom = document.querySelector('#card-newest-comments .aside-list')\r\n      if ($dom) {\r\n        const data = btf.saveToLocal.get(name)\r\n        if (data) {\r\n          newestComments.generateHtml(JSON.parse(data), $dom)\r\n        } else {\r\n          getComment($dom)\r\n        }\r\n      }\r\n    },\r\n\r\n    run: (name, getComment) => {\r\n      newestComments.newestCommentInit(name, getComment)\r\n      btf.addGlobalFn('pjaxComplete', () => newestComments.newestCommentInit(name, getComment), name)\r\n    }\r\n  }"
  },
  {
    "path": "layout/includes/third-party/newest-comments/disqus-comment.pug",
    "content": "!= partial(\"includes/third-party/newest-comments/common.pug\", {}, { cache: true })\r\n\r\nscript.\r\n  window.addEventListener('load', () => {\r\n    const keyName = 'disqus-newest-comments'\r\n    const { changeContent, generateHtml, run } = window.newestComments\r\n\r\n    const getComment = ele => {\r\n      fetch('https://disqus.com/api/3.0/forums/listPosts.json?forum=!{forum}&related=thread&limit=!{newestCommentsLimit}&api_key=!{apiKey}')\r\n        .then(response => response.json())\r\n        .then(data => {\r\n          const disqusArray = data.response.map(item => {\r\n            return {\r\n              'avatar': item.author.avatar.cache,\r\n              'content': changeContent(item.message),\r\n              'nick': item.author.name,\r\n              'url': item.url,\r\n              'date': item.createdAt\r\n            }\r\n          })\r\n\r\n          btf.saveToLocal.set(keyName, JSON.stringify(disqusArray), !{theme.aside.card_newest_comments.storage}/(60*24))\r\n          generateHtml(disqusArray, ele)\r\n        }).catch(e => {\r\n          console.error(e)\r\n          ele.textContent= \"!{_p('aside.card_newest_comments.error')}\"\r\n        })\r\n    }\r\n\r\n    run(keyName, getComment)\r\n  })\r\n\r\n\r\n\r\n"
  },
  {
    "path": "layout/includes/third-party/newest-comments/github-issues.pug",
    "content": "!= partial(\"includes/third-party/newest-comments/common.pug\", {}, { cache: true })\r\n\r\nscript.\r\n  window.addEventListener('load', () => {\r\n    const keyName = 'github-newest-comments'\r\n    const { changeContent, generateHtml, run } = window.newestComments\r\n\r\n    const findTrueUrl = (array, ele) => {\r\n      Promise.all(array.map(item =>\r\n        fetch(item.url).then(resp => resp.json()).then(data => {\r\n          let urlArray = data.body ? data.body.match(/(https?:\\/\\/)?([\\da-z\\.-]+)\\.([a-z\\.]{2,6})([\\/\\w \\.-]*)*\\/?/ig) : []\r\n          if (!Array.isArray(urlArray) || urlArray.length === 0) {\r\n            urlArray = [`${data.html_url}`]\r\n          }\r\n          if (data.user.login === 'utterances-bot') {\r\n            return urlArray.pop()\r\n          } else {\r\n            return urlArray.shift()\r\n          }\r\n        })\r\n      )).then(res => {\r\n          array = array.map((i,index)=> {\r\n            return {\r\n              ...i,\r\n              url: res[index]\r\n            }\r\n          })\r\n\r\n          btf.saveToLocal.set(keyName, JSON.stringify(array), !{theme.aside.card_newest_comments.storage}/(60*24))\r\n          generateHtml(array, ele)\r\n      });\r\n    }\r\n\r\n    const getComment = ele => {\r\n      fetch('https://api.github.com/repos/!{userRepo}/issues/comments?sort=updated&direction=desc&per_page=!{newestCommentsLimit}&page=1',{\r\n        \"headers\": {\r\n          Accept: 'application/vnd.github.v3.html+json'\r\n        }\r\n      })\r\n        .then(response => response.json())\r\n        .then(data => {\r\n          const githubArray = data.map(item => {\r\n            return {\r\n              'avatar': item.user.avatar_url,\r\n              'content': changeContent(item.body_html || item.body),\r\n              'nick': item.user.login,\r\n              'url': item.issue_url,\r\n              'date': item.updated_at\r\n            }\r\n          })\r\n          findTrueUrl(githubArray, ele)\r\n        }).catch(e => {\r\n          console.error(e)\r\n          ele.textContent= \"!{_p('aside.card_newest_comments.error')}\"\r\n        })\r\n    }\r\n    run(keyName, getComment)\r\n  })\r\n\r\n\r\n\r\n\r\n"
  },
  {
    "path": "layout/includes/third-party/newest-comments/index.pug",
    "content": "- let { use } = theme.comments\r\n\r\nif use\r\n  -\r\n    let forum,apiKey,userRepo\r\n    let { limit:newestCommentsLimit } = theme.aside.card_newest_comments\r\n    if (newestCommentsLimit > 10 || newestCommentsLimit < 1) newestCommentsLimit = 6\r\n\r\n  case use[0]\r\n    when 'Valine'\r\n      include ./valine.pug\r\n    when 'Waline'\r\n      include ./waline.pug\r\n    when 'Twikoo'\r\n      include ./twikoo-comment.pug\r\n    when 'Disqus'\r\n      - forum = theme.disqus.shortname\r\n      - apiKey = theme.disqus.apikey\r\n      include ./disqus-comment.pug\r\n    when 'Disqusjs'\r\n      - forum = theme.disqusjs.shortname\r\n      - apiKey = theme.disqusjs.apikey\r\n      include ./disqus-comment.pug\r\n    when 'Gitalk'\r\n      - let { repo,owner } = theme.gitalk\r\n      - userRepo = owner + '/' + repo\r\n      include ./github-issues.pug\r\n    when 'Utterances'\r\n      - userRepo = theme.utterances.repo\r\n      include ./github-issues.pug\r\n    when 'Remark42'\r\n      include ./remark42.pug\r\n    when 'Artalk'\r\n      include ./artalk.pug"
  },
  {
    "path": "layout/includes/third-party/newest-comments/remark42.pug",
    "content": "- const { host, siteId } = theme.remark42\r\n!= partial(\"includes/third-party/newest-comments/common.pug\", {}, { cache: true })\r\n\r\nscript.\r\n  window.addEventListener('load', () => {\r\n    const keyName = 'remark42-newest-comments'\r\n    const { changeContent, generateHtml, run } = window.newestComments\r\n\r\n    const getComment = ele => {\r\n      fetch('!{host}/api/v1/last/!{newestCommentsLimit}?site=!{siteId}')\r\n        .then(response => response.json())\r\n        .then(data => {\r\n          const remark42 = data.map(e => {\r\n            return {\r\n              'avatar': e.user.picture,\r\n              'content': changeContent(e.text),\r\n              'nick': e.user.name,\r\n              'url': e.locator.url,\r\n              'date': e.time,\r\n            }\r\n          })\r\n          btf.saveToLocal.set(keyName, JSON.stringify(remark42), !{theme.aside.card_newest_comments.storage}/(60*24))\r\n          generateHtml(remark42, ele)\r\n        }).catch(e => {\r\n          console.error(e)\r\n          ele.textContent= \"!{_p('aside.card_newest_comments.error')}\"\r\n        })\r\n    }\r\n\r\n    run(keyName, getComment)\r\n  })\r\n"
  },
  {
    "path": "layout/includes/third-party/newest-comments/twikoo-comment.pug",
    "content": "!= partial(\"includes/third-party/newest-comments/common.pug\", {}, { cache: true })\r\n\r\nscript.\r\n  window.addEventListener('load', () => {\r\n    const keyName = 'twikoo-newest-comments'\r\n    const { changeContent, generateHtml, run } = window.newestComments\r\n\r\n    const getComment = ele => {\r\n      const runTwikoo = () => {\r\n        twikoo.getRecentComments({\r\n          envId: '!{theme.twikoo.envId}',\r\n          region: '!{theme.twikoo.region}',\r\n          pageSize: !{newestCommentsLimit},\r\n          includeReply: true\r\n        }).then(res => {\r\n          const twikooArray = res.map(e => {\r\n            return {\r\n              'content': changeContent(e.comment),\r\n              'avatar': e.avatar,\r\n              'nick': e.nick,\r\n              'url': e.url + '#' + e.id,\r\n              'date': new Date(e.created).toISOString()\r\n            }\r\n          })\r\n\r\n          btf.saveToLocal.set(keyName, JSON.stringify(twikooArray), !{theme.aside.card_newest_comments.storage}/(60*24))\r\n          generateHtml(twikooArray, ele)\r\n        }).catch(err => {\r\n          console.error(err)\r\n          ele.textContent= \"!{_p('aside.card_newest_comments.error')}\"\r\n        })\r\n      }\r\n\r\n      if (typeof twikoo === 'object') {\r\n        runTwikoo()\r\n      } else {\r\n        btf.getScript('!{url_for(theme.asset.twikoo)}').then(runTwikoo)\r\n      }\r\n    }\r\n\r\n    run(keyName, getComment)\r\n  })\r\n\r\n\r\n\r\n"
  },
  {
    "path": "layout/includes/third-party/newest-comments/valine.pug",
    "content": "- let default_avatar = theme.valine.avatar\r\n\r\nscript(src=url_for(theme.asset.blueimp_md5))\r\n!= partial(\"includes/third-party/newest-comments/common.pug\", {}, { cache: true })\r\n\r\nscript.\r\n  window.addEventListener('load', () => {\r\n    const keyName = 'valine-newest-comments'\r\n    const { changeContent, generateHtml, run } = window.newestComments\r\n\r\n    const getIcon = (icon, mail) => {\r\n      if (icon) return icon\r\n      let defaultIcon = '!{ default_avatar ? `?d=${default_avatar}` : ''}'\r\n      let iconUrl = `https://gravatar.loli.net/avatar/${md5(mail.toLowerCase()) + defaultIcon}`\r\n      return iconUrl\r\n    }\r\n\r\n    const getComment = ele => {\r\n      const serverURL = '!{theme.valine.serverURLs || `https://${theme.valine.appId.substring(0,8)}.api.lncldglobal.com` }'\r\n\r\n      var settings = {\r\n        \"method\": \"GET\",\r\n        \"headers\": {\r\n          \"X-LC-Id\": '!{theme.valine.appId}',\r\n          \"X-LC-Key\": '!{theme.valine.appKey}',\r\n          \"Content-Type\": \"application/json\"\r\n        },\r\n      }\r\n\r\n      fetch(`${serverURL}/1.1/classes/Comment?limit=!{newestCommentsLimit}&order=-createdAt`,settings)\r\n        .then(response => response.json())\r\n        .then(data => {\r\n          const valineArray = data.results.map(e => {\r\n            return {\r\n              'avatar': getIcon(e.QQAvatar, e.mail),\r\n              'content': changeContent(e.comment),\r\n              'nick': e.nick,\r\n              'url': e.url + '#' + e.objectId,\r\n              'date': e.updatedAt,\r\n            }\r\n          })\r\n          btf.saveToLocal.set(keyName, JSON.stringify(valineArray), !{theme.aside.card_newest_comments.storage}/(60*24))\r\n          generateHtml(valineArray, ele)\r\n        }).catch(e => {\r\n          console.error(e)\r\n          ele.textContent= \"!{_p('aside.card_newest_comments.error')}\"\r\n        })\r\n    }\r\n\r\n    run(keyName, getComment)\r\n  })\r\n"
  },
  {
    "path": "layout/includes/third-party/newest-comments/waline.pug",
    "content": "- const serverURL = theme.waline.serverURL.replace(/\\/$/, '')\r\n\r\n!= partial(\"includes/third-party/newest-comments/common.pug\", {}, { cache: true })\r\n\r\nscript.\r\n  window.addEventListener('load', () => {\r\n    const keyName = 'waline-newest-comments'\r\n    const { changeContent, generateHtml, run } = window.newestComments\r\n\r\n    const getComment = async (ele) => {\r\n      try {\r\n        const res = await fetch('!{serverURL}/api/comment?type=recent&count=!{newestCommentsLimit}')\r\n        const result = await res.json()\r\n        const walineArray = result.data.map(e => {\r\n          return {\r\n            'content': changeContent(e.comment),\r\n            'avatar': e.avatar,\r\n            'nick': e.nick,\r\n            'url': e.url + '#' + e.objectId,\r\n            'date': e.time || e.insertedAt\r\n          }\r\n        })\r\n        btf.saveToLocal.set(keyName, JSON.stringify(walineArray), !{theme.aside.card_newest_comments.storage}/(60*24))\r\n        generateHtml(walineArray, ele)\r\n      } catch (err) {\r\n        console.error(err)\r\n        ele.textContent= \"!{_p('aside.card_newest_comments.error')}\"\r\n      }\r\n    }\r\n\r\n    run(keyName, getComment)\r\n  })\r\n"
  },
  {
    "path": "layout/includes/third-party/pjax.pug",
    "content": "- var pjaxExclude = 'a:not([target=\"_blank\"])'\r\nif theme.pjax.exclude\r\n  each val in theme.pjax.exclude\r\n    - pjaxExclude += `:not([href=\"${val}\"])`\r\n\r\n- let pjaxSelectors = ['head > title', '#config-diff', '#body-wrap', '#rightside-config-hide', '#rightside-config-show', '.js-pjax']\r\n\r\n- let choose = theme.comments.use\r\nif choose\r\n  if choose.includes('Livere') || choose.includes('Utterances') || choose.includes('Giscus')\r\n    - pjaxSelectors.unshift('link[rel=\"canonical\"]')\r\n    if theme.Open_Graph_meta.enable\r\n      - pjaxSelectors.unshift('meta[property=\"og:image\"]', 'meta[property=\"og:title\"]', 'meta[property=\"og:url\"]', 'meta[property=\"og:description\"]')\r\n    else\r\n      - pjaxSelectors.unshift('meta[name=\"description\"]')\r\n\r\nscript(src=url_for(theme.asset.pjax) defer)\r\nscript.\r\n  document.addEventListener('DOMContentLoaded', () => {\r\n    const pjaxSelectors = !{JSON.stringify(pjaxSelectors)}\r\n\r\n    window.pjax = new Pjax({\r\n      elements: '!{pjaxExclude}',\r\n      selectors: pjaxSelectors,\r\n      cacheBust: false,\r\n      analytics: !{theme.google_analytics ? true : false},\r\n      scrollRestoration: false\r\n    })\r\n\r\n    const triggerPjaxFn = (val) => {\r\n      if (!val) return\r\n      Object.values(val).forEach(fn => {\r\n        try {\r\n          fn()\r\n        } catch (err) {\r\n          console.debug('Pjax callback failed:', err)\r\n        }\r\n      })\r\n    }\r\n\r\n    document.addEventListener('pjax:send', () => {\r\n      // removeEventListener\r\n      btf.removeGlobalFnEvent('pjaxSendOnce')\r\n      btf.removeGlobalFnEvent('themeChange')\r\n\r\n      // reset readmode\r\n      const $bodyClassList = document.body.classList\r\n      if ($bodyClassList.contains('read-mode')) $bodyClassList.remove('read-mode')\r\n\r\n      triggerPjaxFn(window.globalFn.pjaxSend)\r\n    })\r\n\r\n    document.addEventListener('pjax:complete', () => {\r\n      btf.removeGlobalFnEvent('pjaxCompleteOnce')\r\n      document.querySelectorAll('script[data-pjax]').forEach(item => {\r\n        const newScript = document.createElement('script')\r\n        const content = item.text || item.textContent || item.innerHTML || \"\"\r\n        Array.from(item.attributes).forEach(attr => newScript.setAttribute(attr.name, attr.value))\r\n        newScript.appendChild(document.createTextNode(content))\r\n        item.parentNode.replaceChild(newScript, item)\r\n      })\r\n\r\n      triggerPjaxFn(window.globalFn.pjaxComplete)\r\n    })\r\n\r\n    document.addEventListener('pjax:error', e => {\r\n      if (e.request.status === 404) {\r\n        !{theme.error_404 && theme.error_404.enable}\r\n          ? pjax.loadUrl('!{url_for(\"/404.html\")}')\r\n          : window.location.href = e.request.responseURL\r\n      }\r\n    })\r\n  })"
  },
  {
    "path": "layout/includes/third-party/prismjs.pug",
    "content": "- const { prismjs_js, prismjs_autoloader, prismjs_lineNumber_js } = theme.asset\r\n- const { prismjs, syntax_highlighter } = config\r\n- const { enable, preprocess, line_number } = prismjs\r\n\r\nif (syntax_highlighter === 'prismjs' || enable) && !preprocess\r\n  script.\r\n    (() => {\r\n      window.Prism = window.Prism || {}\r\n      window.Prism.manual = true\r\n\r\n      const highlightAll = () => {\r\n        window.Prism.highlightAll()\r\n      }\r\n\r\n      window.addEventListener('load', highlightAll)\r\n      btf.addGlobalFn('pjaxComplete', highlightAll, 'prismjs')\r\n      btf.addGlobalFn('encrypt', highlightAll, 'prismjs')\r\n    })()\r\n\r\n  script(src=url_for(prismjs_js) defer)\r\n  script(src=url_for(prismjs_autoloader) defer)\r\n  if (line_number)\r\n    script(src=url_for(prismjs_lineNumber_js) defer)"
  },
  {
    "path": "layout/includes/third-party/search/algolia.pug",
    "content": "#algolia-search\r\n  .search-dialog\r\n    nav.search-nav\r\n      span.search-dialog-title= _p('search.title')\r\n      i.fas.fa-spinner.fa-pulse#loading-status(hidden)\r\n      button.search-close-button\r\n        i.fas.fa-times\r\n\r\n    #algolia-search-input\r\n      .ais-SearchBox\r\n        form.ais-SearchBox-form(action=\"\" role=\"search\" novalidate=\"\")\r\n          input.ais-SearchBox-input(type=\"search\" placeholder=theme.search.placeholder || _p(\"search.input_placeholder\") autocomplete=\"off\" autocorrect=\"off\" autocapitalize=\"off\" spellcheck=\"false\" maxlength=\"512\" aria-label=\"Search\")\r\n          button.ais-SearchBox-submit(type=\"submit\" title=\"Submit the search query\" style=\"display:none;\")\r\n            svg.ais-SearchBox-submitIcon(width=\"10\" height=\"10\" viewBox=\"0 0 40 40\" aria-hidden=\"true\")\r\n              path(d=\"M26.804 29.01c-2.832 2.34-6.465 3.746-10.426 3.746C7.333 32.756 0 25.424 0 16.378 0 7.333 7.333 0 16.378 0c9.046 0 16.378 7.333 16.378 16.378 0 3.96-1.406 7.594-3.746 10.426l10.534 10.534c.607.607.61 1.59-.004 2.202-.61.61-1.597.61-2.202.004L26.804 29.01zm-10.426.627c7.323 0 13.26-5.936 13.26-13.26 0-7.32-5.937-13.257-13.26-13.257C9.056 3.12 3.12 9.056 3.12 16.378c0 7.323 5.936 13.26 13.258 13.26z\")\r\n      hr\r\n      #algolia-search-results\r\n        #algolia-hits\r\n          #algolia-hits-empty(style=\"display:none;\")\r\n          .ais-Hits(style=\"display:none;\")\r\n            ol.ais-Hits-list\r\n        #algolia-pagination.ais-Pagination(style=\"display:none;\")\r\n          ul.ais-Pagination-list\r\n        #algolia-info\r\n          span.ais-Stats-text\r\n          a.algolia-poweredBy(href=\"https://www.algolia.com/?utm_source=algoliasearch.js&utm_medium=website&utm_content=localhost&utm_campaign=poweredby\" target=\"_blank\" aria-label=\"Search by Algolia\" rel=\"noopener noreferrer\")\r\n            svg.ais-PoweredBy-logo(height=\"1.2em\" viewBox=\"0 0 572 64\" style=\"width: auto;\")\r\n              path(fill=\"#36395A\" d=\"M16 48.3c-3.4 0-6.3-.6-8.7-1.7A12.4 12.4 0 0 1 1.9 42C.6 40 0 38 0 35.4h6.5a6.7 6.7 0 0 0 3.9 6c1.4.7 3.3 1.1 5.6 1.1 2.2 0 4-.3 5.4-1a7 7 0 0 0 3-2.4 6 6 0 0 0 1-3.4c0-1.5-.6-2.8-1.9-3.7-1.3-1-3.3-1.6-5.9-1.8l-4-.4c-3.7-.3-6.6-1.4-8.8-3.4a10 10 0 0 1-3.3-7.9c0-2.4.6-4.6 1.8-6.4a12 12 0 0 1 5-4.3c2.2-1 4.7-1.6 7.5-1.6s5.5.5 7.6 1.6a12 12 0 0 1 5 4.4c1.2 1.8 1.8 4 1.8 6.7h-6.5a6.4 6.4 0 0 0-3.5-5.9c-1-.6-2.6-1-4.4-1s-3.2.3-4.4 1c-1.1.6-2 1.4-2.6 2.4-.5 1-.8 2-.8 3.1a5 5 0 0 0 1.5 3.6c1 1 2.6 1.7 4.7 1.9l4 .3c2.8.2 5.2.8 7.2 1.8 2.1 1 3.7 2.2 4.9 3.8a9.7 9.7 0 0 1 1.7 5.8c0 2.5-.7 4.7-2 6.6a13 13 0 0 1-5.6 4.4c-2.4 1-5.2 1.6-8.4 1.6Zm35.6 0c-2.6 0-4.8-.4-6.7-1.3a13 13 0 0 1-4.7-3.5 17.1 17.1 0 0 1-3.6-10.4v-1c0-2 .3-3.8 1-5.6a13 13 0 0 1 7.3-8.3 15 15 0 0 1 6.3-1.4A13.2 13.2 0 0 1 64 24.3c1 2.2 1.6 4.6 1.6 7.2V34H39.4v-4.3h21.8l-1.8 2.2c0-2-.3-3.7-.9-5.1a7.3 7.3 0 0 0-2.7-3.4c-1.2-.7-2.7-1.1-4.6-1.1s-3.4.4-4.7 1.3a8 8 0 0 0-2.9 3.6c-.6 1.5-.9 3.3-.9 5.4 0 2 .3 3.7 1 5.3a7.9 7.9 0 0 0 2.8 3.7c1.3.8 3 1.3 5 1.3s3.8-.5 5.1-1.3c1.3-1 2.1-2 2.4-3.2h6a11.8 11.8 0 0 1-7 8.7 16 16 0 0 1-6.4 1.2ZM80 48c-2.2 0-4-.3-5.7-1a8.4 8.4 0 0 1-3.7-3.3 9.7 9.7 0 0 1-1.3-5.2c0-2 .5-3.8 1.5-5.2a9 9 0 0 1 4.3-3.1c1.8-.7 4-1 6.7-1H89v4.1h-7.5c-2 0-3.4.5-4.4 1.4-1 1-1.6 2.1-1.6 3.6s.5 2.7 1.6 3.6c1 1 2.5 1.4 4.4 1.4 1.1 0 2.2-.2 3.2-.7 1-.4 1.9-1 2.6-2 .6-1 1-2.4 1-4.2l1.7 2.1c-.2 2-.7 3.8-1.5 5.2a9 9 0 0 1-3.4 3.3 12 12 0 0 1-5.3 1Zm9.5-.7v-8.8h-1v-10c0-1.8-.5-3.2-1.4-4.1-1-1-2.4-1.4-4.2-1.4a142.9 142.9 0 0 0-10.2.4v-5.6a74.8 74.8 0 0 1 8.6-.4c3 0 5.5.4 7.5 1.2s3.4 2 4.4 3.6c1 1.7 1.4 4 1.4 6.7v18.4h-5Zm12.9 0V17.8h5v12.3h-.2c0-4.2 1-7.4 2.8-9.5a11 11 0 0 1 8.3-3.1h1v5.6h-2a9 9 0 0 0-6.3 2.2c-1.5 1.5-2.2 3.6-2.2 6.4v15.6h-6.4Zm34.4 1a15 15 0 0 1-6.6-1.3c-1.9-.9-3.4-2-4.7-3.5a15.5 15.5 0 0 1-2.7-5c-.6-1.7-1-3.6-1-5.4v-1c0-2 .4-3.8 1-5.6a15 15 0 0 1 2.8-4.9c1.3-1.5 2.8-2.6 4.6-3.5a16.4 16.4 0 0 1 13.3.2c2 1 3.5 2.3 4.8 4a12 12 0 0 1 2 6H144c-.2-1.6-1-3-2.2-4.1a7.5 7.5 0 0 0-5.2-1.7 8 8 0 0 0-4.7 1.3 8 8 0 0 0-2.8 3.6 13.8 13.8 0 0 0 0 10.3c.6 1.5 1.5 2.7 2.8 3.6s2.8 1.3 4.8 1.3c1.5 0 2.7-.2 3.8-.8a7 7 0 0 0 2.6-2c.7-1 1-2 1.2-3.2h6.2a11 11 0 0 1-2 6.2 15.1 15.1 0 0 1-11.8 5.5Zm19.7-1v-40h6.4V31h-1.3c0-3 .4-5.5 1.1-7.6a9.7 9.7 0 0 1 3.5-4.8A9.9 9.9 0 0 1 172 17h.3c3.5 0 6 1.1 7.9 3.5 1.7 2.3 2.6 5.7 2.6 10v16.8h-6.4V29.6c0-2.1-.6-3.8-1.8-5a6.4 6.4 0 0 0-4.8-1.8c-2 0-3.7.7-5 2a7.8 7.8 0 0 0-1.9 5.5v17h-6.4Zm63.8 1a12.2 12.2 0 0 1-10.9-6.2 19 19 0 0 1-1.8-7.3h1.4v12.5h-5.1v-40h6.4v19.8l-2 3.5c.2-3.1.8-5.7 1.9-7.7a11 11 0 0 1 4.4-4.5c1.8-1 3.9-1.5 6.1-1.5a13.4 13.4 0 0 1 12.8 9.1c.7 1.9 1 3.8 1 6v1c0 2.2-.3 4.1-1 6a13.6 13.6 0 0 1-13.2 9.4Zm-1.2-5.5a8.4 8.4 0 0 0 7.9-5c.7-1.5 1.1-3.3 1.1-5.3s-.4-3.8-1.1-5.3a8.7 8.7 0 0 0-3.2-3.6 9.6 9.6 0 0 0-9.2-.2 8.5 8.5 0 0 0-3.3 3.2c-.8 1.4-1.3 3-1.3 5v2.3a9 9 0 0 0 1.3 4.8 9 9 0 0 0 3.4 3c1.4.7 2.8 1 4.4 1Zm27.3 3.9-10-28.9h6.5l9.5 28.9h-6Zm-7.5 12.2v-5.7h4.9c1 0 2-.1 2.9-.4a4 4 0 0 0 2-1.4c.4-.7.9-1.6 1.2-2.7l8.6-30.9h6.2l-9.3 32.4a14 14 0 0 1-2.5 5 8.9 8.9 0 0 1-4 2.8c-1.5.6-3.4.9-5.6.9h-4.4Zm9-12.2v-5.2h6.4v5.2H248Z\")\r\n              path(fill=\"#003DFF\" d=\"M534.4 9.1H528a.8.8 0 0 1-.7-.7V1.8c0-.4.2-.7.6-.8l6.5-1c.4 0 .8.2.9.6v7.8c0 .4-.4.7-.8.7zM428 35.2V.8c0-.5-.3-.8-.7-.8h-.2l-6.4 1c-.4 0-.7.4-.7.8v35c0 1.6 0 11.8 12.3 12.2.5 0 .8-.4.8-.8V43c0-.4-.3-.7-.6-.8-4.5-.5-4.5-6-4.5-7zm106.5-21.8H528c-.4 0-.7.4-.7.8v34c0 .4.3.8.7.8h6.5c.4 0 .8-.4.8-.8v-34c0-.5-.4-.8-.8-.8zm-17.7 21.8V.8c0-.5-.3-.8-.8-.8l-6.5 1c-.4 0-.7.4-.7.8v35c0 1.6 0 11.8 12.3 12.2.4 0 .8-.4.8-.8V43c0-.4-.3-.7-.7-.8-4.4-.5-4.4-6-4.4-7zm-22.2-20.6a16.5 16.5 0 0 1 8.6 9.3c.8 2.2 1.3 4.8 1.3 7.5a19.4 19.4 0 0 1-4.6 12.6 14.8 14.8 0 0 1-5.2 3.6c-2 .9-5.2 1.4-6.8 1.4a21 21 0 0 1-6.7-1.4 15.4 15.4 0 0 1-8.6-9.3 21.3 21.3 0 0 1 0-14.4 15.2 15.2 0 0 1 8.6-9.3c2-.8 4.3-1.2 6.7-1.2s4.6.4 6.7 1.2zm-6.7 27.6c2.7 0 4.7-1 6.2-3s2.2-4.3 2.2-7.8-.7-6.3-2.2-8.3-3.5-3-6.2-3-4.7 1-6.1 3c-1.5 2-2.2 4.8-2.2 8.3s.7 5.8 2.2 7.8 3.5 3 6.2 3zm-88.8-28.8c-6.2 0-11.7 3.3-14.8 8.2a18.6 18.6 0 0 0 4.8 25.2c1.8 1.2 4 1.8 6.2 1.7s.1 0 .1 0h.9c4.2-.7 8-4 9.1-8.1v7.4c0 .4.3.7.8.7h6.4a.7.7 0 0 0 .7-.7V14.2c0-.5-.3-.8-.7-.8h-13.5zm6.3 26.5a9.8 9.8 0 0 1-5.7 2h-.5a10 10 0 0 1-9.2-14c1.4-3.7 5-6.3 9-6.3h6.4v18.3zm152.3-26.5h13.5c.5 0 .8.3.8.7v33.7c0 .4-.3.7-.8.7h-6.4a.7.7 0 0 1-.8-.7v-7.4c-1.2 4-4.8 7.4-9 8h-.1a4.2 4.2 0 0 1-.5.1h-.9a10.3 10.3 0 0 1-7-2.6c-4-3.3-6.5-8.4-6.5-14.2 0-3.7 1-7.2 3-10 3-5 8.5-8.3 14.7-8.3zm.6 28.4c2.2-.1 4.2-.6 5.7-2V21.7h-6.3a9.8 9.8 0 0 0-9 6.4 10.2 10.2 0 0 0 9.1 13.9h.5zM452.8 13.4c-6.2 0-11.7 3.3-14.8 8.2a18.5 18.5 0 0 0 3.6 24.3 10.4 10.4 0 0 0 13 .6c2.2-1.5 3.8-3.7 4.5-6.1v7.8c0 2.8-.8 5-2.2 6.3-1.5 1.5-4 2.2-7.5 2.2l-6-.3c-.3 0-.7.2-.8.5l-1.6 5.5c-.1.4.1.8.5 1h.1c2.8.4 5.5.6 7 .6 6.3 0 11-1.4 14-4.1 2.7-2.5 4.2-6.3 4.5-11.4V14.2c0-.5-.4-.8-.8-.8h-13.5zm6.3 8.2v18.3a9.6 9.6 0 0 1-5.6 2h-1a10.3 10.3 0 0 1-8.8-14c1.4-3.7 5-6.3 9-6.3h6.4zM291 31.5A32 32 0 0 1 322.8 0h30.8c.6 0 1.2.5 1.2 1.2v61.5c0 1.1-1.3 1.7-2.2 1l-19.2-17a18 18 0 0 1-11 3.4 18.1 18.1 0 1 1 18.2-14.8c-.1.4-.5.7-.9.6-.1 0-.3 0-.4-.2l-3.8-3.4c-.4-.3-.6-.8-.7-1.4a12 12 0 1 0-2.4 8.3c.4-.4 1-.5 1.6-.2l14.7 13.1v-46H323a26 26 0 1 0 10 49.7c.8-.4 1.6-.2 2.3.3l3 2.7c.3.2.3.7 0 1l-.2.2a32 32 0 0 1-47.2-28.6z\")\r\n\r\n  #search-mask\r\n\r\n  script(src=url_for(theme.asset.algolia_search))\r\n  script(src=url_for(theme.asset.algolia_js))"
  },
  {
    "path": "layout/includes/third-party/search/docsearch.pug",
    "content": "- const { placeholder, docsearch: { appId, apiKey, indexName, option } } = theme.search\r\n\r\n.docsearch-wrap\r\n  #docsearch(style=\"display:none\")\r\n  link(rel=\"stylesheet\" href=url_for(theme.asset.docsearch_css))\r\n  script(src=url_for(theme.asset.docsearch_js))\r\n  script.\r\n    (() => {\r\n      docsearch(Object.assign({\r\n        appId: '!{appId}',\r\n        apiKey: '!{apiKey}',\r\n        indexName: '!{indexName}',\r\n        container: '#docsearch',\r\n        placeholder: '!{ placeholder || _p(\"search.input_placeholder\")}',\r\n      }, !{JSON.stringify(option)}))\r\n\r\n      const handleClick = () => {\r\n        document.querySelector('.DocSearch-Button').click()\r\n      }\r\n\r\n      const searchClickFn = () => {\r\n        btf.addEventListenerPjax(document.querySelector('#search-button > .search'), 'click', handleClick)\r\n      }\r\n\r\n      searchClickFn()\r\n      window.addEventListener('pjax:complete', searchClickFn)\r\n    })()\r\n\r\n\r\n"
  },
  {
    "path": "layout/includes/third-party/search/index.pug",
    "content": "case theme.search.use\r\n  when 'algolia_search'\r\n    include ./algolia.pug\r\n  when 'local_search'\r\n    include ./local-search.pug\r\n  when 'docsearch'\r\n    include ./docsearch.pug"
  },
  {
    "path": "layout/includes/third-party/search/local-search.pug",
    "content": "#local-search\r\n  .search-dialog\r\n    nav.search-nav\r\n      span.search-dialog-title= _p('search.title')\r\n      i.fas.fa-spinner.fa-pulse#loading-status(hidden)\r\n      button.search-close-button\r\n        i.fas.fa-times\r\n\r\n    #loading-database.text-center\r\n      i.fas.fa-spinner.fa-pulse\r\n      span= '  ' + _p(\"search.load_data\")\r\n\r\n    .local-search-input\r\n      input(placeholder=theme.search.placeholder || _p(\"search.input_placeholder\") type=\"text\")\r\n\r\n    hr\r\n\r\n    #local-search-results\r\n    #local-search-pagination.ais-Pagination(style=\"display:none;\")\r\n      ul.ais-Pagination-list\r\n    #local-search-stats\r\n\r\n  #search-mask\r\n  script(src=url_for(theme.asset.local_search))"
  },
  {
    "path": "layout/includes/third-party/share/addtoany.pug",
    "content": ".addtoany\n  .a2a_kit.a2a_kit_size_32.a2a_default_style\n    - let addtoanyItem = theme.share.addtoany.item.split(',')\n    each name in addtoanyItem\n      a(class=\"a2a_button_\" + name)\n\n    a.a2a_dd(href=\"https://www.addtoany.com/share\")\nscript(async src='https://static.addtoany.com/menu/page.js')\n\n\n"
  },
  {
    "path": "layout/includes/third-party/share/index.pug",
    "content": "- const { use } = theme.share\r\n\r\nif use\r\n  .post-share\r\n    case use\r\n      when 'addtoany'\r\n        !=partial('includes/third-party/share/addtoany', {}, {cache: true})\r\n      when 'sharejs'\r\n        include ./share-js.pug"
  },
  {
    "path": "layout/includes/third-party/share/share-js.pug",
    "content": "- const coverVal = page.cover_type === 'img' ? page.cover : theme.avatar.img\r\n.social-share(data-image=url_for(coverVal) data-sites= theme.share.sharejs.sites)\r\nlink(rel='stylesheet' href=url_for(theme.asset.sharejs_css) media=\"print\" onload=\"this.media='all'\")\r\nscript(src=url_for(theme.asset.sharejs) defer)"
  },
  {
    "path": "layout/includes/third-party/subtitle.pug",
    "content": "- const { effect, source, sub, typed_option } = theme.subtitle\r\n- let subContent = sub || new Array()\r\n\r\nscript.\r\n  window.typedJSFn = {\r\n    init: str => {\r\n      window.typed = new Typed('#subtitle', Object.assign({\r\n        strings: str,\r\n        startDelay: 300,\r\n        typeSpeed: 150,\r\n        loop: true,\r\n        backSpeed: 50,\r\n      }, !{JSON.stringify(typed_option)}))\r\n    },\r\n    run: subtitleType => {\r\n      if (!{effect}) {\r\n        if (typeof Typed === 'function') {\r\n          subtitleType()\r\n        } else {\r\n          btf.getScript('!{url_for(theme.asset.typed)}').then(subtitleType)\r\n        }\r\n      } else {\r\n        subtitleType()\r\n      }\r\n    },\r\n    processSubtitle: (content, extraContents = []) => {\r\n      if (!{effect}) {\r\n        const sub = !{JSON.stringify(subContent)}.slice()\r\n\r\n        if (extraContents.length > 0) {\r\n          sub.unshift(...extraContents)\r\n        }\r\n\r\n        if (typeof content === 'string') {\r\n          sub.unshift(content)\r\n        } else if (Array.isArray(content)) {\r\n          sub.unshift(...content)\r\n        }\r\n\r\n        sub.length > 0 && typedJSFn.init(sub)\r\n      } else {\r\n        document.getElementById('subtitle').textContent = typeof content === 'string' ? content :\r\n          (Array.isArray(content) && content.length > 0 ? content[0] : '')\r\n      }\r\n    }\r\n  }\r\n  btf.addGlobalFn('pjaxSendOnce', () => { typed.destroy() }, 'typedDestroy')\r\n\r\ncase source\r\n  when 1\r\n    script.\r\n      function subtitleType () {\r\n        fetch('https://v1.hitokoto.cn')\r\n          .then(response => response.json())\r\n          .then(data => {\r\n            const from = '出自 ' + data.from\r\n            typedJSFn.processSubtitle(data.hitokoto, [from])\r\n          })\r\n          .catch(err => {\r\n            console.error('Failed to get the Hitokoto API:', err)\r\n            typedJSFn.processSubtitle(!{JSON.stringify(subContent)})\r\n          })\r\n      }\r\n      typedJSFn.run(subtitleType)\r\n\r\n  when 2\r\n    script.\r\n      function subtitleType () {\r\n        fetch('https://v.api.aa1.cn/api/yiyan/index.php')\r\n          .then(response => response.text())\r\n          .then(data => {\r\n            const reg = /<p>(.*?)<\\/p>/g\r\n            const result = reg.exec(data)\r\n            if (result && result[1]) {\r\n              typedJSFn.processSubtitle(result[1])\r\n            } else {\r\n              throw new Error('Failed to parse the return value of the Yiyan API')\r\n            }\r\n          })\r\n          .catch(err => {\r\n            console.error('Failed to get the Yiyan API:', err)\r\n            typedJSFn.processSubtitle(!{JSON.stringify(subContent.length)})\r\n          })\r\n      }\r\n      typedJSFn.run(subtitleType)\r\n\r\n  when 3\r\n    script.\r\n      function subtitleType () {\r\n        btf.getScript('https://sdk.jinrishici.com/v2/browser/jinrishici.js')\r\n          .then(() => {\r\n            jinrishici.load(result => {\r\n              if (result && result.data && result.data.content) {\r\n                typedJSFn.processSubtitle(result.data.content)\r\n              } else {\r\n                throw new Error('Failed to parse the return value of Jinrishici API')\r\n              }\r\n            })\r\n          })\r\n          .catch(err => {\r\n            console.error('Failed to get the Jinrishici API:', err)\r\n            typedJSFn.processSubtitle(!{JSON.stringify(subContent.length)})\r\n          })\r\n      }\r\n      typedJSFn.run(subtitleType)\r\n\r\n  default\r\n    if subContent.length > 0\r\n      script.\r\n        function subtitleType () {\r\n          typedJSFn.processSubtitle(!{JSON.stringify(subContent)})\r\n        }\r\n        typedJSFn.run(subtitleType)"
  },
  {
    "path": "layout/includes/third-party/umami_analytics.pug",
    "content": "- let { serverURL, script_name, website_id, option, UV_PV } = theme.umami_analytics\r\n- const isServerURL = !!serverURL\r\n- const baseURL = serverURL ? serverURL.replace(/\\/$/, '') : 'https://cloud.umami.is'\r\n- const apiUrl = serverURL ? serverURL.replace(/\\/$/, '') + '/api' : 'https://api.umami.is/v1'\r\n\r\nscript.\r\n  (() => {\r\n    const option = !{JSON.stringify(option)}\r\n    const config = !{JSON.stringify(UV_PV)}\r\n\r\n    const runTrack = () => {\r\n      if (typeof umami !== 'undefined' && typeof umami.track === 'function') {\r\n        umami.track(props => ({ ...props, url: window.location.pathname, title: GLOBAL_CONFIG_SITE.title }))\r\n      } else {\r\n        console.warn('Umami Analytics: umami.track is not available')\r\n      }\r\n    }\r\n\r\n    const loadUmamiJS = () => {\r\n      btf.getScript('!{baseURL}/!{script_name}', {\r\n        'data-website-id': '!{website_id}',\r\n        'data-auto-track': 'false',\r\n        ...option\r\n      }).then(() => {\r\n        runTrack()\r\n      }).catch(error => {\r\n        console.error('Umami Analytics: Error loading script', error)\r\n      })\r\n    }\r\n\r\n    const getData = async (isPost) => {\r\n      try {\r\n        const now = Date.now()\r\n        const keyUrl = isPost ? `&url=${window.location.pathname}&path=${window.location.pathname}` : ''\r\n        const headerList = { 'Accept': 'application/json' }\r\n\r\n        if (!{isServerURL}) {\r\n          headerList['Authorization'] = `Bearer ${config.token}`\r\n        } else {\r\n          headerList['x-umami-api-key'] = config.token\r\n        }\r\n\r\n        const res = await fetch(`!{apiUrl}/websites/!{website_id}/stats?startAt=0000000000&endAt=${now}${keyUrl}`, {\r\n          method: \"GET\",\r\n          headers: headerList\r\n        })\r\n\r\n        if (!res.ok) {\r\n          throw new Error(`HTTP error! status: ${res.status}`)\r\n        }\r\n\r\n        return await res.json()\r\n      } catch (error) {\r\n        console.error('Umami Analytics: Failed to fetch data', error)\r\n        throw error\r\n      }\r\n    }\r\n\r\n    const insertData = async () => {\r\n      try {\r\n        if (GLOBAL_CONFIG_SITE.pageType === 'post' && config.page_pv) {\r\n          const pagePV = document.getElementById('umamiPV')\r\n          if (pagePV) {\r\n            const data = await getData(true)\r\n            if (data && data.pageviews) {\r\n              pagePV.textContent = typeof data.pageviews.value !== 'undefined' ? data.pageviews.value : data.pageviews\r\n            } else {\r\n              console.warn('Umami Analytics: Invalid page view data received')\r\n            }\r\n          }\r\n        }\r\n\r\n        if (config.site_uv || config.site_pv) {\r\n          const data = await getData(false)\r\n\r\n          if (config.site_uv) {\r\n            const siteUV = document.getElementById('umami-site-uv')\r\n            if (siteUV && data && data.visitors) {\r\n              siteUV.textContent = typeof data.visitors.value !== 'undefined' ? data.visitors.value : data.visitors\r\n            } else if (siteUV) {\r\n              console.warn('Umami Analytics: Invalid site UV data received')\r\n            }\r\n          }\r\n\r\n          if (config.site_pv) {\r\n            const sitePV = document.getElementById('umami-site-pv')\r\n            if (sitePV && data && data.pageviews) {\r\n              sitePV.textContent = typeof data.pageviews.value !== 'undefined' ? data.pageviews.value : data.pageviews\r\n            } else if (sitePV) {\r\n              console.warn('Umami Analytics: Invalid site PV data received')\r\n            }\r\n          }\r\n        }\r\n      } catch (error) {\r\n        console.error('Umami Analytics: Failed to insert data', error)\r\n      }\r\n    }\r\n\r\n    btf.addGlobalFn('pjaxComplete', runTrack, 'umami_analytics_run_track')\r\n    btf.addGlobalFn('pjaxComplete', insertData, 'umami_analytics_insert')\r\n\r\n\r\n    loadUmamiJS()\r\n\r\n    if (document.readyState === 'loading') {\r\n      document.addEventListener('DOMContentLoaded', insertData)\r\n    } else {\r\n      setTimeout(insertData, 100)\r\n    }\r\n  })()"
  },
  {
    "path": "layout/includes/widget/card_ad.pug",
    "content": "if theme.ad && theme.ad.aside\r\n  .card-widget.ads-wrap\r\n    != theme.ad.aside\r\n"
  },
  {
    "path": "layout/includes/widget/card_announcement.pug",
    "content": "if theme.aside.card_announcement.enable\r\n  .card-widget.card-announcement\r\n    .item-headline\r\n      i.fas.fa-bullhorn.fa-shake\r\n      span= _p('aside.card_announcement')\r\n    .announcement_content!= theme.aside.card_announcement.content"
  },
  {
    "path": "layout/includes/widget/card_archives.pug",
    "content": "if theme.aside.card_archives.enable\r\n  .card-widget.card-archives\r\n    - let type = theme.aside.card_archives.type || 'monthly'\r\n    - let format = theme.aside.card_archives.format || 'MMMM YYYY'\r\n    - let order = theme.aside.card_archives.order || -1\r\n    - let limit = theme.aside.card_archives.limit === 0 ? 0 : theme.aside.card_archives.limit || 8\r\n    != aside_archives({ type:type, format: format, order: order, limit: limit })\r\n"
  },
  {
    "path": "layout/includes/widget/card_author.pug",
    "content": "if theme.aside.card_author.enable\r\n  .card-widget.card-info.text-center\r\n    .avatar-img\r\n      img(src=url_for(theme.avatar.img) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt=\"avatar\")\r\n    .author-info-name= config.author\r\n    .author-info-description!= theme.aside.card_author.description || config.description\r\n\r\n    .site-data\r\n      a(href=url_for(config.archive_dir) + '/')\r\n        .headline= _p('aside.articles')\r\n        .length-num= site.posts.length\r\n      a(href=url_for(config.tag_dir) + '/')\r\n        .headline= _p('aside.tags')\r\n        .length-num= site.tags.length\r\n      a(href=url_for(config.category_dir) + '/')\r\n        .headline= _p('aside.categories')\r\n        .length-num= site.categories.length\r\n\r\n    if theme.aside.card_author.button.enable\r\n      a#card-info-btn(href=theme.aside.card_author.button.link)\r\n        i(class=theme.aside.card_author.button.icon)\r\n        span=theme.aside.card_author.button.text\r\n\r\n    if(theme.social)\r\n      .card-info-social-icons\r\n        !=partial('includes/header/social', {}, {cache: true})\r\n"
  },
  {
    "path": "layout/includes/widget/card_bottom_self.pug",
    "content": "if site.data.widget && site.data.widget.bottom\r\n  each item in site.data.widget.bottom\r\n    .card-widget(class=item.class_name id=item.id_name style=item.order ? `order: ${item.order}` : '')\r\n      .item-headline\r\n        i(class=item.icon)\r\n        span=item.name\r\n      .item-content\r\n        !=item.html\r\n\r\n"
  },
  {
    "path": "layout/includes/widget/card_categories.pug",
    "content": "if theme.aside.card_categories.enable\r\n  if site.categories.length\r\n    .card-widget.card-categories\r\n      !=aside_categories({ limit: theme.aside.card_categories.limit === 0 ? 0 : theme.aside.card_categories.limit || 8 , expand: theme.aside.card_categories.expand })\r\n"
  },
  {
    "path": "layout/includes/widget/card_newest_comment.pug",
    "content": "if theme.aside.card_newest_comments.enable && theme.comments.use && !['Livere','Facebook Comments','Giscus'].includes(theme.comments.use[0])\r\n  .card-widget#card-newest-comments\r\n    .item-headline\r\n      i.fas.fa-comment-dots\r\n      span= _p('aside.card_newest_comments.headline')\r\n    .aside-list\r\n      span= _p('aside.card_newest_comments.loading_text')\r\n"
  },
  {
    "path": "layout/includes/widget/card_post_series.pug",
    "content": "if theme.aside.card_post_series.enable\r\n  - const array = fragment_cache('seriesArr', groupPosts)\r\n  .card-widget.card-post-series\r\n    .item-headline\r\n      i.fa-solid.fa-layer-group\r\n      span= theme.aside.card_post_series.series_title ? page.series : _p('aside.card_post_series')\r\n    .aside-list\r\n      each item in array[page.series]\r\n        - const { path, title = _p('no_title'), cover, cover_type, date:dateA } = item\r\n        - let link = url_for(path)\r\n        - let no_cover = cover === false || !theme.cover.aside_enable ? 'no-cover' : ''\r\n        .aside-list-item(class=no_cover)\r\n          if cover && theme.cover.aside_enable\r\n            a.thumbnail(href=link title=title)\r\n              if cover_type === 'img'\r\n                img(src=url_for(cover) onerror=`this.onerror=null;this.src='${url_for(theme.error_img.post_page)}'` alt=title)\r\n              else\r\n                div(style=`background: ${cover}`)\r\n          .content\r\n            a.title(href=link title=title)= title\r\n            time(datetime=date_xml(dateA) title=_p('post.created') + ' ' + full_date(dateA)) #[=date(dateA, config.date_format)]\r\n"
  },
  {
    "path": "layout/includes/widget/card_post_toc.pug",
    "content": "- let tocNumber = typeof page.toc_number === 'boolean' ? page.toc_number : theme.toc.number\r\n- let tocExpand = typeof page.toc_expand === 'boolean' ? page.toc_expand : theme.toc.expand\r\n- let tocExpandClass = tocExpand ? 'is-expand' : ''\r\n\r\n#card-toc.card-widget\r\n  .item-headline\r\n    i.fas.fa-stream\r\n    span= _p('aside.card_toc')\r\n    span.toc-percentage\r\n\r\n  if (page.encrypt == true)\r\n    .toc-content.toc-div-class(class=tocExpandClass style=\"display:none\")!=toc(page.origin, {list_number: tocNumber})\r\n  else\r\n    .toc-content(class=tocExpandClass)!=toc(page.content, {list_number: tocNumber})\r\n"
  },
  {
    "path": "layout/includes/widget/card_recent_post.pug",
    "content": "if theme.aside.card_recent_post.enable\r\n  .card-widget.card-recent-post\r\n    .item-headline\r\n      i.fas.fa-history\r\n      span= _p('aside.card_recent_post')\r\n    .aside-list\r\n      - let postLimit = theme.aside.card_recent_post.limit === 0 ? site.posts.length : theme.aside.card_recent_post.limit || 5\r\n      - let sort = theme.aside.card_recent_post.sort === 'updated' ? 'updated' : 'date'\r\n      - site.posts.sort(sort, -1).limit(postLimit).each(function(article){\r\n        - let link = article.link || article.path\r\n        - let title = article.title || _p('no_title')\r\n        - let no_cover = article.cover === false || !theme.cover.aside_enable ? 'no-cover' : ''\r\n        - let post_cover = article.cover\r\n        .aside-list-item(class=no_cover)\r\n          if post_cover && theme.cover.aside_enable\r\n            a.thumbnail(href=url_for(link) title=title)\r\n              if article.cover_type === 'img'\r\n                img(src=url_for(post_cover) onerror=`this.onerror=null;this.src='${url_for(theme.error_img.post_page)}'` alt=title)\r\n              else\r\n                div(style=`background: ${post_cover}`)\r\n          .content\r\n            a.title(href=url_for(link) title=title)= title\r\n            if theme.aside.card_recent_post.sort === 'updated'\r\n              time(datetime=date_xml(article.updated) title=_p('post.updated') + ' ' + full_date(article.updated)) #[=date(article.updated, config.date_format)]\r\n            else\r\n              time(datetime=date_xml(article.date) title=_p('post.created') + ' ' + full_date(article.date)) #[=date(article.date, config.date_format)]\r\n      - })"
  },
  {
    "path": "layout/includes/widget/card_tags.pug",
    "content": "if theme.aside.card_tags.enable\r\n  if site.tags.length\r\n    .card-widget.card-tags\r\n      .item-headline\r\n        i.fas.fa-tags\r\n        span= _p('aside.card_tags')\r\n\r\n      - let { limit, orderby, order, custom_colors } = theme.aside.card_tags\r\n      - limit = limit === 0 ? 0 : limit || 40\r\n\r\n      if theme.aside.card_tags.color\r\n        .card-tag-cloud!= cloudTags({source: site.tags, orderby: orderby, order: order, minfontsize: 1.15, maxfontsize: 1.45, limit: limit, unit: 'em', page: 'index', custom_colors: custom_colors})\r\n      else\r\n        .card-tag-cloud!= tagcloud({orderby: orderby, order: order, min_font: 1.1, max_font: 1.5, amount: limit, color: true, start_color: '#999', end_color: '#99a9bf', unit: 'em'})\r\n"
  },
  {
    "path": "layout/includes/widget/card_top_self.pug",
    "content": "if site.data.widget && site.data.widget.top\r\n  each item in site.data.widget.top\r\n    .card-widget(class=item.class_name id=item.id_name)\r\n      .item-headline\r\n        i(class=item.icon)\r\n        span=item.name\r\n      .item-content\r\n        !=item.html"
  },
  {
    "path": "layout/includes/widget/card_webinfo.pug",
    "content": "if theme.aside.card_webinfo.enable\r\n  .card-widget.card-webinfo\r\n    .item-headline\r\n      i.fas.fa-chart-line\r\n      span= _p('aside.card_webinfo.headline')\r\n    .webinfo\r\n      if theme.aside.card_webinfo.post_count\r\n        .webinfo-item\r\n          .item-name= `${_p('aside.card_webinfo.article_name')} :`\r\n          .item-count= site.posts.length\r\n      if theme.aside.card_webinfo.runtime_date\r\n        .webinfo-item\r\n          .item-name= `${_p('aside.card_webinfo.runtime.name')} :`\r\n          .item-count#runtimeshow(data-publishDate=date_xml(theme.aside.card_webinfo.runtime_date))\r\n            i.fa-solid.fa-spinner.fa-spin\r\n      if theme.wordcount.enable && theme.wordcount.total_wordcount\r\n        .webinfo-item\r\n          .item-name= `${_p('aside.card_webinfo.site_wordcount')} :`\r\n          .item-count= totalcount(site)\r\n      if theme.umami_analytics.enable && theme.umami_analytics.UV_PV.site_uv\r\n        .webinfo-item\r\n          .item-name= `${_p('aside.card_webinfo.site_uv_name')} :`\r\n          .item-count#umami-site-uv\r\n            i.fa-solid.fa-spinner.fa-spin\r\n      else if theme.busuanzi.site_uv\r\n        .webinfo-item\r\n          .item-name= `${_p('aside.card_webinfo.site_uv_name')} :`\r\n          .item-count#busuanzi_value_site_uv\r\n            i.fa-solid.fa-spinner.fa-spin\r\n      if theme.umami_analytics.enable && theme.umami_analytics.UV_PV.site_pv\r\n        .webinfo-item\r\n          .item-name= `${_p('aside.card_webinfo.site_pv_name')} :`\r\n          .item-count#umami-site-pv\r\n            i.fa-solid.fa-spinner.fa-spin\r\n      else if theme.busuanzi.site_pv\r\n        .webinfo-item\r\n          .item-name= `${_p('aside.card_webinfo.site_pv_name')} :`\r\n          .item-count#busuanzi_value_site_pv\r\n            i.fa-solid.fa-spinner.fa-spin\r\n      if theme.aside.card_webinfo.last_push_date\r\n        .webinfo-item\r\n          .item-name= `${_p('aside.card_webinfo.last_push_date.name')} :`\r\n          .item-count#last-push-date(data-lastPushDate=date_xml(Date.now()))\r\n            i.fa-solid.fa-spinner.fa-spin"
  },
  {
    "path": "layout/includes/widget/index.pug",
    "content": "#aside-content.aside-content\r\n  //- post\r\n  if globalPageType === 'post'\r\n    - const tocStyle = page.toc_style_simple\r\n    - const tocStyleVal = tocStyle === true || tocStyle === false ? tocStyle : theme.toc.style_simple\r\n    if showToc && tocStyleVal\r\n      .sticky_layout\r\n        include ./card_post_toc.pug\r\n    else\r\n      !=partial('includes/widget/card_author', {}, {cache: true})\r\n      !=partial('includes/widget/card_announcement', {}, {cache: true})\r\n      !=partial('includes/widget/card_top_self', {}, {cache: true})\r\n      .sticky_layout\r\n        if showToc\r\n          include ./card_post_toc.pug\r\n        if page.series\r\n          include ./card_post_series.pug\r\n        !=partial('includes/widget/card_recent_post', {}, {cache: true})\r\n        !=partial('includes/widget/card_ad', {}, {cache: true})\r\n  else\r\n    //- page\r\n    !=partial('includes/widget/card_author', {}, {cache: true})\r\n    !=partial('includes/widget/card_announcement', {}, {cache: true})\r\n    !=partial('includes/widget/card_top_self', {}, {cache: true})\r\n\r\n    .sticky_layout\r\n      if showToc\r\n        include ./card_post_toc.pug\r\n      !=partial('includes/widget/card_recent_post', {}, {cache: true})\r\n      !=partial('includes/widget/card_ad', {}, {cache: true})\r\n      !=partial('includes/widget/card_newest_comment', {}, {cache: true})\r\n      !=partial('includes/widget/card_categories', {}, {cache: true})\r\n      !=partial('includes/widget/card_tags', {}, {cache: true})\r\n      !=partial('includes/widget/card_archives', {}, {cache: true})\r\n      !=partial('includes/widget/card_webinfo', {}, {cache: true})\r\n      !=partial('includes/widget/card_bottom_self', {}, {cache: true})"
  },
  {
    "path": "layout/index.pug",
    "content": "extends includes/layout.pug\r\n\r\nblock content\r\n  include ./includes/mixins/indexPostUI.pug\r\n  +indexPostUI"
  },
  {
    "path": "layout/page.pug",
    "content": "extends includes/layout.pug\r\n\r\nblock content\r\n  - const noCardLayout = ['shuoshuo', '404'].includes(page.type) ? 'nc' : ''\r\n  - var commentsJsLoad = false\r\n\r\n  mixin commentLoad\r\n    if page.comments !== false && theme.comments.use\r\n      - commentsJsLoad = true\r\n      !=partial('includes/third-party/comments/index', {}, {cache: true})\r\n\r\n  #page(class=noCardLayout)\r\n    if top_img === false && page.title\r\n      .page-title= page.title\r\n\r\n    case page.type\r\n      when 'tags'\r\n        include includes/page/tags.pug\r\n        +commentLoad\r\n      when 'link'\r\n        include includes/page/flink.pug\r\n        +commentLoad\r\n      when 'categories'\r\n        include includes/page/categories.pug\r\n        +commentLoad\r\n      when '404'\r\n        include includes/page/404.pug\r\n      when 'shuoshuo'\r\n        include includes/page/shuoshuo.pug\r\n      default\r\n        include includes/page/default-page.pug\r\n        +commentLoad"
  },
  {
    "path": "layout/post.pug",
    "content": "extends includes/layout.pug\r\n\r\nblock content\r\n  #post\r\n    if top_img === false\r\n      include includes/header/post-info.pug\r\n\r\n    article#article-container.container.post-content\r\n      if theme.noticeOutdate.enable && page.noticeOutdate !== false\r\n        include includes/post/outdate-notice.pug\r\n      else\r\n        !=page.content\r\n    include includes/post/post-copyright.pug\r\n    .tag_share\r\n      if (page.tags.length > 0 && theme.post_meta.post.tags)\r\n        .post-meta__tag-list\r\n          each item, index in page.tags.data\r\n            a(href=url_for(item.path)).post-meta__tags #[=item.name]\r\n      include includes/third-party/share/index.pug\r\n\r\n    if theme.reward.enable && theme.reward.QR_code\r\n      !=partial('includes/post/reward', {}, {cache: true})\r\n\r\n    //- ad\r\n    if theme.ad && theme.ad.post\r\n      .ads-wrap!=theme.ad.post\r\n\r\n    if theme.post_pagination\r\n      include includes/pagination.pug\r\n    if theme.related_post && theme.related_post.enable\r\n      != related_posts(page,site.posts)\r\n\r\n    if page.comments !== false && theme.comments.use\r\n      - var commentsJsLoad = true\r\n      !=partial('includes/third-party/comments/index', {}, {cache: true})\r\n"
  },
  {
    "path": "layout/tag.pug",
    "content": "extends includes/layout.pug\r\n\r\nblock content\r\n  if theme.tag_ui == 'index'\r\n    include ./includes/mixins/indexPostUI.pug\r\n    +indexPostUI\r\n  else\r\n    include ./includes/mixins/article-sort.pug\r\n    #tag\r\n      .article-sort-title= _p('page.tag') + ' - ' + page.tag\r\n      +articleSort(page.posts)\r\n      include includes/pagination.pug"
  },
  {
    "path": "package.json",
    "content": "{\r\n  \"name\": \"hexo-theme-butterfly\",\r\n  \"version\": \"5.5.4\",\r\n  \"description\": \"A Simple and Card UI Design theme for Hexo\",\r\n  \"main\": \"package.json\",\r\n  \"scripts\": {\r\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\r\n  },\r\n  \"keywords\": [\r\n    \"hexo\",\r\n    \"theme\",\r\n    \"butterfly\",\r\n    \"Card UI Design\",\r\n    \"Jerry\",\r\n    \"hexo-theme-butterfly\"\r\n  ],\r\n  \"repository\": {\r\n    \"type\": \"git\",\r\n    \"url\": \"https://github.com/jerryc127/hexo-theme-butterfly.git\"\r\n  },\r\n  \"bugs\": {\r\n    \"url\": \"https://github.com/jerryc127/hexo-theme-butterfly/issues\",\r\n    \"email\": \"my@crazywong.com\"\r\n  },\r\n  \"dependencies\": {\r\n    \"hexo-renderer-pug\": \"^3.0.0\",\r\n    \"hexo-renderer-stylus\": \"^3.0.1\",\r\n    \"hexo-util\": \"^4.0.0\",\r\n    \"moment-timezone\": \"^0.6.0\"\r\n  },\r\n  \"homepage\": \"https://butterfly.js.org/\",\r\n  \"author\": \"Jerry <my@crazywong.com>\",\r\n  \"license\": \"Apache-2.0\"\r\n}\r\n"
  },
  {
    "path": "plugins.yml",
    "content": "abcjs_basic_js:\r\n  name: abcjs\r\n  file: dist/abcjs-basic-min.js\r\n  version: 6.6.0\r\nactivate_power_mode:\r\n  name: butterfly-extsrc\r\n  file: dist/activate-power-mode.min.js\r\n  version: 1.1.6\r\nalgolia_search:\r\n  name: algoliasearch\r\n  file: dist/lite/builds/browser.umd.js\r\n  version: 5.47.0\r\naplayer_css:\r\n  name: aplayer\r\n  file: dist/APlayer.min.css\r\n  version: 1.10.1\r\naplayer_js:\r\n  name: aplayer\r\n  file: dist/APlayer.min.js\r\n  version: 1.10.1\r\nartalk_css:\r\n  name: artalk\r\n  file: dist/Artalk.css\r\n  version: 2.9.1\r\nartalk_js:\r\n  name: artalk\r\n  file: dist/Artalk.js\r\n  version: 2.9.1\r\nblueimp_md5:\r\n  name: blueimp-md5\r\n  file: js/md5.min.js\r\n  version: 2.19.0\r\ncanvas_fluttering_ribbon:\r\n  name: butterfly-extsrc\r\n  file: dist/canvas-fluttering-ribbon.min.js\r\n  version: 1.1.6\r\ncanvas_nest:\r\n  name: butterfly-extsrc\r\n  file: dist/canvas-nest.min.js\r\n  version: 1.1.6\r\ncanvas_ribbon:\r\n  name: butterfly-extsrc\r\n  file: dist/canvas-ribbon.min.js\r\n  version: 1.1.6\r\nchartjs:\r\n  name: chart.js\r\n  file: dist/chart.umd.js\r\n  version: 4.5.1\r\nclickShowText:\r\n  name: butterfly-extsrc\r\n  file: dist/click-show-text.min.js\r\n  version: 1.1.6\r\nclick_heart:\r\n  name: butterfly-extsrc\r\n  file: dist/click-heart.min.js\r\n  version: 1.1.6\r\ndisqusjs:\r\n  name: disqusjs\r\n  file: dist/browser/disqusjs.es2015.umd.min.js\r\n  version: 3.2.1\r\ndisqusjs_css:\r\n  name: disqusjs\r\n  file: dist/browser/styles/disqusjs.css\r\n  version: 3.2.1\r\ndocsearch_css:\r\n  name: '@docsearch/css'\r\n  other_name: docsearch-css\r\n  file: dist/style.css\r\n  version: 4.5.3\r\ndocsearch_js:\r\n  name: '@docsearch/js'\r\n  other_name: docsearch-js\r\n  file: dist/umd/index.js\r\n  version: 4.5.3\r\negjs_infinitegrid:\r\n  name: '@egjs/infinitegrid'\r\n  other_name: egjs-infinitegrid\r\n  file: dist/infinitegrid.min.js\r\n  version: 4.13.0\r\nfancybox:\r\n  name: '@fancyapps/ui'\r\n  file: dist/fancybox/fancybox.umd.js\r\n  version: 6.1.9\r\n  other_name: fancyapps-ui\r\nfancybox_css:\r\n  name: '@fancyapps/ui'\r\n  file: dist/fancybox/fancybox.css\r\n  version: 6.1.9\r\n  other_name: fancyapps-ui\r\nfireworks:\r\n  name: butterfly-extsrc\r\n  file: dist/fireworks.min.js\r\n  version: 1.1.6\r\nfontawesome:\r\n  name: '@fortawesome/fontawesome-free'\r\n  file: css/all.min.css\r\n  other_name: font-awesome\r\n  version: 7.1.0\r\ngitalk:\r\n  name: gitalk\r\n  file: dist/gitalk.min.js\r\n  version: 1.8.0\r\ngitalk_css:\r\n  name: gitalk\r\n  file: dist/gitalk.css\r\n  version: 1.8.0\r\ninstantpage:\r\n  name: instant.page\r\n  file: instantpage.js\r\n  version: 5.2.0\r\nkatex:\r\n  name: katex\r\n  file: dist/katex.min.css\r\n  other_name: KaTeX\r\n  version: 0.16.28\r\nkatex_copytex:\r\n  name: katex\r\n  file: dist/contrib/copy-tex.min.js\r\n  other_name: KaTeX\r\n  version: 0.16.28\r\nlazyload:\r\n  name: vanilla-lazyload\r\n  file: dist/lazyload.iife.min.js\r\n  version: 19.1.3\r\nmathjax:\r\n  name: mathjax\r\n  file: tex-mml-chtml.js\r\n  version: 4.1.0\r\nmedium_zoom:\r\n  name: medium-zoom\r\n  file: dist/medium-zoom.min.js\r\n  version: 1.1.0\r\nmermaid:\r\n  name: mermaid\r\n  file: dist/mermaid.min.js\r\n  version: 11.12.2\r\nmeting_js:\r\n  name: butterfly-extsrc\r\n  file: metingjs/dist/Meting.min.js\r\n  version: 1.1.6\r\npace_default_css:\r\n  name: pace-js\r\n  other_name: pace\r\n  file: themes/blue/pace-theme-minimal.css\r\n  version: 1.2.4\r\npace_js:\r\n  name: pace-js\r\n  other_name: pace\r\n  file: pace.min.js\r\n  version: 1.2.4\r\npjax:\r\n  name: pjax\r\n  file: pjax.min.js\r\n  version: 0.2.8\r\nprismjs_autoloader:\r\n  name: prismjs\r\n  file: plugins/autoloader/prism-autoloader.min.js\r\n  other_name: prism\r\n  version: 1.30.0\r\nprismjs_js:\r\n  name: prismjs\r\n  file: prism.js\r\n  other_name: prism\r\n  version: 1.30.0\r\nprismjs_lineNumber_js:\r\n  name: prismjs\r\n  file: plugins/line-numbers/prism-line-numbers.min.js\r\n  other_name: prism\r\n  version: 1.30.0\r\nsharejs:\r\n  name: butterfly-extsrc\r\n  file: sharejs/dist/js/social-share.min.js\r\n  version: 1.1.6\r\nsharejs_css:\r\n  name: butterfly-extsrc\r\n  file: sharejs/dist/css/share.min.css\r\n  version: 1.1.6\r\nsnackbar:\r\n  name: node-snackbar\r\n  file: dist/snackbar.min.js\r\n  version: 0.1.16\r\nsnackbar_css:\r\n  name: node-snackbar\r\n  file: dist/snackbar.min.css\r\n  version: 0.1.16\r\ntwikoo:\r\n  name: twikoo\r\n  file: dist/twikoo.all.min.js\r\n  version: 1.6.44\r\ntyped:\r\n  name: typed.js\r\n  file: dist/typed.umd.js\r\n  version: 3.0.0\r\nvaline:\r\n  name: valine\r\n  file: dist/Valine.min.js\r\n  version: 1.5.3\r\nwaline_css:\r\n  name: '@waline/client'\r\n  file: dist/waline.css\r\n  other_name: waline\r\n  version: 3.8.0\r\nwaline_js:\r\n  name: '@waline/client'\r\n  file: dist/waline.js\r\n  other_name: waline\r\n  version: 3.8.0\r\n"
  },
  {
    "path": "scripts/common/default_config.js",
    "content": "// Butterfly 主題默認配置\n// Default configuration for Butterfly theme\n\nmodule.exports = {\n  nav: {\n    logo: null,\n    display_title: true,\n    display_post_title: true,\n    fixed: false\n  },\n  menu: null,\n  code_blocks: {\n    theme: 'light',\n    macStyle: false,\n    height_limit: false,\n    word_wrap: false,\n    copy: true,\n    language: true,\n    shrink: false,\n    fullpage: false\n  },\n  social: null,\n  favicon: '/img/favicon.png',\n  avatar: {\n    img: '/img/butterfly-icon.png',\n    effect: false\n  },\n  disable_top_img: false,\n  default_top_img: null,\n  index_img: null,\n  archive_img: null,\n  tag_img: null,\n  tag_per_img: null,\n  category_img: null,\n  category_per_img: null,\n  footer_img: false,\n  background: null,\n  cover: {\n    index_enable: true,\n    aside_enable: true,\n    archives_enable: true,\n    default_cover: null\n  },\n  error_img: {\n    flink: '/img/friend_404.gif',\n    post_page: '/img/404.jpg'\n  },\n  error_404: {\n    enable: false,\n    subtitle: 'Page Not Found',\n    background: '/img/error-page.png'\n  },\n  post_meta: {\n    page: {\n      date_type: 'created',\n      date_format: 'date',\n      categories: true,\n      tags: false,\n      label: true\n    },\n    post: {\n      position: 'left',\n      date_type: 'both',\n      date_format: 'date',\n      categories: true,\n      tags: true,\n      label: true\n    }\n  },\n  index_site_info_top: null,\n  index_top_img_height: null,\n  subtitle: {\n    enable: false,\n    effect: true,\n    typed_option: null,\n    source: false,\n    sub: null\n  },\n  index_layout: 3,\n  index_post_content: {\n    method: 3,\n    length: 500\n  },\n  toc: {\n    post: true,\n    page: false,\n    number: true,\n    expand: false,\n    style_simple: false,\n    scroll_percent: true\n  },\n  post_copyright: {\n    enable: true,\n    decode: false,\n    author_href: null,\n    license: 'CC BY-NC-SA 4.0',\n    license_url: 'https://creativecommons.org/licenses/by-nc-sa/4.0/'\n  },\n  reward: {\n    enable: false,\n    text: null,\n    QR_code: null\n  },\n  post_edit: {\n    enable: false,\n    url: null\n  },\n  related_post: {\n    enable: true,\n    limit: 6,\n    date_type: 'created'\n  },\n  post_pagination: 1,\n  noticeOutdate: {\n    enable: false,\n    style: 'flat',\n    limit_day: 365,\n    position: 'top',\n    message_prev: 'It has been',\n    message_next: 'days since the last update, the content of the article may be outdated.'\n  },\n  footer: {\n    nav: null,\n    owner: {\n      enable: true,\n      since: 2025\n    },\n    copyright: {\n      enable: true,\n      version: true\n    },\n    custom_text: null\n  },\n  aside: {\n    enable: true,\n    hide: false,\n    button: true,\n    mobile: true,\n    position: 'right',\n    display: {\n      archive: true,\n      tag: true,\n      category: true\n    },\n    card_author: {\n      enable: true,\n      description: null,\n      button: {\n        enable: true,\n        icon: 'fab fa-github',\n        text: 'Follow Me',\n        link: 'https://github.com/xxxxxx'\n      }\n    },\n    card_announcement: {\n      enable: true,\n      content: 'This is my Blog'\n    },\n    card_recent_post: {\n      enable: true,\n      limit: 5,\n      sort: 'date',\n      sort_order: null\n    },\n    card_newest_comments: {\n      enable: false,\n      sort_order: null,\n      limit: 6,\n      storage: 10,\n      avatar: true\n    },\n    card_categories: {\n      enable: true,\n      limit: 8,\n      expand: 'none',\n      sort_order: null\n    },\n    card_tags: {\n      enable: true,\n      limit: 40,\n      color: false,\n      custom_colors: null,\n      orderby: 'random',\n      order: 1,\n      sort_order: null\n    },\n    card_archives: {\n      enable: true,\n      type: 'monthly',\n      format: 'MMMM YYYY',\n      order: -1,\n      limit: 8,\n      sort_order: null\n    },\n    card_post_series: {\n      enable: true,\n      series_title: false,\n      orderBy: 'date',\n      order: -1\n    },\n    card_webinfo: {\n      enable: true,\n      post_count: true,\n      last_push_date: true,\n      sort_order: null,\n      runtime_date: null\n    }\n  },\n  rightside_bottom: null,\n  translate: {\n    enable: false,\n    default: '繁',\n    defaultEncoding: 2,\n    translateDelay: 0,\n    msgToTraditionalChinese: '繁',\n    msgToSimplifiedChinese: '簡'\n  },\n  readmode: true,\n  darkmode: {\n    enable: true,\n    button: true,\n    autoChangeMode: false,\n    start: null,\n    end: null\n  },\n  rightside_scroll_percent: false,\n  rightside_item_order: {\n    enable: false,\n    hide: null,\n    show: null\n  },\n  rightside_config_animation: true,\n  anchor: {\n    auto_update: false,\n    click_to_scroll: false\n  },\n  photofigcaption: false,\n  copy: {\n    enable: true,\n    copyright: {\n      enable: false,\n      limit_count: 150\n    }\n  },\n  wordcount: {\n    enable: false,\n    post_wordcount: true,\n    min2read: true,\n    total_wordcount: true\n  },\n  busuanzi: {\n    site_uv: true,\n    site_pv: true,\n    page_pv: true\n  },\n  math: {\n    use: null,\n    per_page: true,\n    hide_scrollbar: false,\n    mathjax: {\n      enableMenu: true,\n      tags: 'none'\n    },\n    katex: {\n      copy_tex: false\n    }\n  },\n  search: {\n    use: null,\n    placeholder: null,\n    algolia_search: {\n      hitsPerPage: 6\n    },\n    local_search: {\n      preload: false,\n      top_n_per_article: 1,\n      unescape: false,\n      pagination: {\n        enable: false,\n        hitsPerPage: 8\n      },\n      CDN: null\n    },\n    docsearch: {\n      appId: null,\n      apiKey: null,\n      indexName: null,\n      option: null\n    }\n  },\n  share: {\n    use: 'sharejs',\n    sharejs: {\n      sites: 'facebook,x,wechat,weibo,qq'\n    },\n    addtoany: {\n      item: 'facebook,x,wechat,sina_weibo,facebook_messenger,email,copy_link'\n    }\n  },\n  comments: {\n    use: null,\n    text: true,\n    lazyload: false,\n    count: false,\n    card_post_count: false\n  },\n  disqus: {\n    shortname: null,\n    apikey: null\n  },\n  disqusjs: {\n    shortname: null,\n    apikey: null,\n    option: null\n  },\n  livere: {\n    uid: null\n  },\n  gitalk: {\n    client_id: null,\n    client_secret: null,\n    repo: null,\n    owner: null,\n    admin: null,\n    option: null\n  },\n  valine: {\n    appId: null,\n    appKey: null,\n    avatar: 'monsterid',\n    serverURLs: null,\n    bg: null,\n    visitor: false,\n    option: null\n  },\n  waline: {\n    serverURL: null,\n    bg: null,\n    pageview: false,\n    option: null\n  },\n  utterances: {\n    repo: null,\n    issue_term: 'pathname',\n    light_theme: 'github-light',\n    dark_theme: 'photon-dark',\n    js: null,\n    option: null\n  },\n  facebook_comments: {\n    app_id: null,\n    user_id: null,\n    pageSize: 10,\n    order_by: 'social',\n    lang: 'en_US'\n  },\n  twikoo: {\n    envId: null,\n    region: null,\n    visitor: false,\n    option: null\n  },\n  giscus: {\n    repo: null,\n    repo_id: null,\n    category_id: null,\n    light_theme: 'light',\n    dark_theme: 'dark',\n    js: null,\n    option: null\n  },\n  remark42: {\n    host: null,\n    siteId: null,\n    option: null\n  },\n  artalk: {\n    server: null,\n    site: null,\n    visitor: false,\n    option: null\n  },\n  chat: {\n    use: null,\n    rightside_button: false,\n    button_hide_show: false\n  },\n  chatra: {\n    id: null\n  },\n  tidio: {\n    public_key: null\n  },\n  crisp: {\n    website_id: null\n  },\n  google_tag_manager: {\n    tag_id: null,\n    domain: 'https://www.googletagmanager.com'\n  },\n  baidu_analytics: null,\n  google_analytics: null,\n  cloudflare_analytics: null,\n  microsoft_clarity: null,\n  umami_analytics: {\n    enable: false,\n    serverURL: null,\n    script_name: 'script.js',\n    website_id: null,\n    option: null,\n    UV_PV: {\n      site_uv: false,\n      site_pv: false,\n      page_pv: false,\n      token: null\n    }\n  },\n  google_adsense: {\n    enable: false,\n    auto_ads: true,\n    js: 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js',\n    client: null,\n    enable_page_level_ads: true\n  },\n  ad: {\n    index: null,\n    aside: null,\n    post: null\n  },\n  site_verification: null,\n  category_ui: null,\n  tag_ui: null,\n  rounded_corners_ui: true,\n  text_align_justify: false,\n  mask: {\n    header: true,\n    footer: true\n  },\n  preloader: {\n    enable: false,\n    source: 1,\n    pace_css_url: null\n  },\n  enter_transitions: true,\n  display_mode: 'light',\n  beautify: {\n    enable: false,\n    field: 'post',\n    title_prefix_icon: null,\n    title_prefix_icon_color: null\n  },\n  font: {\n    global_font_size: null,\n    code_font_size: null,\n    font_family: null,\n    code_font_family: null\n  },\n  blog_title_font: {\n    font_link: null,\n    font_family: null\n  },\n  hr_icon: {\n    enable: true,\n    icon: null,\n    icon_top: null\n  },\n  activate_power_mode: {\n    enable: false,\n    colorful: true,\n    shake: true,\n    mobile: false\n  },\n  canvas_ribbon: {\n    enable: false,\n    size: 150,\n    alpha: 0.6,\n    zIndex: -1,\n    click_to_change: false,\n    mobile: false\n  },\n  canvas_fluttering_ribbon: {\n    enable: false,\n    mobile: false\n  },\n  canvas_nest: {\n    enable: false,\n    color: '0,0,255',\n    opacity: 0.7,\n    zIndex: -1,\n    count: 99,\n    mobile: false\n  },\n  fireworks: {\n    enable: false,\n    zIndex: 9999,\n    mobile: false\n  },\n  click_heart: {\n    enable: false,\n    mobile: false\n  },\n  clickShowText: {\n    enable: false,\n    text: null,\n    fontSize: '15px',\n    random: false,\n    mobile: false\n  },\n  lightbox: null,\n  series: {\n    enable: false,\n    orderBy: 'title',\n    order: 1,\n    number: true\n  },\n  abcjs: {\n    enable: false,\n    per_page: true\n  },\n  mermaid: {\n    enable: false,\n    code_write: false,\n    theme: {\n      light: 'default',\n      dark: 'dark'\n    },\n    open_in_new_tab: true,\n    zoom_pan: true\n  },\n  chartjs: {\n    enable: false,\n    fontColor: {\n      light: 'rgba(0, 0, 0, 0.8)',\n      dark: 'rgba(255, 255, 255, 0.8)'\n    },\n    borderColor: {\n      light: 'rgba(0, 0, 0, 0.1)',\n      dark: 'rgba(255, 255, 255, 0.2)'\n    },\n    scale_ticks_backdropColor: {\n      light: 'transparent',\n      dark: 'transparent'\n    }\n  },\n  note: {\n    style: 'flat',\n    icons: true,\n    border_radius: 3,\n    light_bg_offset: 0\n  },\n  pjax: {\n    enable: false,\n    exclude: null\n  },\n  aplayerInject: {\n    enable: false,\n    per_page: true\n  },\n  snackbar: {\n    enable: false,\n    position: 'bottom-left',\n    bg_light: '#49b1f5',\n    bg_dark: '#1f1f1f'\n  },\n  instantpage: false,\n  lazyload: {\n    enable: false,\n    native: false,\n    field: 'site',\n    placeholder: null,\n    blur: false\n  },\n  pwa: {\n    enable: false,\n    manifest: null,\n    apple_touch_icon: null,\n    favicon_32_32: null,\n    favicon_16_16: null,\n    mask_icon: null\n  },\n  Open_Graph_meta: {\n    enable: true,\n    option: null\n  },\n  structured_data: {\n    enable: false,\n    alternate_name: null\n  },\n  css_prefix: true,\n  inject: {\n    head: null,\n    bottom: null\n  },\n  CDN: {\n    internal_provider: 'local',\n    third_party_provider: 'jsdelivr',\n    version: true,\n    custom_format: null,\n    option: null\n  }\n}\n"
  },
  {
    "path": "scripts/common/postDesc.js",
    "content": "'use strict'\r\n\r\nconst { stripHTML, truncate } = require('hexo-util')\r\n\r\n// Truncates the given content to a specified length, removing HTML tags and replacing newlines with spaces.\r\nconst truncateContent = (content, length, encrypt = false) => {\r\n  if (!content || encrypt) return ''\r\n  return truncate(stripHTML(content).replace(/\\n/g, ' '), { length })\r\n}\r\n\r\n// Generates a post description based on the provided data and theme configuration.\r\nconst postDesc = (data, hexo) => {\r\n  const { description, content, postDesc, encrypt } = data\r\n\r\n  if (postDesc) return postDesc\r\n\r\n  const { length, method } = hexo.theme.config.index_post_content\r\n\r\n  if (method === false) return\r\n\r\n  let result\r\n  switch (method) {\r\n    case 1:\r\n      result = description\r\n      break\r\n    case 2:\r\n      result = description || truncateContent(content, length, encrypt)\r\n      break\r\n    default:\r\n      result = truncateContent(content, length, encrypt)\r\n  }\r\n\r\n  data.postDesc = result\r\n  return result\r\n}\r\n\r\nmodule.exports = { truncateContent, postDesc }\r\n"
  },
  {
    "path": "scripts/events/404.js",
    "content": "/**\r\n * Butterfly\r\n * 404 error page\r\n */\r\n\r\n'use strict'\r\n\r\nhexo.extend.generator.register('404', function (locals) {\r\n  if (!hexo.theme.config.error_404.enable) return\r\n  return {\r\n    path: '404.html',\r\n    layout: ['page'],\r\n    data: {\r\n      type: '404',\r\n      top_img: false,\r\n      comments: false,\r\n      aside: false\r\n    }\r\n  }\r\n})\r\n"
  },
  {
    "path": "scripts/events/cdn.js",
    "content": "/**\r\n * Butterfly\r\n * Merge CDN\r\n */\r\n\r\n'use strict'\r\n\r\nconst { version } = require('../../package.json')\r\nconst path = require('path')\r\n\r\nhexo.extend.filter.register('before_generate', () => {\r\n  const themeConfig = hexo.theme.config\r\n  const { CDN } = themeConfig\r\n\r\n  const thirdPartySrc = hexo.render.renderSync({ path: path.join(hexo.theme_dir, '/plugins.yml'), engine: 'yaml' })\r\n  const internalSrc = {\r\n    main: {\r\n      name: 'hexo-theme-butterfly',\r\n      file: 'js/main.js',\r\n      version\r\n    },\r\n    utils: {\r\n      name: 'hexo-theme-butterfly',\r\n      file: 'js/utils.js',\r\n      version\r\n    },\r\n    translate: {\r\n      name: 'hexo-theme-butterfly',\r\n      file: 'js/tw_cn.js',\r\n      version\r\n    },\r\n    local_search: {\r\n      name: 'hexo-theme-butterfly',\r\n      file: 'js/search/local-search.js',\r\n      version\r\n    },\r\n    algolia_js: {\r\n      name: 'hexo-theme-butterfly',\r\n      file: 'js/search/algolia.js',\r\n      version\r\n    }\r\n  }\r\n\r\n  const minFile = file => {\r\n    return file.replace(/(?<!\\.min)\\.(js|css)$/g, ext => '.min' + ext)\r\n  }\r\n\r\n  const createCDNLink = (data, type, cond = '') => {\r\n    return Object.keys(data).reduce((result, key) => {\r\n      let { name, version, file, other_name: otherName } = data[key]\r\n      const cdnjsName = otherName || name\r\n      const cdnjsFile = file.replace(/^[lib|dist]*\\/|browser\\//g, '')\r\n      const minCdnjsFile = minFile(cdnjsFile)\r\n      if (cond === 'internal') file = `source/${file}`\r\n      const minFilePath = minFile(file)\r\n      const verType = CDN.version ? (type === 'local' ? `?v=${version}` : `@${version}`) : ''\r\n\r\n      const value = {\r\n        version,\r\n        name,\r\n        file,\r\n        cdnjs_file: cdnjsFile,\r\n        min_file: minFilePath,\r\n        min_cdnjs_file: minCdnjsFile,\r\n        cdnjs_name: cdnjsName\r\n      }\r\n\r\n      const cdnSource = {\r\n        local: cond === 'internal' ? `${cdnjsFile + verType}` : `/pluginsSrc/${name}/${file + verType}`,\r\n        jsdelivr: `https://cdn.jsdelivr.net/npm/${name}${verType}/${minFilePath}`,\r\n        unpkg: `https://unpkg.com/${name}${verType}/${file}`,\r\n        cdnjs: `https://cdnjs.cloudflare.com/ajax/libs/${cdnjsName}/${version}/${minCdnjsFile}`,\r\n        custom: (CDN.custom_format || '').replace(/\\$\\{(.+?)\\}/g, (match, $1) => value[$1])\r\n      }\r\n\r\n      result[key] = cdnSource[type]\r\n      return result\r\n    }, cond === 'internal' ? { main_css: 'css/index.css' + (CDN.version ? `?v=${version}` : '') } : {})\r\n  }\r\n\r\n  // delete null value\r\n  const deleteNullValue = obj => {\r\n    if (!obj) return {}\r\n    for (const i in obj) {\r\n      if (obj[i] === null) delete obj[i]\r\n    }\r\n    return obj\r\n  }\r\n\r\n  themeConfig.asset = Object.assign(\r\n    createCDNLink(internalSrc, CDN.internal_provider, 'internal'),\r\n    createCDNLink(thirdPartySrc, CDN.third_party_provider),\r\n    deleteNullValue(CDN.option)\r\n  )\r\n})\r\n"
  },
  {
    "path": "scripts/events/init.js",
    "content": "const { deepMerge } = require('hexo-util')\nconst path = require('path')\n\n// Cache default config to avoid repeated file reads\nlet cachedDefaultConfig = null\n\n/**\n * Check Hexo version and configuration\n */\nfunction checkHexoEnvironment (hexo) {\n  const { version, log, locals } = hexo\n\n  const [major, minor] = version.split('.').map(Number)\n  const requiredMajor = 5\n  const requiredMinor = 3\n\n  if (major < requiredMajor || (major === requiredMajor && minor < requiredMinor)) {\n    log.error('Please update Hexo to V5.3.0 or higher!')\n    log.error('請把 Hexo 升級到 V5.3.0 或更高的版本！')\n    throw new Error('Hexo version too old')\n  }\n\n  // Check for deprecated configuration file\n  if (locals.get) {\n    const data = locals.get('data')\n    if (data && data.butterfly) {\n      log.error(\"'butterfly.yml' is deprecated. Please use '_config.butterfly.yml'\")\n      log.error(\"'butterfly.yml' 已經棄用，請使用 '_config.butterfly.yml'\")\n      throw new Error('Deprecated configuration file')\n    }\n  }\n}\n\n/**\n * Load default configuration\n */\nfunction loadDefaultConfig () {\n  if (cachedDefaultConfig) {\n    return cachedDefaultConfig\n  }\n\n  const configPath = path.join(__dirname, '../common/default_config.js')\n  cachedDefaultConfig = require(configPath)\n  return cachedDefaultConfig\n}\n\n/**\n * Process comment system configuration\n */\nfunction processCommentConfig (themeConfig) {\n  const { comments } = themeConfig\n  if (!comments || !comments.use) {\n    return\n  }\n\n  let { use } = comments\n\n  if (!Array.isArray(use)) {\n    use = typeof use === 'string' ? use.split(',') : [use]\n  }\n\n  use = use\n    .map(item => {\n      if (typeof item !== 'string') return item\n      return item.trim().toLowerCase().replace(/\\b[a-z]/g, s => s.toUpperCase())\n    })\n    .filter(Boolean)\n\n  // Handle Disqus and Disqusjs conflict\n  if (use.includes('Disqus') && use.includes('Disqusjs')) {\n    hexo.log.warn('Disqus and Disqusjs conflict detected, keeping only the first one')\n    hexo.log.warn('檢測到 Disqus 和 Disqusjs 衝突，只保留第一個')\n    use = [use[0]]\n  }\n\n  themeConfig.comments.use = use\n}\n\nhexo.extend.filter.register('before_generate', () => {\n  checkHexoEnvironment(hexo)\n\n  const defaultConfig = loadDefaultConfig()\n  hexo.theme.config = deepMerge(defaultConfig, hexo.theme.config)\n\n  processCommentConfig(hexo.theme.config)\n}, 1)\n"
  },
  {
    "path": "scripts/events/stylus.js",
    "content": "/**\r\n * Stylus renderer\r\n */\r\n\r\n'use strict'\r\n\r\nhexo.extend.filter.register('stylus:renderer', style => {\r\n  const { syntax_highlighter: syntaxHighlighter, highlight, prismjs } = hexo.config\r\n  let { enable: highlightEnable, line_number: highlightLineNumber } = highlight\r\n  let { enable: prismjsEnable, line_number: prismjsLineNumber } = prismjs\r\n\r\n  // for hexo > 7.0\r\n  if (syntaxHighlighter) {\r\n    highlightEnable = syntaxHighlighter === 'highlight.js'\r\n    prismjsEnable = syntaxHighlighter === 'prismjs'\r\n  }\r\n\r\n  style.define('$highlight_enable', highlightEnable)\r\n    .define('$highlight_line_number', highlightLineNumber)\r\n    .define('$prismjs_enable', prismjsEnable)\r\n    .define('$prismjs_line_number', prismjsLineNumber)\r\n    .define('$language', hexo.config.language)\r\n  // .import(`${this.source_dir.replace(/\\\\/g, '/')}_data/css/*`)\r\n})\r\n"
  },
  {
    "path": "scripts/events/welcome.js",
    "content": "hexo.on('ready', () => {\r\n  const { version } = require('../../package.json')\r\n  hexo.log.info(`\r\n  ===================================================================\r\n      #####  #    # ##### ##### ###### #####  ###### #      #   #\r\n      #    # #    #   #     #   #      #    # #      #       # #\r\n      #####  #    #   #     #   #####  #    # #####  #        #\r\n      #    # #    #   #     #   #      #####  #      #        #\r\n      #    # #    #   #     #   #      #   #  #      #        #\r\n      #####   ####    #     #   ###### #    # #      ######   #\r\n                            ${version}\r\n  ===================================================================`)\r\n})\r\n"
  },
  {
    "path": "scripts/filters/post_lazyload.js",
    "content": "/**\r\n * Butterfly\r\n * Lazyload filter\r\n * Replace src with data-lazy-src for lazy loading\r\n */\r\n\r\n'use strict'\r\n\r\nconst urlFor = require('hexo-util').url_for.bind(hexo)\r\n\r\nconst lazyload = htmlContent => {\r\n  if (hexo.theme.config.lazyload.native) {\r\n    // Use more precise replacement: only replace img tags in HTML, not content inside script tags\r\n    return htmlContent.replace(/(<img(?![^>]*?\\bloading=)(?:\\s[^>]*?)?>)(?![^<]*<\\/script>)/gi, match => {\r\n      return match.replace(/>$/, ' loading=\\'lazy\\'>')\r\n    })\r\n  }\r\n\r\n  const bg = hexo.theme.config.lazyload.placeholder ? urlFor(hexo.theme.config.lazyload.placeholder) : 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'\r\n\r\n  // Handle src attributes with double quotes, single quotes, or no quotes (unified approach)\r\n  // Matches: src=\"...\" or src='...' or src=... (e.g., after minification by hexo-minify)\r\n  return htmlContent.replace(/(<img(?![^>]*?\\bdata-lazy-src=)(?:\\s[^>]*?)?\\ssrc=)(?:\"([^\"]*)\"|'([^']*)'|([^\\s>]+))(?![^<]*<\\/script>)/gi, (match, prefix, srcDoubleQuote, srcSingleQuote, srcNoQuote) => {\r\n    const src = srcDoubleQuote || srcSingleQuote || srcNoQuote\r\n    return `${prefix}\"${bg}\" data-lazy-src=\"${src}\"`\r\n  })\r\n}\r\n\r\nhexo.extend.filter.register('after_render:html', data => {\r\n  const { enable, field } = hexo.theme.config.lazyload\r\n  if (!enable || field !== 'site') return\r\n  return lazyload(data)\r\n})\r\n\r\nhexo.extend.filter.register('after_post_render', data => {\r\n  const { enable, field } = hexo.theme.config.lazyload\r\n  if (!enable || field !== 'post') return\r\n  data.content = lazyload(data.content)\r\n  return data\r\n})\r\n"
  },
  {
    "path": "scripts/filters/random_cover.js",
    "content": "/**\r\n * Random cover for posts\r\n */\r\n\r\n'use strict'\r\n\r\nhexo.extend.generator.register('post', locals => {\r\n  const imgTestReg = /\\.(png|jpe?g|gif|svg|webp|avif)(\\?.*)?$/i\r\n  const { post_asset_folder: postAssetFolder } = hexo.config\r\n  const { cover: { default_cover: defaultCover } } = hexo.theme.config\r\n\r\n  function * createCoverGenerator () {\r\n    if (!defaultCover) {\r\n      while (true) yield false\r\n    }\r\n    if (!Array.isArray(defaultCover)) {\r\n      while (true) yield defaultCover\r\n    }\r\n\r\n    const coverCount = defaultCover.length\r\n    if (coverCount === 1) {\r\n      while (true) yield defaultCover[0]\r\n    }\r\n\r\n    const maxHistory = Math.min(3, coverCount - 1)\r\n    const history = []\r\n\r\n    while (true) {\r\n      let index\r\n      do {\r\n        index = Math.floor(Math.random() * coverCount)\r\n      } while (history.includes(index))\r\n\r\n      history.push(index)\r\n      if (history.length > maxHistory) history.shift()\r\n\r\n      yield defaultCover[index]\r\n    }\r\n  }\r\n\r\n  const coverGenerator = createCoverGenerator()\r\n\r\n  const handleImg = data => {\r\n    let { cover: coverVal, top_img: topImg, pagination_cover: paginationCover } = data\r\n\r\n    // Add path to top_img and cover if post_asset_folder is enabled\r\n    if (postAssetFolder) {\r\n      if (topImg && topImg.indexOf('/') === -1 && imgTestReg.test(topImg)) {\r\n        data.top_img = `${data.path}${topImg}`\r\n      }\r\n      if (coverVal && coverVal.indexOf('/') === -1 && imgTestReg.test(coverVal)) {\r\n        data.cover = `${data.path}${coverVal}`\r\n      }\r\n      if (paginationCover && paginationCover.indexOf('/') === -1 && imgTestReg.test(paginationCover)) {\r\n        data.pagination_cover = `${data.path}${paginationCover}`\r\n      }\r\n    }\r\n\r\n    if (coverVal === false) return data\r\n\r\n    // If cover is not set, use random cover\r\n    if (!coverVal) {\r\n      const randomCover = coverGenerator.next().value\r\n      data.cover = randomCover\r\n      coverVal = randomCover\r\n    }\r\n\r\n    if (coverVal && (coverVal.indexOf('//') !== -1 || imgTestReg.test(coverVal))) {\r\n      data.cover_type = 'img'\r\n    }\r\n\r\n    return data\r\n  }\r\n\r\n  const posts = locals.posts.sort('date').toArray()\r\n  const { length } = posts\r\n\r\n  return posts.map((post, i) => {\r\n    if (i) post.prev = posts[i - 1]\r\n    if (i < length - 1) post.next = posts[i + 1]\r\n\r\n    post.__post = true\r\n\r\n    return {\r\n      data: handleImg(post),\r\n      layout: 'post',\r\n      path: post.path\r\n    }\r\n  })\r\n})\r\n"
  },
  {
    "path": "scripts/helpers/aside_archives.js",
    "content": "'use strict'\r\n\r\nhexo.extend.helper.register('aside_archives', function (options = {}) {\r\n  const { config, page, site, url_for: urlFor, _p } = this\r\n  const { archive_dir: archiveDir, timezone, language } = config\r\n\r\n  // Destructure and set default options with object destructuring\r\n  const {\r\n    type = 'monthly',\r\n    format = type === 'monthly' ? 'MMMM YYYY' : 'YYYY',\r\n    show_count: showCount = true,\r\n    order = -1,\r\n    limit,\r\n    transform\r\n  } = options\r\n\r\n  // Optimize locale handling\r\n  const lang = toMomentLocale(page.lang || page.language || language)\r\n\r\n  // Early return if no posts\r\n  if (!site.posts.length) return ''\r\n\r\n  const archives = new Map()\r\n  site.posts.forEach(post => {\r\n    const date = post.date\r\n    const year = date.year()\r\n    const month = date.month() + 1\r\n    const key = type === 'yearly' ? year : `${year}-${month}`\r\n\r\n    if (archives.has(key)) {\r\n      archives.get(key).count++\r\n    } else {\r\n      archives.set(key, {\r\n        year,\r\n        month,\r\n        count: 1,\r\n        date // Store date object for later formatting\r\n      })\r\n    }\r\n  })\r\n\r\n  const data = Array.from(archives.values()).sort((a, b) => {\r\n    if (order === -1) {\r\n      return b.year - a.year || b.month - a.month\r\n    }\r\n    return a.year - b.year || a.month - b.month\r\n  })\r\n\r\n  // Format names after aggregation\r\n  data.forEach(item => {\r\n    let date = item.date.clone()\r\n    if (timezone) date = date.tz(timezone)\r\n    if (lang) date = date.locale(lang)\r\n    item.name = date.format(format)\r\n    delete item.date // Clean up\r\n  })\r\n\r\n  // Create link generator function\r\n  const createArchiveLink = item => {\r\n    let url = `${archiveDir}/${item.year}/`\r\n    if (type === 'monthly') {\r\n      url += item.month < 10 ? `0${item.month}/` : `${item.month}/`\r\n    }\r\n    return urlFor(url)\r\n  }\r\n\r\n  // Limit results efficiently\r\n  const limitedData = limit > 0 ? data.slice(0, Math.min(data.length, limit)) : data\r\n\r\n  // Use template literal for better readability\r\n  const archiveHeader = `\r\n    <div class=\"item-headline\">\r\n      <i class=\"fas fa-archive\"></i>\r\n      <span>${_p('aside.card_archives')}</span>\r\n      ${\r\n        data.length > limitedData.length\r\n          ? `<a class=\"card-more-btn\" href=\"${urlFor(archiveDir)}/\"\r\n            title=\"${_p('aside.more_button')}\">\r\n            <i class=\"fas fa-angle-right\"></i>\r\n          </a>`\r\n          : ''\r\n      }\r\n    </div>\r\n  `\r\n\r\n  // Use map for generating list items, join for performance\r\n  const archiveList = `\r\n    <ul class=\"card-archive-list\">\r\n      ${limitedData\r\n        .map(\r\n          item => `\r\n        <li class=\"card-archive-list-item\">\r\n          <a class=\"card-archive-list-link\" href=\"${createArchiveLink(item)}\">\r\n            <span class=\"card-archive-list-date\">\r\n              ${transform ? transform(item.name) : item.name}\r\n            </span>\r\n            ${showCount ? `<span class=\"card-archive-list-count\">${item.count}</span>` : ''}\r\n          </a>\r\n        </li>\r\n      `\r\n        )\r\n        .join('')}\r\n    </ul>\r\n  `\r\n\r\n  return archiveHeader + archiveList\r\n})\r\n\r\n// Improved locale conversion function\r\nconst toMomentLocale = lang => {\r\n  if (!lang || ['en', 'default'].includes(lang)) return 'en'\r\n  return lang.toLowerCase().replace('_', '-')\r\n}\r\n"
  },
  {
    "path": "scripts/helpers/aside_categories.js",
    "content": "'use strict'\r\n\r\nhexo.extend.helper.register('aside_categories', function (categories, options = {}) {\r\n  if (!categories || !Object.prototype.hasOwnProperty.call(categories, 'length')) {\r\n    options = categories || {}\r\n    categories = this.site.categories\r\n  }\r\n\r\n  if (!categories || !categories.length) return ''\r\n\r\n  const { config } = this\r\n  const showCount = Object.prototype.hasOwnProperty.call(options, 'show_count') ? options.show_count : true\r\n  const depth = options.depth ? parseInt(options.depth, 10) : 0\r\n  const orderby = options.orderby || 'name'\r\n  const order = options.order || 1\r\n  const categoryDir = this.url_for(config.category_dir)\r\n  const limit = options.limit === 0 ? categories.length : (options.limit || categories.length)\r\n  const isExpand = options.expand !== 'none'\r\n  const expandClass = isExpand && options.expand === true ? 'expand' : ''\r\n  const buttonLabel = this._p('aside.more_button')\r\n\r\n  const categoryMap = new Map()\r\n  categories.forEach(cat => {\r\n    if (cat.length) {\r\n      const parentId = cat.parent || 'root'\r\n      if (!categoryMap.has(parentId)) {\r\n        categoryMap.set(parentId, [])\r\n      }\r\n      categoryMap.get(parentId).push(cat)\r\n    }\r\n  })\r\n\r\n  const sortFn = (a, b) => {\r\n    const valA = a[orderby]\r\n    const valB = b[orderby]\r\n    if (valA < valB) return -order\r\n    if (valA > valB) return order\r\n    return 0\r\n  }\r\n\r\n  for (const list of categoryMap.values()) {\r\n    list.sort(sortFn)\r\n  }\r\n\r\n  const hierarchicalList = (remaining, level = 0, parentId = 'root') => {\r\n    let result = ''\r\n    if (remaining > 0 && categoryMap.has(parentId)) {\r\n      categoryMap.get(parentId).forEach(cat => {\r\n        if (remaining > 0) {\r\n          remaining -= 1\r\n          let child = ''\r\n          if (!depth || level + 1 < depth) {\r\n            const childList = hierarchicalList(remaining, level + 1, cat._id)\r\n            child = childList.result\r\n            remaining = childList.remaining\r\n          }\r\n\r\n          const isTopLevel = parentId === 'root'\r\n          const parentClass = isExpand && isTopLevel && child ? 'parent' : ''\r\n          result += `<li class=\"card-category-list-item ${parentClass}\">`\r\n          result += `<a class=\"card-category-list-link\" href=\"${this.url_for(cat.path)}\">`\r\n          result += `<span class=\"card-category-list-name\">${cat.name}</span>`\r\n\r\n          if (showCount) {\r\n            result += `<span class=\"card-category-list-count\">${cat.length}</span>`\r\n          }\r\n\r\n          if (isExpand && isTopLevel && child) {\r\n            result += `<i class=\"fas fa-caret-left ${expandClass}\"></i>`\r\n          }\r\n\r\n          result += '</a>'\r\n\r\n          if (child) {\r\n            result += `<ul class=\"card-category-list child\">${child}</ul>`\r\n          }\r\n\r\n          result += '</li>'\r\n        }\r\n      })\r\n    }\r\n    return { result, remaining }\r\n  }\r\n\r\n  const list = hierarchicalList(limit)\r\n\r\n  const moreButton = categories.length > limit\r\n    ? `<a class=\"card-more-btn\" href=\"${categoryDir}/\" title=\"${buttonLabel}\">\r\n      <i class=\"fas fa-angle-right\"></i></a>`\r\n    : ''\r\n\r\n  return `<div class=\"item-headline\">\r\n            <i class=\"fas fa-folder-open\"></i>\r\n            <span>${this._p('aside.card_categories')}</span>\r\n            ${moreButton}\r\n          </div>\r\n          <ul class=\"card-category-list${isExpand && list.result ? ' expandBtn' : ''}\" id=\"aside-cat-list\">\r\n            ${list.result}\r\n          </ul>`\r\n})\r\n"
  },
  {
    "path": "scripts/helpers/getArchiveLength.js",
    "content": "hexo.extend.helper.register('getArchiveLength', function () {\r\n  const archiveGenerator = hexo.config.archive_generator\r\n  const posts = this.site.posts\r\n\r\n  const { yearly, monthly, daily } = archiveGenerator\r\n  const { year, month, day } = this.page\r\n\r\n  // Archives Page\r\n  if (!year) return posts.length\r\n\r\n  // Create a map to count posts per period\r\n  const mapData = this.fragment_cache('createArchiveObj', () => {\r\n    const map = new Map()\r\n    posts.forEach(post => {\r\n      const date = post.date\r\n      const y = date.year()\r\n      const m = date.month() + 1\r\n      const d = date.date()\r\n\r\n      if (yearly) {\r\n        const keyYear = `${y}`\r\n        map.set(keyYear, (map.get(keyYear) || 0) + 1)\r\n      }\r\n\r\n      if (monthly) {\r\n        const keyMonth = `${y}-${m}`\r\n        map.set(keyMonth, (map.get(keyMonth) || 0) + 1)\r\n      }\r\n\r\n      if (daily) {\r\n        const keyDay = `${y}-${m}-${d}`\r\n        map.set(keyDay, (map.get(keyDay) || 0) + 1)\r\n      }\r\n    })\r\n    return map\r\n  })\r\n\r\n  // Determine the appropriate key to fetch based on current page context\r\n  let key\r\n  if (yearly && year) key = `${year}`\r\n  if (monthly && month) key = `${year}-${month}`\r\n  if (daily && day) key = `${year}-${month}-${day}`\r\n\r\n  // Return the count for the current period or default to the total posts\r\n  return mapData.get(key) || posts.length\r\n})\r\n"
  },
  {
    "path": "scripts/helpers/inject_head_js.js",
    "content": "'use strict'\r\n\r\nhexo.extend.helper.register('inject_head_js', function () {\r\n  const { darkmode, aside, pjax } = this.theme\r\n  const start = darkmode.start || 6\r\n  const end = darkmode.end || 18\r\n  const { theme_color: themeColor } = hexo.theme.config\r\n  const themeColorLight = themeColor && themeColor.enable ? themeColor.meta_theme_color_light : '#ffffff'\r\n  const themeColorDark = themeColor && themeColor.enable ? themeColor.meta_theme_color_dark : '#0d0d0d'\r\n\r\n  const createCustomJs = () => `\r\n    const saveToLocal = {\r\n      set: (key, value, ttl) => {\r\n        if (!ttl) return\r\n        const expiry = Date.now() + ttl * 86400000\r\n        localStorage.setItem(key, JSON.stringify({ value, expiry }))\r\n      },\r\n      get: key => {\r\n        const itemStr = localStorage.getItem(key)\r\n        if (!itemStr) return undefined\r\n        const { value, expiry } = JSON.parse(itemStr)\r\n        if (Date.now() > expiry) {\r\n          localStorage.removeItem(key)\r\n          return undefined\r\n        }\r\n        return value\r\n      }\r\n    }\r\n\r\n    window.btf = {\r\n      saveToLocal,\r\n      getScript: (url, attr = {}) => new Promise((resolve, reject) => {\r\n        const script = document.createElement('script')\r\n        script.src = url\r\n        script.async = true\r\n        Object.entries(attr).forEach(([key, val]) => script.setAttribute(key, val))\r\n        script.onload = script.onreadystatechange = () => {\r\n          if (!script.readyState || /loaded|complete/.test(script.readyState)) resolve()\r\n        }\r\n        script.onerror = reject\r\n        document.head.appendChild(script)\r\n      }),\r\n      getCSS: (url, id) => new Promise((resolve, reject) => {\r\n        const link = document.createElement('link')\r\n        link.rel = 'stylesheet'\r\n        link.href = url\r\n        if (id) link.id = id\r\n        link.onload = link.onreadystatechange = () => {\r\n          if (!link.readyState || /loaded|complete/.test(link.readyState)) resolve()\r\n        }\r\n        link.onerror = reject\r\n        document.head.appendChild(link)\r\n      }),\r\n      addGlobalFn: (key, fn, name = false, parent = window) => {\r\n        if (!${pjax.enable} && key.startsWith('pjax')) return\r\n        const globalFn = parent.globalFn || {}\r\n        globalFn[key] = globalFn[key] || {}\r\n        globalFn[key][name || Object.keys(globalFn[key]).length] = fn\r\n        parent.globalFn = globalFn\r\n      }\r\n    }\r\n  `\r\n\r\n  const createDarkmodeJs = () => {\r\n    if (!darkmode.enable) return ''\r\n\r\n    let darkmodeJs = `\r\n      const activateDarkMode = () => {\r\n        document.documentElement.setAttribute('data-theme', 'dark')\r\n        if (document.querySelector('meta[name=\"theme-color\"]') !== null) {\r\n          document.querySelector('meta[name=\"theme-color\"]').setAttribute('content', '${themeColorDark}')\r\n        }\r\n      }\r\n      const activateLightMode = () => {\r\n        document.documentElement.setAttribute('data-theme', 'light')\r\n        if (document.querySelector('meta[name=\"theme-color\"]') !== null) {\r\n          document.querySelector('meta[name=\"theme-color\"]').setAttribute('content', '${themeColorLight}')\r\n        }\r\n      }\r\n\r\n      btf.activateDarkMode = activateDarkMode\r\n      btf.activateLightMode = activateLightMode\r\n\r\n      const theme = saveToLocal.get('theme')\r\n    `\r\n\r\n    switch (darkmode.autoChangeMode) {\r\n      case 1:\r\n        darkmodeJs += `\r\n          const mediaQueryDark = window.matchMedia('(prefers-color-scheme: dark)')\r\n          const mediaQueryLight = window.matchMedia('(prefers-color-scheme: light)')\r\n\r\n          if (theme === undefined) {\r\n            if (mediaQueryLight.matches) activateLightMode()\r\n            else if (mediaQueryDark.matches) activateDarkMode()\r\n            else {\r\n              const hour = new Date().getHours()\r\n              const isNight = hour <= ${start} || hour >= ${end}\r\n              isNight ? activateDarkMode() : activateLightMode()\r\n            }\r\n            mediaQueryDark.addEventListener('change', () => {\r\n              if (saveToLocal.get('theme') === undefined) {\r\n                e.matches ? activateDarkMode() : activateLightMode()\r\n              }\r\n            })\r\n          } else {\r\n            theme === 'light' ? activateLightMode() : activateDarkMode()\r\n          }\r\n        `\r\n        break\r\n      case 2:\r\n        darkmodeJs += `\r\n          const hour = new Date().getHours()\r\n          const isNight = hour <= ${start} || hour >= ${end}\r\n          if (theme === undefined) isNight ? activateDarkMode() : activateLightMode()\r\n          else theme === 'light' ? activateLightMode() : activateDarkMode()\r\n        `\r\n        break\r\n      default:\r\n        darkmodeJs += `\r\n          theme === 'dark' ? activateDarkMode() : theme === 'light' ? activateLightMode() : null\r\n        `\r\n    }\r\n\r\n    return darkmodeJs\r\n  }\r\n\r\n  const createAsideStatusJs = () => {\r\n    if (!aside.enable || !aside.button) return ''\r\n    return `\r\n      const asideStatus = saveToLocal.get('aside-status')\r\n      if (asideStatus !== undefined) {\r\n        document.documentElement.classList.toggle('hide-aside', asideStatus === 'hide')\r\n      }\r\n    `\r\n  }\r\n\r\n  const createDetectAppleJs = () => `\r\n    const detectApple = () => {\r\n      if (/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)) {\r\n        document.documentElement.classList.add('apple')\r\n      }\r\n    }\r\n    detectApple()\r\n  `\r\n\r\n  return `<script>\r\n    (() => {\r\n      ${createCustomJs()}\r\n      ${createDarkmodeJs()}\r\n      ${createAsideStatusJs()}\r\n      ${createDetectAppleJs()}\r\n    })()\r\n  </script>`\r\n})\r\n"
  },
  {
    "path": "scripts/helpers/page.js",
    "content": "'use strict'\r\n\r\nconst { truncateContent, postDesc } = require('../common/postDesc')\r\nconst { prettyUrls } = require('hexo-util')\r\nconst crypto = require('crypto')\r\nconst moment = require('moment-timezone')\r\n\r\nconst absoluteUrlPattern = /^(?:[a-z][a-z\\d+.-]*:)?\\/\\//i\r\nconst relativeUrlPattern = /^(\\.\\/|\\.\\.\\/|\\/|[^/]+\\/).*$/\r\nconst colorPattern = /^(#|rgb|rgba|hsl|hsla)/i\r\nconst simpleFilePattern = /\\.(png|jpg|jpeg|gif|bmp|webp|svg|tiff)$/i\r\nconst archiveRegex = /\\/archives\\//\r\n\r\nhexo.extend.helper.register('truncate', truncateContent)\r\n\r\nhexo.extend.helper.register('postDesc', data => {\r\n  return postDesc(data, hexo)\r\n})\r\n\r\nhexo.extend.helper.register('cloudTags', function (options = {}) {\r\n  const env = this\r\n  let { source, minfontsize, maxfontsize, limit, unit = 'px', orderby, order, page = 'tags', custom_colors } = options\r\n\r\n  if (limit > 0) {\r\n    source = source.limit(limit)\r\n  }\r\n\r\n  const sizes = [...new Set(source.map(tag => tag.length).sort((a, b) => a - b))]\r\n  const sizeMap = new Map(sizes.map((size, index) => [size, index]))\r\n  const length = sizes.length - 1\r\n\r\n  const getRandomColor = () => {\r\n    const r = Math.floor(Math.random() * 201)\r\n    const g = Math.floor(Math.random() * 201)\r\n    const b = Math.floor(Math.random() * 201)\r\n    return `rgb(${Math.max(r, 50)}, ${Math.max(g, 50)}, ${Math.max(b, 50)})`\r\n  }\r\n\r\n  const normalizeColors = input => {\r\n    if (!input) return null\r\n    if (typeof input === 'string') {\r\n      const color = input.trim()\r\n      return color ? [color] : null\r\n    }\r\n    if (Array.isArray(input)) {\r\n      const result = []\r\n      for (let i = 0; i < input.length; i++) {\r\n        const value = input[i]\r\n        if (value === null || value === undefined) continue\r\n        const color = String(value).trim()\r\n        if (!color) continue\r\n        result.push(color)\r\n      }\r\n      return result.length ? result : null\r\n    }\r\n    return null\r\n  }\r\n\r\n  const userColors = normalizeColors(custom_colors)\r\n\r\n  const resolveColorClass = (idx) => `tag-color-${idx % userColors.length}`\r\n\r\n  const generateStyle = (size, unit, page, color) => {\r\n    const colorStyle = page === 'tags' ? `background-color: ${color};` : `color: ${color};`\r\n    return `font-size: ${parseFloat(size.toFixed(2))}${unit}; ${colorStyle}`\r\n  }\r\n\r\n  return source.sort(orderby, order).map((tag, idx) => {\r\n    const ratio = length ? sizeMap.get(tag.length) / length : 0\r\n    const size = minfontsize + ((maxfontsize - minfontsize) * ratio)\r\n\r\n    if (userColors && userColors.length) {\r\n      const colorClass = resolveColorClass(idx)\r\n      const color = userColors[idx % userColors.length]\r\n      const style = generateStyle(size, unit, page, color)\r\n      return `<a href=\"${env.url_for(tag.path)}\" class=\"tag-cloud-item ${colorClass}\" style=\"${style}\">${tag.name}</a>`\r\n    }\r\n\r\n    const color = getRandomColor()\r\n    const style = generateStyle(size, unit, page, color)\r\n    return `<a href=\"${env.url_for(tag.path)}\" style=\"${style}\">${tag.name}</a>`\r\n  }).join('')\r\n})\r\n\r\nhexo.extend.helper.register('urlNoIndex', function (url = null, trailingIndex = false, trailingHtml = false) {\r\n  return prettyUrls(url || this.url, { trailing_index: trailingIndex, trailing_html: trailingHtml })\r\n})\r\n\r\nhexo.extend.helper.register('md5', function (path) {\r\n  return crypto.createHash('md5').update(decodeURI(this.url_for(path, { relative: false }))).digest('hex')\r\n})\r\n\r\nhexo.extend.helper.register('injectHtml', data => {\r\n  return data ? data.join('') : ''\r\n})\r\n\r\nhexo.extend.helper.register('findArchivesTitle', function (page, menu, date) {\r\n  if (page.year) {\r\n    const dateStr = page.month ? `${page.year}-${page.month}` : `${page.year}`\r\n    const dateFormat = page.month ? hexo.theme.config.aside.card_archives.format : 'YYYY'\r\n    return date(dateStr, dateFormat)\r\n  }\r\n\r\n  const defaultTitle = this._p('page.archives')\r\n  if (!menu) return defaultTitle\r\n\r\n  const loop = m => {\r\n    for (const key in m) {\r\n      if (typeof m[key] === 'object') {\r\n        const result = loop(m[key])\r\n        if (result) return result\r\n      }\r\n\r\n      if (archiveRegex.test(m[key])) {\r\n        return key\r\n      }\r\n    }\r\n  }\r\n\r\n  return loop(menu) || defaultTitle\r\n})\r\n\r\nhexo.extend.helper.register('getBgPath', function (path) {\r\n  if (!path) return ''\r\n\r\n  if (colorPattern.test(path)) {\r\n    return `background-color: ${path};`\r\n  } else if (absoluteUrlPattern.test(path) || relativeUrlPattern.test(path) || simpleFilePattern.test(path)) {\r\n    return `background-image: url(${this.url_for(path)});`\r\n  } else {\r\n    return `background: ${path};`\r\n  }\r\n})\r\n\r\nhexo.extend.helper.register('shuoshuoFN', (data, page) => {\r\n  const { limit } = page\r\n\r\n  // Shallow copy to avoid mutating original data\r\n  let processedData = data.map(item => ({ ...item }))\r\n\r\n  // Check if limit.value is a valid date\r\n  const isValidDate = date => !isNaN(Date.parse(date))\r\n\r\n  // order by date\r\n  processedData.sort((a, b) => Date.parse(b.date) - Date.parse(a.date))\r\n\r\n  // Apply number limit or time limit conditionally\r\n  if (limit && limit.type === 'num' && limit.value > 0) {\r\n    processedData = processedData.slice(0, limit.value)\r\n  } else if (limit && limit.type === 'date' && isValidDate(limit.value)) {\r\n    const limitDate = Date.parse(limit.value)\r\n    processedData = processedData.filter(item => Date.parse(item.date) >= limitDate)\r\n  }\r\n\r\n  // This is a hack method, because hexo treats time as UTC time\r\n  // so you need to manually convert the time zone\r\n  processedData.forEach(item => {\r\n    const utcDate = moment.utc(item.date).format('YYYY-MM-DD HH:mm:ss')\r\n    item.date = moment.tz(utcDate, hexo.config.timezone).format('YYYY-MM-DD HH:mm:ss')\r\n    // markdown\r\n    item.content = hexo.render.renderSync({ text: item.content, engine: 'markdown' })\r\n  })\r\n\r\n  return processedData\r\n})\r\n\r\nhexo.extend.helper.register('getPageType', (page, isHome) => {\r\n  const { layout, tag, category, type, archive } = page\r\n  if (layout) return layout\r\n  if (tag) return 'tag'\r\n  if (category) return 'category'\r\n  if (archive) return 'archive'\r\n  if (type) {\r\n    if (type === 'tags' || type === 'categories') return type\r\n    else return 'page'\r\n  }\r\n  if (isHome) return 'home'\r\n  return 'post'\r\n})\r\n\r\nhexo.extend.helper.register('getVersion', () => {\r\n  const { version } = require('../../package.json')\r\n  return { hexo: hexo.version, theme: version }\r\n})\r\n\r\nhexo.extend.helper.register('safeJSON', data => {\r\n  // Safely serialize JSON for embedding in <script> tags\r\n  return JSON.stringify(data)\r\n    .replace(/</g, '\\\\u003c')\r\n    .replace(/>/g, '\\\\u003e')\r\n    .replace(/\\u2028/g, '\\\\u2028')\r\n    .replace(/\\u2029/g, '\\\\u2029')\r\n})\r\n"
  },
  {
    "path": "scripts/helpers/related_post.js",
    "content": "/* eslint-disable camelcase */\r\n/**\r\n * Butterfly\r\n * Related Posts\r\n * According the tag\r\n */\r\n\r\n'use strict'\r\n\r\nconst { postDesc } = require('../common/postDesc')\r\n\r\nhexo.extend.helper.register('related_posts', function (currentPost) {\r\n  const relatedPosts = new Map()\r\n  const tagsData = currentPost.tags\r\n\r\n  if (!tagsData || !tagsData.length) return ''\r\n\r\n  tagsData.forEach(tag => {\r\n    const posts = tag.posts\r\n    posts.forEach(post => {\r\n      if (currentPost.path === post.path) return\r\n\r\n      if (relatedPosts.has(post.path)) {\r\n        relatedPosts.get(post.path).weight += 1\r\n      } else {\r\n        const getPostDesc = post.postDesc || postDesc(post, hexo)\r\n        relatedPosts.set(post.path, {\r\n          title: post.title,\r\n          path: post.path,\r\n          cover: post.cover,\r\n          cover_type: post.cover_type,\r\n          weight: 1,\r\n          updated: post.updated,\r\n          created: post.date,\r\n          postDesc: getPostDesc,\r\n          random: Math.random()\r\n        })\r\n      }\r\n    })\r\n  })\r\n\r\n  if (relatedPosts.size === 0) {\r\n    return ''\r\n  }\r\n\r\n  const hexoConfig = hexo.config\r\n  const config = hexo.theme.config\r\n\r\n  const limitNum = config.related_post.limit || 6\r\n  const dateType = config.related_post.date_type || 'created'\r\n  const headlineLang = this._p('post.recommend')\r\n\r\n  const relatedPostsList = Array.from(relatedPosts.values()).sort((a, b) => {\r\n    if (b.weight !== a.weight) {\r\n      return b.weight - a.weight\r\n    }\r\n    return b.random - a.random\r\n  })\r\n\r\n  let result = '<div class=\"relatedPosts\">'\r\n  result += `<div class=\"headline\"><i class=\"fas fa-thumbs-up fa-fw\"></i><span>${headlineLang}</span></div>`\r\n  result += '<div class=\"relatedPosts-list\">'\r\n\r\n  for (let i = 0; i < Math.min(relatedPostsList.length, limitNum); i++) {\r\n    let { cover, title, path, cover_type, created, updated, postDesc } = relatedPostsList[i]\r\n    const { escape_html, url_for, date } = this\r\n    cover = cover || 'var(--default-bg-color)'\r\n    title = escape_html(title)\r\n    const className = postDesc ? 'pagination-related' : 'pagination-related no-desc'\r\n    result += `<a class=\"${className}\" href=\"${url_for(path)}\" title=\"${title}\">`\r\n    if (cover_type === 'img') {\r\n      result += `<img class=\"cover\" src=\"${url_for(cover)}\" alt=\"cover\">`\r\n    } else {\r\n      result += `<div class=\"cover\" style=\"background: ${cover}\"></div>`\r\n    }\r\n    if (dateType === 'created') {\r\n      result += `<div class=\"info text-center\"><div class=\"info-1\"><div class=\"info-item-1\"><i class=\"far fa-calendar-alt fa-fw\"></i> ${date(created, hexoConfig.date_format)}</div>`\r\n    } else {\r\n      result += `<div class=\"info text-center\"><div class=\"info-1\"><div class=\"info-item-1\"><i class=\"fas fa-history fa-fw\"></i> ${date(updated, hexoConfig.date_format)}</div>`\r\n    }\r\n    result += `<div class=\"info-item-2\">${title}</div></div>`\r\n\r\n    if (postDesc) {\r\n      result += `<div class=\"info-2\"><div class=\"info-item-1\">${postDesc}</div></div>`\r\n    }\r\n    result += '</div></a>'\r\n  }\r\n\r\n  result += '</div></div>'\r\n  return result\r\n})\r\n"
  },
  {
    "path": "scripts/helpers/series.js",
    "content": "'use strict'\r\n\r\nhexo.extend.helper.register('groupPosts', function () {\r\n  const getGroupArray = array => {\r\n    return array.reduce((groups, item) => {\r\n      const key = item.series\r\n      if (key) {\r\n        groups[key] = groups[key] || []\r\n        groups[key].push(item)\r\n      }\r\n      return groups\r\n    }, {})\r\n  }\r\n\r\n  const sortPosts = posts => {\r\n    const { orderBy = 'date', order = 1 } = this.theme.aside.card_post_series\r\n    if (orderBy === 'title') return posts.sort('title', order)\r\n    return posts.sort('date', order)\r\n  }\r\n\r\n  return getGroupArray(sortPosts(this.site.posts))\r\n})\r\n"
  },
  {
    "path": "scripts/tag/button.js",
    "content": "/**\r\n * Button\r\n * {% btn url text icon option %}\r\n * option: color outline center block larger\r\n * color : default/blue/pink/red/purple/orange/green\r\n */\r\n\r\n'use strict'\r\n\r\nconst urlFor = require('hexo-util').url_for.bind(hexo)\r\n\r\nconst btn = args => {\r\n  const [url = '', text = '', icon = '', option = ''] = args.join(' ').split(',').map(arg => arg.trim())\r\n\r\n  const iconHTML = icon ? `<i class=\"${icon}\"></i>` : ''\r\n  const textHTML = text ? `<span>${text}</span>` : ''\r\n\r\n  return `<a class=\"btn-beautify ${option}\" href=\"${urlFor(url)}\" title=\"${text}\">${iconHTML}${textHTML}</a>`\r\n}\r\n\r\nhexo.extend.tag.register('btn', btn, { ends: false })\r\n"
  },
  {
    "path": "scripts/tag/chartjs.js",
    "content": "/**\r\n * Butterfly\r\n * chartjs\r\n * https://www.chartjs.org/\r\n * {% chartjs [width, abreast, chartId] %}\r\n * <!-- chart -->\r\n * <!-- endchart -->\r\n * <!-- desc -->\r\n * <!-- enddesc -->\r\n * {% endchartjs %}\r\n */\r\n\r\n'use strict'\r\n\r\nconst { escapeHTML } = require('hexo-util')\r\n\r\nconst chartjs = (args, content) => {\r\n  if (!content) return\r\n\r\n  const chartRegex = /<!--\\s*chart\\s*-->\\n([\\w\\W\\s\\S]*?)<!--\\s*endchart\\s*-->/\r\n  const descRegex = /<!--\\s*desc\\s*-->\\n([\\w\\W\\s\\S]*?)<!--\\s*enddesc\\s*-->/\r\n  const selfConfig = args.join(' ').trim()\r\n\r\n  const [width = '', layout = false, chartId = ''] = selfConfig.split(',').map(s => s.trim())\r\n\r\n  const chartMatch = content.match(chartRegex)\r\n  const descMatch = content.match(descRegex)\r\n\r\n  if (!chartMatch) {\r\n    hexo.log.warn('chartjs tag: chart content is required!')\r\n    return\r\n  }\r\n\r\n  const chartConfig = chartMatch && chartMatch[1] ? chartMatch[1] : ''\r\n  const descContent = descMatch && descMatch[1] ? descMatch[1] : ''\r\n\r\n  const renderedDesc = descContent ? hexo.render.renderSync({ text: descContent, engine: 'markdown' }).trim() : ''\r\n\r\n  const descDOM = renderedDesc ? `<div class=\"chartjs-desc\">${renderedDesc}</div>` : ''\r\n  const abreastClass = layout ? ' chartjs-abreast' : ''\r\n  const widthStyle = width ? `data-width=\"${width}%\"` : ''\r\n\r\n  return `<div class=\"chartjs-container${abreastClass}\" data-chartjs-id=\"${chartId}\" ${widthStyle}>\r\n            <pre class=\"chartjs-src\" hidden>${escapeHTML(chartConfig)}</pre>\r\n            ${descDOM}\r\n          </div>`\r\n}\r\n\r\nhexo.extend.tag.register('chartjs', chartjs, { ends: true })\r\n"
  },
  {
    "path": "scripts/tag/flink.js",
    "content": "/**\r\n * flink\r\n */\r\n\r\n'use strict'\r\n\r\nconst urlFor = require('hexo-util').url_for.bind(hexo)\r\n\r\nconst flinkFn = (args, content) => {\r\n  const data = hexo.render.renderSync({ text: content, engine: 'yaml' })\r\n  let result = ''\r\n\r\n  data.forEach(item => {\r\n    const className = item.class_name ? `<div class=\"flink-name\">${item.class_name}</div>` : ''\r\n    const classDesc = item.class_desc ? `<div class=\"flink-desc\">${item.class_desc}</div>` : ''\r\n\r\n    const listResult = item.link_list.map(link => `\r\n      <div class=\"flink-list-item\">\r\n        <a href=\"${link.link}\" title=\"${link.name}\" target=\"_blank\">\r\n          <div class=\"flink-item-icon\">\r\n            <img class=\"no-lightbox\" src=\"${link.avatar}\" onerror='this.onerror=null;this.src=\"${urlFor(hexo.theme.config.error_img.flink)}\"' alt=\"${link.name}\" />\r\n          </div>\r\n          <div class=\"flink-item-name\">${link.name}</div>\r\n          <div class=\"flink-item-desc\" title=\"${link.descr}\">${link.descr}</div>\r\n        </a>\r\n      </div>`).join('')\r\n\r\n    result += `${className}${classDesc}<div class=\"flink-list\">${listResult}</div>`\r\n  })\r\n\r\n  return `<div class=\"flink\">${result}</div>`\r\n}\r\n\r\nhexo.extend.tag.register('flink', flinkFn, { ends: true })\r\n"
  },
  {
    "path": "scripts/tag/gallery.js",
    "content": "/**\r\n * Butterfly\r\n * galleryGroup and gallery\r\n * {% galleryGroup [name] [descr] [url] [img] %}\r\n *\r\n * {% gallery [button],[limit],[firstLimit] %}\r\n * {% gallery url,[url],[button] %}\r\n */\r\n\r\n'use strict'\r\n\r\nconst urlFor = require('hexo-util').url_for.bind(hexo)\r\n\r\nconst DEFAULT_LIMIT = 10\r\nconst DEFAULT_FIRST_LIMIT = 10\r\nconst IMAGE_REGEX = /!\\[(.*?)\\]\\(([^\\s]*)\\s*(?:[\"'](.*?)[\"']?)?\\s*\\)/g\r\n\r\n// Helper functions\r\nconst parseGalleryArgs = args => {\r\n  const [type, ...rest] = args.join(' ').split(',').map(arg => arg.trim())\r\n  return {\r\n    isUrl: type === 'url',\r\n    params: type === 'url' ? rest : [type, ...rest]\r\n  }\r\n}\r\n\r\nconst parseImageContent = content => {\r\n  const images = []\r\n  let match\r\n\r\n  while ((match = IMAGE_REGEX.exec(content)) !== null) {\r\n    images.push({\r\n      url: match[2],\r\n      alt: match[1] || '',\r\n      title: match[3] || ''\r\n    })\r\n  }\r\n\r\n  return images\r\n}\r\n\r\nconst createGalleryHTML = (type, dataStr, button, limit, firstLimit) => {\r\n  return `<div class=\"gallery-container\" data-type=\"${type}\" data-button=\"${button}\" data-limit=\"${limit}\" data-first=\"${firstLimit}\">\r\n    <div class=\"gallery-items\">${dataStr}</div>\r\n  </div>`\r\n}\r\n\r\nconst gallery = (args, content) => {\r\n  const { isUrl, params } = parseGalleryArgs(args)\r\n\r\n  if (isUrl) {\r\n    const [dataStr, button = false, limit = DEFAULT_LIMIT, firstLimit = DEFAULT_FIRST_LIMIT] = params\r\n    return createGalleryHTML('url', urlFor(dataStr), button, limit, firstLimit)\r\n  }\r\n\r\n  const [button = false, limit = DEFAULT_LIMIT, firstLimit = DEFAULT_FIRST_LIMIT] = params\r\n  const images = parseImageContent(content)\r\n  return createGalleryHTML('data', JSON.stringify(images), button, limit, firstLimit)\r\n}\r\n\r\nconst galleryGroup = args => {\r\n  const [name = '', descr = '', url = '', img = ''] = args.map(arg => arg.trim())\r\n\r\n  return `<figure class=\"gallery-group\">\r\n    <img class=\"gallery-group-img no-lightbox\" src='${urlFor(img)}' alt=\"Group Image Gallery\">\r\n    <figcaption>\r\n      <div class=\"gallery-group-name\">${name}</div>\r\n      <p>${descr}</p>\r\n      <a href='${urlFor(url)}'></a>\r\n    </figcaption>\r\n  </figure>`\r\n}\r\n\r\n// Register tags\r\nhexo.extend.tag.register('gallery', gallery, { ends: true })\r\nhexo.extend.tag.register('galleryGroup', galleryGroup)\r\n"
  },
  {
    "path": "scripts/tag/hide.js",
    "content": "/**\r\n * Butterfly\r\n * @example\r\n * hideInline\r\n * {% hideInline content,display,bg,color %}\r\n * content不能包含當引號，可用 &apos;\r\n * hideBlock\r\n * {% hideBlock display,bg,color %}\r\n * content\r\n * {% endhideBlock %}\r\n * hideToggle\r\n * {% hideToggle display,bg,color %}\r\n * content\r\n * {% endhideToggle %}\r\n */\r\n\r\n'use strict'\r\n\r\nconst parseArgs = args => args.join(' ').split(',')\r\n\r\nconst generateStyle = (bg, color) => {\r\n  let style = 'style=\"'\r\n  if (bg) style += `background-color: ${bg};`\r\n  if (color) style += `color: ${color}`\r\n  style += '\"'\r\n  return style\r\n}\r\n\r\nconst hideInline = args => {\r\n  const [content, display = 'Click', bg = false, color = false] = parseArgs(args)\r\n  const style = generateStyle(bg, color)\r\n  return `<span class=\"hide-inline\"><button type=\"button\" class=\"hide-button\" ${style}>${display}</button><span class=\"hide-content\">${content}</span></span>`\r\n}\r\n\r\nconst hideBlock = (args, content) => {\r\n  const [display = 'Click', bg = false, color = false] = parseArgs(args)\r\n  const style = generateStyle(bg, color)\r\n  const renderedContent = hexo.render.renderSync({ text: content, engine: 'markdown' })\r\n  return `<div class=\"hide-block\"><button type=\"button\" class=\"hide-button\" ${style}>${display}</button><div class=\"hide-content\">${renderedContent}</div></div>`\r\n}\r\n\r\nconst hideToggle = (args, content) => {\r\n  const [display, bg = false, color = false] = parseArgs(args)\r\n  const style = generateStyle(bg, color)\r\n  const border = bg ? `style=\"border: 1px solid ${bg}\"` : ''\r\n  const renderedContent = hexo.render.renderSync({ text: content, engine: 'markdown' })\r\n  return `<details class=\"toggle\" ${border}><summary class=\"toggle-button\" ${style}>${display}</summary><div class=\"toggle-content\">${renderedContent}</div></details>`\r\n}\r\n\r\nhexo.extend.tag.register('hideInline', hideInline)\r\nhexo.extend.tag.register('hideBlock', hideBlock, { ends: true })\r\nhexo.extend.tag.register('hideToggle', hideToggle, { ends: true })\r\n"
  },
  {
    "path": "scripts/tag/inlineImg.js",
    "content": "/**\r\n * inlineImg\r\n * @param {Array} args - Image name and height\r\n * @param {string} args[0] - Image name\r\n * @param {number} args[1] - Image height\r\n * @returns {string} - Image tag\r\n */\r\n\r\n'use strict'\r\n\r\nconst urlFor = require('hexo-util').url_for.bind(hexo)\r\n\r\nconst inlineImg = ([img, height = '']) => {\r\n  const heightStyle = height ? `style=\"height:${height}\"` : ''\r\n  const src = urlFor(img)\r\n  return `<img class=\"inline-img\" src=\"${src}\" ${heightStyle} />`\r\n}\r\n\r\nhexo.extend.tag.register('inlineImg', inlineImg, { ends: false })\r\n"
  },
  {
    "path": "scripts/tag/label.js",
    "content": "/**\r\n * Butterfly\r\n * label\r\n * {% label text color %}\r\n */\r\n\r\n'use strict'\r\n\r\nconst addLabel = args => {\r\n  const [text, className = 'default'] = args\r\n  return `<mark class=\"hl-label ${className}\">${text}</mark>`\r\n}\r\n\r\nhexo.extend.tag.register('label', addLabel, { ends: false })\r\n"
  },
  {
    "path": "scripts/tag/mermaid.js",
    "content": "/**\r\n * Butterfly\r\n * mermaid\r\n * https://github.com/mermaid-js/mermaid\r\n */\r\n\r\n'use strict'\r\n\r\nconst { escapeHTML } = require('hexo-util')\r\n\r\nconst mermaid = (args, content) => {\r\n  const config = args[0] || '{}'\r\n  return `<div class=\"mermaid-wrap\"><pre class=\"mermaid-src\" data-config=\"${escapeHTML(config)}\" hidden>\r\n    ${escapeHTML(content)}\r\n  </pre></div>`\r\n}\r\n\r\nhexo.extend.tag.register('mermaid', mermaid, { ends: true })\r\n"
  },
  {
    "path": "scripts/tag/note.js",
    "content": "/**\r\n * note.js\r\n * transplant from hexo-theme-next\r\n * Modify by Jerry\r\n */\r\n\r\n'use strict'\r\n\r\nconst postNote = (args, content) => {\r\n  const styleConfig = hexo.theme.config.note.style\r\n  const noteTag = ['flat', 'modern', 'simple', 'disabled']\r\n  if (!noteTag.includes(args[args.length - 1])) {\r\n    args.push(styleConfig)\r\n  }\r\n\r\n  let icon = ''\r\n  const iconArray = args[args.length - 2]\r\n  if (iconArray && iconArray.startsWith('fa')) {\r\n    icon = `<i class=\"note-icon ${iconArray}\"></i>`\r\n    args[args.length - 2] = 'icon-padding'\r\n  }\r\n\r\n  return `<div class=\"note ${args.join(' ')}\">${icon + hexo.render.renderSync({ text: content, engine: 'markdown' })}</div>`\r\n}\r\n\r\nhexo.extend.tag.register('note', postNote, { ends: true })\r\nhexo.extend.tag.register('subnote', postNote, { ends: true })\r\n"
  },
  {
    "path": "scripts/tag/score.js",
    "content": "/**\r\n * Music Score\r\n * {% score %}\r\n */\r\n\r\n'use strict'\r\n\r\nconst score = (args, content) => {\r\n  // Escape HTML tags and some special characters, including curly braces\r\n  const escapeHtmlTags = s => {\r\n    const lookup = {\r\n      '&': '&amp;',\r\n      '\"': '&quot;',\r\n      \"'\": '&apos;',\r\n      '<': '&lt;',\r\n      '>': '&gt;',\r\n      '{': '&#123;',\r\n      '}': '&#125;'\r\n    }\r\n    return s.replace(/[&\"'<>{}]/g, c => lookup[c])\r\n  }\r\n\r\n  const trimmed = content.trim()\r\n  // Split content using six dashes as a delimiter\r\n  const parts = trimmed.split('------')\r\n\r\n  if (parts.length < 2) {\r\n    // If no delimiter is found, treat the entire content as the score\r\n    return `<div class=\"abc-music-sheet\">${escapeHtmlTags(trimmed)}</div>`\r\n  }\r\n\r\n  // First part is parameters (JSON string), the rest is the score content\r\n  const paramPart = parts[0].trim()\r\n  const scorePart = parts.slice(1).join('------').trim()\r\n\r\n  let paramsObj = {}\r\n  try {\r\n    paramsObj = JSON.parse(paramPart)\r\n  } catch (e) {\r\n    console.error('Failed to parse JSON in score tag:', e)\r\n  }\r\n\r\n  // Use double quotes for data-params attribute value,\r\n  // ensuring JSON internal double quotes are escaped\r\n  return `<div class=\"abc-music-sheet\" data-params=\"${escapeHtmlTags(JSON.stringify(paramsObj))}\">\r\n    ${escapeHtmlTags(scorePart)}\r\n  </div>`\r\n}\r\n\r\nhexo.extend.tag.register('score', score, { ends: true })\r\n"
  },
  {
    "path": "scripts/tag/series.js",
    "content": "/**\r\n * series plugin\r\n * Syntax:\r\n *  {% series [series name] %}\r\n * Usage:\r\n * {% series %}\r\n * {% series series1 %}\r\n */\r\n\r\n'use strict'\r\n\r\nconst urlFor = require('hexo-util').url_for.bind(hexo)\r\nconst groups = {}\r\n\r\nhexo.extend.filter.register('before_post_render', data => {\r\n  if (!hexo.theme.config.series.enable) return data\r\n\r\n  const { layout, series } = data\r\n  if (layout === 'post' && series) {\r\n    if (!groups[series]) groups[series] = []\r\n    groups[series].push({\r\n      title: data.title,\r\n      path: data.path,\r\n      date: data.date.unix()\r\n    })\r\n  }\r\n  return data\r\n})\r\n\r\nfunction series (args) {\r\n  const { series } = hexo.theme.config\r\n  if (!series.enable) {\r\n    hexo.log.warn('Series plugin is disabled in the theme config')\r\n    return ''\r\n  }\r\n\r\n  const seriesArr = args.length ? groups[args[0]] : groups[this.series]\r\n\r\n  if (!seriesArr) {\r\n    hexo.log.warn(`There is no series named \"${args[0]}\"`)\r\n    return ''\r\n  }\r\n\r\n  const isAsc = (series.order || 1) === 1 // 1: asc, -1: desc\r\n  const isSortByTitle = series.orderBy === 'title'\r\n\r\n  const compareFn = (a, b) => {\r\n    const itemA = isSortByTitle ? a.title.toUpperCase() : a.date\r\n    const itemB = isSortByTitle ? b.title.toUpperCase() : b.date\r\n\r\n    return itemA < itemB ? (isAsc ? -1 : 1) : itemA > itemB ? (isAsc ? 1 : -1) : 0\r\n  }\r\n\r\n  seriesArr.sort(compareFn)\r\n\r\n  const listItems = seriesArr.map(ele =>\r\n    `<li><a href=\"${urlFor(ele.path)}\" title=\"${ele.title}\">${ele.title}</a></li>`\r\n  ).join('')\r\n\r\n  return series.number ? `<ol class=\"series-items\">${listItems}</ol>` : `<ul class=\"series-items\">${listItems}</ul>`\r\n}\r\n\r\nhexo.extend.tag.register('series', series, { ends: false })\r\n"
  },
  {
    "path": "scripts/tag/tabs.js",
    "content": "/**\r\n * Tabs\r\n * transplant from hexo-theme-next\r\n * modify by Jerry\r\n */\r\n\r\n'use strict'\r\n\r\nconst postTabs = (args, content) => {\r\n  const tabBlock = /<!--\\s*tab (.*?)\\s*-->\\n([\\w\\W\\s\\S]*?)<!--\\s*endtab\\s*-->/g\r\n  args = args.join(' ').split(',')\r\n  const tabName = args[0] || 'tab'\r\n  const tabActive = Number(args[1]) || 0\r\n  const matches = []\r\n  let match\r\n  let tabId = 0\r\n  let tabNav = ''\r\n  let tabContent = ''\r\n  let noDefault = true\r\n\r\n  while ((match = tabBlock.exec(content)) !== null) {\r\n    matches.push(match[1], match[2])\r\n  }\r\n\r\n  for (let i = 0; i < matches.length; i += 2) {\r\n    const [tabCaption = '', tabIcon = ''] = matches[i].split('@')\r\n    let postContent = matches[i + 1]\r\n\r\n    postContent = hexo.render.renderSync({ text: postContent, engine: 'markdown' }).trim()\r\n    tabId += 1\r\n\r\n    const caption = (tabCaption || tabIcon) ? tabCaption : `${tabName} ${tabId}`\r\n    const iconHtml = tabIcon ? `<i class=\"${tabIcon.trim()}\"></i>` : ''\r\n    const isActive = (tabActive > 0 && tabActive === tabId) || (tabActive === 0 && tabId === 1) ? ' active' : ''\r\n\r\n    if (isActive) noDefault = false\r\n\r\n    tabNav += `<button type=\"button\" class=\"tab${isActive}\">${iconHtml}${caption.trim()}</button>`\r\n    tabContent += `<div class=\"tab-item-content${isActive}\">${postContent}</div>`\r\n  }\r\n\r\n  const toTop = '<div class=\"tab-to-top\"><button type=\"button\" aria-label=\"scroll to top\"><i class=\"fas fa-arrow-up\"></i></button></div>'\r\n  tabNav = `<div class=\"nav-tabs${noDefault ? ' no-default' : ''}\">${tabNav}</div>`\r\n  tabContent = `<div class=\"tab-contents\">${tabContent}</div>`\r\n\r\n  return `<div class=\"tabs\">${tabNav}${tabContent}${toTop}</div>`\r\n}\r\n\r\nhexo.extend.tag.register('tabs', postTabs, { ends: true })\r\nhexo.extend.tag.register('subtabs', postTabs, { ends: true })\r\nhexo.extend.tag.register('subsubtabs', postTabs, { ends: true })\r\n"
  },
  {
    "path": "scripts/tag/timeline.js",
    "content": "/**\r\n * Timeline tag for Hexo\r\n * Syntax:\r\n * {% timeline [headline],[color] %}\r\n * <!-- timeline [title] -->\r\n * [content]\r\n * <!-- endtimeline -->\r\n * <!-- timeline [title] -->\r\n * [content]\r\n * <!-- endtimeline -->\r\n * {% endtimeline %}\r\n */\r\n\r\n'use strict'\r\n\r\nconst timeLineFn = (args, content) => {\r\n  // Use named capture groups for better readability\r\n  const tlBlock = /<!--\\s*timeline\\s*(?<title>.*?)\\s*-->\\n(?<content>[\\s\\S]*?)<!--\\s*endtimeline\\s*-->/g\r\n\r\n  // Pre-compile markdown render function\r\n  const renderMd = text => hexo.render.renderSync({ text, engine: 'markdown' })\r\n\r\n  // Parse arguments more efficiently\r\n  const [text, color = ''] = args.length ? args.join(' ').split(',') : []\r\n\r\n  // Build initial headline if text exists\r\n  const headline = text\r\n    ? `<div class='timeline-item headline'>\r\n        <div class='timeline-item-title'>\r\n          <div class='item-circle'>${renderMd(text)}</div>\r\n        </div>\r\n      </div>`\r\n    : ''\r\n\r\n  // Match all timeline blocks in one pass and transform\r\n  const items = Array.from(content.matchAll(tlBlock))\r\n    .map(({ groups: { title, content } }) =>\r\n      `<div class='timeline-item'>\r\n        <div class='timeline-item-title'>\r\n          <div class='item-circle'>${renderMd(title)}</div>\r\n        </div>\r\n        <div class='timeline-item-content'>${renderMd(content)}</div>\r\n      </div>`\r\n    )\r\n    .join('')\r\n\r\n  return `<div class=\"timeline ${color}\">${headline}${items}</div>`\r\n}\r\n\r\nhexo.extend.tag.register('timeline', timeLineFn, { ends: true })\r\n"
  },
  {
    "path": "source/css/_global/function.styl",
    "content": ".limit-one-line\r\n  overflow: hidden\r\n  text-overflow: ellipsis\r\n  white-space: nowrap\r\n\r\n.limit-more-line\r\n  display: -webkit-box\r\n  overflow: hidden\r\n  -webkit-box-orient: vertical\r\n\r\n.fontawesomeIcon\r\n  display: inline-block\r\n  font-weight: 600\r\n  font-family: 'Font Awesome 7 Free', 'Font Awesome 6 Free'\r\n  text-rendering: auto\r\n  -webkit-font-smoothing: antialiased\r\n\r\naddBorderRadius(x = 6, hide = false)\r\n  if hexo-config('rounded_corners_ui')\r\n    border-radius: unit(x, 'px')\r\n\r\n    if hide\r\n      overflow: hidden\r\n\r\n// card hover\r\n.cardHover\r\n  background: var(--card-bg)\r\n  box-shadow: var(--card-box-shadow)\r\n  transition: all .3s\r\n  addBorderRadius(8)\r\n\r\n  &:hover\r\n    box-shadow: var(--card-hover-box-shadow)\r\n\r\n.imgHover\r\n  width: 100%\r\n  height: 100%\r\n  transition: filter 375ms ease-in .2s, transform .6s\r\n  object-fit: cover\r\n\r\n  &:hover\r\n    transform: scale(1.1)\r\n\r\n.postImgHover\r\n  &:hover\r\n    .cover\r\n      opacity: .5\r\n      transform: scale(1.1)\r\n\r\n  .cover\r\n    width: 100%\r\n    height: 100%\r\n    opacity: .4\r\n    transition: all .6s, filter 375ms ease-in .2s\r\n    object-fit: cover\r\n\r\n.list-beauty\r\n  list-style: none\r\n\r\n  li\r\n    position: relative\r\n    padding: .12em .4em .12em 1.4em\r\n\r\n    &:hover\r\n      &:before\r\n        border-color: var(--pseudo-hover)\r\n\r\n    &:before\r\n      position: absolute\r\n      top: .67em\r\n      left: 0\r\n      width: w = .43em\r\n      height: h = w\r\n      border: .5 * w solid $light-blue\r\n      border-radius: w\r\n      background: transparent\r\n      content: ''\r\n      cursor: pointer\r\n      transition: all .3s ease-out\r\n\r\n.custom-hr\r\n  position: relative\r\n  margin: 40px auto\r\n  border: 2px dashed var(--hr-border)\r\n\r\n  if hexo-config('hr_icon.enable')\r\n    width: calc(100% - 4px)\r\n\r\n    &:hover\r\n      &:before\r\n        left: calc(95% - 20px)\r\n\r\n    &:before\r\n      position: absolute\r\n      top: $hr-icon-top\r\n      left: 5%\r\n      z-index: 1\r\n      color: var(--hr-before-color)\r\n      content: $hr-icon\r\n      font-size: 20px\r\n      line-height: 1\r\n      transition: all 1s ease-in-out\r\n      @extend .fontawesomeIcon\r\n\r\n.verticalCenter\r\n  position: absolute\r\n  top: 50%\r\n  width: 100%\r\n  transform: translate(0, -50%)\r\n\r\nmaxWidth600()\r\n  @media screen and (max-width: 600px)\r\n    {block}\r\n\r\nmaxWidth768()\r\n  @media screen and (max-width: 768px)\r\n    {block}\r\n\r\nminWidth768()\r\n  @media screen and (min-width: 768px)\r\n    {block}\r\n\r\nmaxWidth1024()\r\n  @media screen and (max-width: 1024px)\r\n    {block}\r\n\r\nminWidth1024()\r\n  @media screen and (min-width: 1024px)\r\n    {block}\r\n\r\nmaxWidth900()\r\n  @media screen and (max-width: 900px)\r\n    {block}\r\n\r\nminWidth901()\r\n  @media screen and (min-width: 901px)\r\n    {block}\r\n\r\nminWidth900()\r\n  @media screen and (min-width: 900px)\r\n    {block}\r\n\r\nminWidth2000()\r\n  @media screen and (min-width: 2000px)\r\n    {block}\r\n\r\n// animation\r\nif hexo-config('enter_transitions')\r\n  #content-inner,\r\n  #footer\r\n    animation: bottom-top 1s\r\n\r\n  #page-header:not(.full_page),\r\n  #nav.show\r\n    animation: header-effect 1s\r\n\r\n  #site-title,\r\n  #site-subtitle\r\n    animation: titleScale 1s\r\n\r\n  canvas:not(#ribbon-canvas),\r\n  #web_bg.bg-animation\r\n    animation: to_show 4s\r\n\r\n  #ribbon-canvas\r\n    animation: ribbon_to_show 4s\r\n\r\n  #sidebar-menus\r\n    &.open\r\n      for i in 1 2 3 4\r\n        > :nth-child({i})\r\n          animation: sidebarItem (i / 5s)\r\n\r\n.scroll-down-effects\r\n  animation: scroll-down-effect 1.5s infinite\r\n\r\nif hexo-config('avatar.effect') == true\r\n  .avatar-img\r\n    animation: avatar_turn_around 2s linear infinite\r\n\r\n.reward-main\r\n  animation: donate_effcet .3s .1s ease both\r\n\r\n.btn-effects\r\n  position: relative\r\n  overflow: hidden\r\n  transition: all .3s cubic-bezier(.4, 0, .2, 1)\r\n  transform: translateZ(0)\r\n\r\n  &:hover\r\n    box-shadow: 0 4px 12px rgba(0, 0, 0, .15)\r\n    text-decoration: none\r\n    transform: translateY(-1px) scale(1.02)\r\n\r\n  &:active\r\n    transition-duration: .1s\r\n    transform: translateY(0) scale(.98)\r\n\r\n  i\r\n    display: inline-block\r\n    vertical-align: middle\r\n    transition: all .3s cubic-bezier(.4, 0, .2, 1)\r\n\r\n  &:hover i\r\n    animation: buttonIconBounce .6s ease-in-out\r\n\r\n  i + span\r\n    margin-left: 6px\r\n    vertical-align: middle\r\n    transition: margin-left .3s cubic-bezier(.4, 0, .2, 1)\r\n\r\n  &:hover i + span\r\n    margin-left: 8px\r\n\r\n  &::before\r\n    position: absolute\r\n    top: 0\r\n    left: -100%\r\n    z-index: 1\r\n    width: 100%\r\n    height: 100%\r\n    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .2), transparent)\r\n    content: ''\r\n    transition: left .5s cubic-bezier(.4, 0, .2, 1)\r\n\r\n  &:hover::before\r\n    left: 100%\r\n\r\n  & > *\r\n    position: relative\r\n    z-index: 2\r\n\r\n.btn-effects-large\r\n  &:hover\r\n    box-shadow: 0 6px 16px rgba(0, 0, 0, .2)\r\n    transform: translateY(-2px) scale(1.03)\r\n\r\n@keyframes scroll-down-effect\r\n  0%\r\n    opacity: .4\r\n    transform: translate(0, 0)\r\n\r\n  50%\r\n    opacity: 1\r\n    transform: translate(0, -16px)\r\n\r\n  100%\r\n    opacity: .4\r\n    transform: translate(0, 0)\r\n\r\n@keyframes header-effect\r\n  0%\r\n    transform: translateY(-35px)\r\n\r\n  100%\r\n    transform: translateY(0)\r\n\r\n@keyframes bottom-top\r\n  0%\r\n    transform: translateY(35px)\r\n\r\n  100%\r\n    transform: translateY(0)\r\n\r\n@keyframes titleScale\r\n  0%\r\n    opacity: 0\r\n    transform: scale(.7)\r\n\r\n  100%\r\n    opacity: 1\r\n    transform: scale(1)\r\n\r\n@keyframes search_close\r\n  0%\r\n    opacity: 1\r\n    transform: scale(1)\r\n\r\n  100%\r\n    opacity: 0\r\n    transform: scale(.7)\r\n\r\n@keyframes to_show\r\n  0%\r\n    opacity: 0\r\n\r\n  100%\r\n    opacity: 1\r\n\r\n@keyframes to_hide\r\n  0%\r\n    opacity: 1\r\n\r\n  100%\r\n    opacity: 0\r\n\r\n@keyframes ribbon_to_show\r\n  0%\r\n    opacity: 0\r\n\r\n  100%\r\n    opacity: hexo-config('canvas_ribbon.alpha')\r\n\r\n@keyframes avatar_turn_around\r\n  from\r\n    transform: rotate(0)\r\n\r\n  to\r\n    transform: rotate(360deg)\r\n\r\n@keyframes sub_menus\r\n  0%\r\n    opacity: 0\r\n    transform: translateY(10px)\r\n\r\n  100%\r\n    opacity: 1\r\n    transform: translateY(0)\r\n\r\n@keyframes donate_effcet\r\n  0%\r\n    opacity: 0\r\n    transform: translateY(-20px)\r\n\r\n  100%\r\n    opacity: 1\r\n    transform: translateY(0)\r\n\r\n@keyframes sidebarItem\r\n  0%\r\n    transform: translateX(200px)\r\n\r\n  100%\r\n    transform: translateX(0)\r\n\r\n@keyframes buttonIconBounce\r\n  0%,\r\n  100%\r\n    transform: translateY(0) scale(1)\r\n\r\n  25%\r\n    transform: translateY(-3px) scale(1.1) rotateZ(-5deg)\r\n\r\n  50%\r\n    transform: translateY(0) scale(1.05) rotateZ(0)\r\n\r\n  75%\r\n    transform: translateY(-1px) scale(1.02) rotateZ(2deg)"
  },
  {
    "path": "source/css/_global/index.styl",
    "content": ":root\r\n  --global-font-size: $font-size\r\n  --global-bg: $body-bg\r\n  --font-color: $font-black\r\n  --hr-border: lighten($theme-hr-color, 50%)\r\n  --hr-before-color: lighten($theme-hr-color, 30%)\r\n  --search-bg: $search-bg\r\n  --search-input-color: $search-input-color\r\n  --search-a-color: $search-a-color\r\n  --preloader-bg: $preloader-bg\r\n  --preloader-color: $preloader-word-color\r\n  --tab-border-color: $tab-border-color\r\n  --tab-button-bg: $tab-button-bg\r\n  --tab-button-color: $tab-button-color\r\n  --tab-button-hover-bg: $tab-button-hover-bg\r\n  --tab-button-active-bg: $tab-button-active-bg\r\n  --card-bg: $card-bg\r\n  --card-meta: $theme-meta-color\r\n  --sidebar-bg: $sidebar-background\r\n  --sidebar-menu-bg: $white\r\n  --btn-hover-color: $button-hover-color\r\n  --btn-color: $button-color\r\n  --btn-bg: $button-bg\r\n  --text-bg-hover: rgba($text-bg-hover, .7)\r\n  --light-grey: $light-grey\r\n  --dark-grey: $dark-grey\r\n  --white: $white\r\n  --text-highlight-color: $text-highlight-color\r\n  --blockquote-color: $blockquote-color\r\n  --blockquote-bg: $blockquote-background-color\r\n  --reward-pop: $reward-pop-up-bg\r\n  --toc-link-color: $toc-link-color\r\n  --card-box-shadow: 0 3px 8px 6px rgba(7, 17, 27, .05)\r\n  --card-hover-box-shadow: 0 3px 8px 6px rgba(7, 17, 27, .09)\r\n  --pseudo-hover: $pseudo-hover\r\n  --headline-presudo: #a0a0a0\r\n  --scrollbar-color: $scrollbar-color\r\n  --default-bg-color: $theme-color\r\n  --zoom-bg: #fff\r\n  --mark-bg: alpha($dark-black, .3)\r\n\r\n// tags plugin\r\n:root\r\n  --btn-color: $btn-color\r\n  --btn-default-color: $btn-default-color\r\n  --tags-blue-color: $tagsP-blue-color\r\n  --tags-blue-color-lighten: lighten($tagsP-blue-color, 85%)\r\n  --tags-pink-color: $tagsP-pink-color\r\n  --tags-pink-color-lighten: lighten($tagsP-pink-color, 85%)\r\n  --tags-red-color: $tagsP-red-color\r\n  --tags-red-color-lighten: lighten($tagsP-red-color, 85%)\r\n  --tags-orange-color: $tagsP-orange-color\r\n  --tags-orange-color-lighten: lighten($tagsP-orange-color, 85%)\r\n  --tags-purple-color: $tagsP-purple-color\r\n  --tags-purple-color-lighten: lighten($tagsP-purple-color, 85%)\r\n  --tags-green-color: $tagsP-green-color\r\n  --tags-green-color-lighten: lighten($tagsP-green-color, 85%)\r\n  --note-default-border: $note-default-border\r\n  --note-default-bg: $note-default-bg\r\n  --note-default-text: $note-default-text\r\n  --note-modern-default-border: $note-modern-default-border\r\n  --note-modern-default-bg: $note-modern-default-bg\r\n  --note-modern-default-text: $note-modern-default-text\r\n  --note-modern-default-hover: $note-modern-default-hover\r\n  --note-primary-border: $note-primary-border\r\n  --note-primary-bg: $note-primary-bg\r\n  --note-primary-text: $note-primary-text\r\n  --note-modern-primary-border: $note-modern-primary-border\r\n  --note-modern-primary-bg: $note-modern-primary-bg\r\n  --note-modern-primary-text: $note-modern-primary-text\r\n  --note-modern-primary-hover: $note-modern-primary-hover\r\n  --note-info-border: $note-info-border\r\n  --note-info-bg: $note-info-bg\r\n  --note-info-text: $note-info-text\r\n  --note-modern-info-border: $note-modern-info-border\r\n  --note-modern-info-bg: $note-modern-info-bg\r\n  --note-modern-info-text: $note-modern-info-text\r\n  --note-modern-info-hover: $note-modern-info-hover\r\n  --note-success-border: $note-success-border\r\n  --note-success-bg: $note-success-bg\r\n  --note-success-text: $note-success-text\r\n  --note-modern-success-border: $note-modern-success-border\r\n  --note-modern-success-bg: $note-modern-success-bg\r\n  --note-modern-success-text: $note-modern-success-text\r\n  --note-modern-success-hover: $note-modern-success-hover\r\n  --note-warning-border: $note-warning-border\r\n  --note-warning-bg: $note-warning-bg\r\n  --note-warning-text: $note-warning-text\r\n  --note-modern-warning-border: $note-modern-warning-border\r\n  --note-modern-warning-bg: $note-modern-warning-bg\r\n  --note-modern-warning-text: $note-modern-warning-text\r\n  --note-modern-warning-hover: $note-modern-warning-hover\r\n  --note-danger-border: $note-danger-border\r\n  --note-danger-bg: $note-danger-bg\r\n  --note-danger-text: $note-danger-text\r\n  --note-modern-danger-border: $note-modern-danger-border\r\n  --note-modern-danger-bg: $note-modern-danger-bg\r\n  --note-modern-danger-text: $note-modern-danger-text\r\n  --note-modern-danger-hover: $note-modern-danger-hover\r\n\r\nbody\r\n  position: relative\r\n  overflow-y: scroll\r\n  min-height: 100%\r\n  background: var(--global-bg)\r\n  color: var(--font-color)\r\n  font-size: var(--global-font-size)\r\n  font-family: $font-family\r\n  line-height: $text-line-height\r\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0)\r\n  scroll-behavior: smooth\r\n\r\n  if !hexo-config('copy.enable')\r\n    user-select: none\r\n    -webkit-user-select: none\r\n\r\n// scrollbar - firefox\r\n@-moz-document url-prefix()\r\n  *\r\n    scrollbar-width: thin\r\n    scrollbar-color: var(--scrollbar-color) transparent\r\n\r\n// scrollbar - chrome/safari\r\n*::-webkit-scrollbar\r\n  width: 5px\r\n  height: 5px\r\n\r\n*::-webkit-scrollbar-thumb\r\n  background: var(--scrollbar-color)\r\n\r\n*::-webkit-scrollbar-track\r\n  background-color: transparent\r\n\r\ninput::placeholder\r\n  color: var(--font-color)\r\n\r\nif hexo-config('background')\r\n  #web_bg\r\n    position: fixed\r\n    z-index: -999\r\n    width: 100%\r\n    height: 100%\r\n    background-attachment: local\r\n    background-position: center\r\n    background-size: cover\r\n    background-repeat: no-repeat\r\n\r\nh1,\r\nh2,\r\nh3,\r\nh4,\r\nh5,\r\nh6\r\n  position: relative\r\n  margin: 20px 0 14px\r\n  color: var(--text-highlight-color)\r\n  font-weight: bold\r\n\r\n  code\r\n    font-size: inherit !important\r\n\r\n*\r\n  box-sizing: border-box\r\n\r\n.table-wrap\r\n  overflow-x: scroll\r\n  margin: 0 0 20px\r\n\r\n  if hexo-config('rounded_corners_ui')\r\n    $borderRadius = 5px\r\n    border-radius: $borderRadius\r\n\r\n    table\r\n      border-radius: $borderRadius\r\n\r\n      thead > tr:first-child\r\n        th:first-child\r\n          border-top-left-radius: $borderRadius\r\n\r\n        th:last-child\r\n          border-top-right-radius: $borderRadius\r\n\r\n      tbody > tr:last-child\r\n        td:first-child\r\n          border-bottom-left-radius: $borderRadius\r\n\r\n        td:last-child\r\n          border-bottom-right-radius: $borderRadius\r\n\r\ntable\r\n  display: table\r\n  width: 100%\r\n  border-spacing: 0\r\n  border-collapse: separate\r\n  border-top: 1px solid var(--light-grey)\r\n  border-left: 1px solid var(--light-grey)\r\n  empty-cells: show\r\n\r\n  thead\r\n    background: alpha($table-thead-bg, 10%)\r\n\r\n  th,\r\n  td\r\n    padding: 6px 12px\r\n    border: 1px solid var(--light-grey)\r\n    border-top: none\r\n    border-left: none\r\n    vertical-align: middle\r\n\r\n*::selection\r\n  background: $theme-text-selection-color\r\n  color: #F7F7F7\r\n\r\nbutton\r\n  padding: 0\r\n  outline: 0\r\n  border: none\r\n  background: none\r\n  cursor: pointer\r\n  touch-action: manipulation\r\n\r\na\r\n  color: $a-link-color\r\n  text-decoration: none\r\n  word-wrap: break-word\r\n  transition: all .2s\r\n  overflow-wrap: break-word\r\n\r\n  &:hover\r\n    color: $light-blue\r\n\r\n// font\r\nif $site-name-font\r\n  #site-title,\r\n  #site-subtitle,\r\n  .site-name,\r\n  #aside-content .author-info-name,\r\n  #aside-content .author-info-description\r\n    font-family: $site-name-font\r\n\r\n.text-center\r\n  text-align: center\r\n\r\n.text-right\r\n  text-align: right\r\n\r\nimg\r\n  &[src=''],\r\n  &:not([src])\r\n    opacity: 0\r\n\r\n// lazyload blur\r\nif hexo-config('lazyload.enable') && hexo-config('lazyload.blur') && !hexo-config('lazyload.placeholder')\r\n  img\r\n    &[data-lazy-src]:not(.loaded)\r\n      filter: blur(8px) brightness(1)\r\n\r\n    &[data-lazy-src].error\r\n      filter: none\r\n\r\n.img-alt\r\n  margin: -10px 0 10px\r\n  color: #858585\r\n\r\n  &:hover\r\n    text-decoration: none !important\r\n\r\nblockquote\r\n  margin: 0 0 20px\r\n  padding: 7px 15px\r\n  border-left: 4px solid $blockquote-padding-color\r\n  background-color: var(--blockquote-bg)\r\n  color: var(--blockquote-color)\r\n  addBorderRadius()\r\n\r\n  footer\r\n    cite\r\n      &:before\r\n        padding: 0 5px\r\n        content: '—'\r\n\r\n  & > :last-child\r\n    margin-bottom: 0 !important\r\n\r\n.fa-fw\r\n  width: 1.25em\r\n  text-align: center"
  },
  {
    "path": "source/css/_highlight/highlight/diff.styl",
    "content": "figure.highlight\r\n  table\r\n    @extend $scrollbar-style\r\n\r\n  pre .deletion\r\n    color: $highlight-deletion\r\n\r\n  pre .addition\r\n    color: $highlight-addition\r\n\r\n  pre .meta\r\n    color: $highlight-purple\r\n\r\n  pre\r\n    .comment\r\n      color: $highlight-comment\r\n\r\n    .variable,\r\n    .attribute,\r\n    .regexp,\r\n    .ruby .constant,\r\n    .xml .tag .title,\r\n    .xml .pi,\r\n    .xml .doctype,\r\n    .html .doctype,\r\n    .css .id,\r\n    .tag .name,\r\n    .css .class,\r\n    .css .pseudo\r\n      color: $highlight-red\r\n\r\n    .tag\r\n      color: $highlight-aqua\r\n\r\n    .number,\r\n    .preprocessor,\r\n    .literal,\r\n    .params,\r\n    .constant,\r\n    .command\r\n      color: $highlight-orange\r\n\r\n    .built_in\r\n      color: $highlight-yellow\r\n\r\n    .ruby .class .title,\r\n    .css .rules .attribute,\r\n    .string,\r\n    .value,\r\n    .inheritance,\r\n    .header,\r\n    .ruby .symbol,\r\n    .xml .cdata,\r\n    .special,\r\n    .number,\r\n    .formula\r\n      color: $highlight-green\r\n\r\n    .keyword,\r\n    .title,\r\n    .css .hexcolor\r\n      color: $highlight-aqua\r\n\r\n    .function,\r\n    .python .decorator,\r\n    .python .title,\r\n    .ruby .function .title,\r\n    .ruby .title .keyword,\r\n    .perl .sub,\r\n    .javascript .title,\r\n    .coffeescript .title\r\n      color: $highlight-blue\r\n\r\n    .tag .attr,\r\n    .javascript .function\r\n      color: $highlight-purple\r\n"
  },
  {
    "path": "source/css/_highlight/highlight/index.styl",
    "content": "if $highlight_theme != false\r\n  @require 'diff'\r\n\r\n.container\r\n  figure.highlight\r\n    .line\r\n      if wordWrap\r\n        &:before\r\n          display: inline-block\r\n          padding: 0 6px 0 0\r\n          min-width: 30px\r\n          color: var(--hlnumber-color)\r\n          content: counter(line)\r\n          counter-increment: line\r\n          text-align: left\r\n\r\n      &.marked\r\n        background-color: $highlight-selection\r\n\r\n    table\r\n      display: block\r\n      overflow: auto\r\n      border: none\r\n\r\n      td\r\n        padding: 0\r\n        border: none\r\n\r\n    .gutter pre\r\n      padding-right: 10px\r\n      padding-left: 10px\r\n      background-color: var(--hlnumber-bg)\r\n      color: var(--hlnumber-color)\r\n      text-align: right\r\n\r\n    .code pre\r\n      padding-right: 10px\r\n      padding-left: 10px\r\n      width: 100%\r\n"
  },
  {
    "path": "source/css/_highlight/highlight.styl",
    "content": "$highlight_theme = hexo-config('code_blocks.theme')\r\n$highlight_macstyle = hexo-config('code_blocks.macStyle')\r\nwordWrap = $highlight_enable && !$highlight_line_number && hexo-config('code_blocks.word_wrap')\r\n\r\n@require 'theme'\r\n\r\n:root\r\n  --hl-color: $highlight-foreground\r\n  --hl-bg: $highlight-background\r\n  --hltools-bg: $highlight-tools.bg-color\r\n  --hltools-color: $highlight-tools.color\r\n  --hlnumber-bg: $highlight-gutter.bg-color\r\n  --hlnumber-color: $highlight-gutter.color\r\n  --hlscrollbar-bg: $highlight-scrollbar\r\n  --hlexpand-bg: linear-gradient(180deg, rgba($highlight-background, .6), rgba($highlight-background, .9))\r\n\r\n[data-theme='dark']\r\n  --hl-color: alpha(#FFFFFF, .7)\r\n  --hl-bg: lighten(#121212, 2)\r\n  --hltools-bg: lighten(#121212, 3)\r\n  --hltools-color: #90a4ae\r\n  --hlnumber-bg: lighten(#121212, 2)\r\n  --hlnumber-color: alpha(#FFFFFF, .4)\r\n  --hlscrollbar-bg: lighten(#121212, 5)\r\n  --hlexpand-bg: linear-gradient(180deg, rgba(lighten(#121212, 2), .6), rgba(lighten(#121212, 2), .9))\r\n\r\n$scrollbar-style\r\n  // scrollbar - firefox\r\n  @-moz-document url-prefix()\r\n    scrollbar-color: var(--hlscrollbar-bg) transparent\r\n\r\n  &::-webkit-scrollbar-thumb\r\n    background: var(--hlscrollbar-bg)\r\n\r\nif $highlight_enable\r\n  @require 'highlight/index'\r\n\r\nif $prismjs_enable\r\n  @require 'prismjs/index'\r\n\r\n$code-block\r\n  overflow: auto\r\n  margin: 0 0 20px\r\n  padding: 0\r\n  background: var(--hl-bg)\r\n  color: var(--hl-color)\r\n  line-height: $line-height-code-block\r\n\r\n  if wordWrap\r\n    counter-reset: line\r\n    white-space: pre-wrap\r\n\r\n.container\r\n  pre,\r\n  code\r\n    font-size: $code-font-size\r\n    font-family: $code-font-family !important\r\n    addBorderRadius()\r\n\r\n  code\r\n    padding: 2px 5px\r\n    background: $code-background\r\n    color: $code-foreground\r\n\r\n  pre\r\n    @extend $code-block\r\n    padding: 10px 20px\r\n\r\n    code\r\n      padding: 0\r\n      background: none\r\n      color: var(--hl-color)\r\n      text-shadow: none\r\n\r\n  figure.highlight\r\n    @extend $code-block\r\n    position: relative\r\n    addBorderRadius()\r\n\r\n    pre\r\n      margin: 0\r\n      padding: 8px 0\r\n      border: none\r\n\r\n    figcaption,\r\n    .caption\r\n      padding: 6px 0 2px 14px\r\n      font-size: $code-font-size\r\n      line-height: 1em\r\n\r\n      a\r\n        float: right\r\n        padding-right: 10px\r\n        color: var(--hl-color)\r\n\r\n        &:hover\r\n          border-bottom-color: var(--hl-color)\r\n\r\n    &.default\r\n      pre\r\n        padding: 10px 20px\r\n        @extend $scrollbar-style\r\n\r\n    &.copy-true\r\n      user-select: all\r\n      -webkit-user-select: all\r\n\r\n      & > table,\r\n      & > pre\r\n        display: block !important\r\n        opacity: 0\r\n\r\n  .highlight-tools\r\n    display: flex\r\n    align-items: center\r\n    overflow: hidden\r\n    padding: 0 8px\r\n    min-height: 24px\r\n    height: 2.15em\r\n    background: var(--hltools-bg)\r\n    color: var(--hltools-color)\r\n    font-size: $code-font-size\r\n\r\n    & > *\r\n      flex: 0 0 auto\r\n      margin: 2px\r\n\r\n    i\r\n      display: inline-flex\r\n      justify-content: center\r\n      align-items: center\r\n      padding: 5px\r\n      cursor: pointer\r\n      transition: all .3s\r\n\r\n      &:hover\r\n        color: $theme-color\r\n\r\n    &.closed\r\n      & ~ *\r\n        display: none\r\n\r\n      .expand\r\n        transform: rotate(-90deg)\r\n\r\n    if !$highlight_macstyle\r\n      & > .macStyle\r\n        margin: 0\r\n\r\n    .code-lang\r\n      flex: 1 1 auto\r\n      overflow: hidden\r\n      padding-right: 10px\r\n      text-transform: uppercase\r\n      text-overflow: ellipsis\r\n      white-space: nowrap\r\n      font-weight: bold\r\n      font-size: 1.15em\r\n      user-select: none\r\n      -webkit-user-select: none\r\n\r\n    if hexo-config('code_blocks.language')\r\n      margin-right: auto\r\n    else if !$highlight_macstyle && hexo-config('code_blocks.shrink') != 'none'\r\n      & > :nth-child(2)\r\n        margin-right: auto\r\n    else\r\n      & > :nth-child(1)\r\n        margin-right: auto\r\n\r\n  .gutter\r\n    user-select: none\r\n    -webkit-user-select: none\r\n\r\n  .gist table\r\n    width: auto\r\n\r\n    td\r\n      border: none\r\n\r\n.copy-notice\r\n  position: absolute\r\n  z-index: 99999\r\n  padding: 2px 6px\r\n  border-radius: 3px\r\n  background: var(--hltools-bg)\r\n  white-space: nowrap\r\n  font-size: 12px\r\n  pointer-events: none\r\n\r\nif $highlight_macstyle\r\n  .container\r\n    figure.highlight\r\n      margin: 0 0 24px\r\n      border-radius: 8px\r\n      box-shadow: 0 5px 10px 0 $highlight-mac-border\r\n      -webkit-transform: translateZ(0)\r\n\r\n      .highlight-tools\r\n        .macStyle\r\n          display: flex\r\n          padding: 3px\r\n\r\n          & > *\r\n            margin-right: 8px\r\n            width: 12px\r\n            height: 12px\r\n            border-radius: 50%\r\n\r\n          & > :last-child\r\n            margin-right: 5px\r\n\r\n          .mac-close\r\n            background: #fc625d\r\n\r\n          .mac-minimize\r\n            background: #fdbc40\r\n\r\n          .mac-maximize\r\n            background: #35cd4b\r\n\r\n        if hexo-config('code_blocks.shrink') != 'none'\r\n          & > :nth-child(2)\r\n            order: 8\r\n\r\n          &.closed\r\n            .expand\r\n              transform: rotate(90deg)\r\n\r\nif hexo-config('code_blocks.height_limit')\r\n  .container\r\n    .code-expand-btn\r\n      position: absolute\r\n      bottom: 0\r\n      z-index: 10\r\n      width: 100%\r\n      background: var(--hlexpand-bg)\r\n      text-align: center\r\n      font-size: $code-font-size\r\n      cursor: pointer\r\n\r\n      i\r\n        padding: 6px 0\r\n        color: var(--hlnumber-color)\r\n        animation: code-expand-key 1.2s infinite\r\n\r\n      &.expand-done\r\n        & > i\r\n          transform: rotate(180deg)\r\n\r\n        & + table,\r\n        & + pre\r\n          margin-bottom: 1.8em\r\n\r\n      &:not(.expand-done)\r\n        & ~ table,\r\n        & ~ pre\r\n          overflow: hidden\r\n          height: unit(hexo-config('code_blocks.height_limit'), px)\r\n\r\n  @keyframes code-expand-key\r\n    0%\r\n      opacity: .6\r\n\r\n    50%\r\n      opacity: .1\r\n\r\n    100%\r\n      opacity: .6\r\n\r\nif hexo-config('code_blocks.fullpage')\r\n  .container\r\n    figure.highlight.code-fullpage\r\n      position: fixed\r\n      top: 0\r\n      right: 0\r\n      bottom: 0\r\n      left: 0\r\n      z-index: 9999\r\n      margin: 0\r\n      border-radius: 0\r\n      animation: code-fullpage .3s\r\n\r\n      .code-expand-btn,\r\n      .expand\r\n        display: none\r\n\r\n      .highlight-tools\r\n        & ~ pre,\r\n        & ~ table\r\n          display: block\r\n          overflow: auto\r\n          margin-bottom: 0\r\n          height: calc(100vh - 2.15em)\r\n\r\n  @keyframes code-fullpage\r\n    0%,\r\n    100%\r\n      transform: translateX(0)\r\n\r\n    20%,\r\n    60%\r\n      transform: translateX(-5px)\r\n\r\n    40%,\r\n    80%\r\n      transform: translateX(5px)"
  },
  {
    "path": "source/css/_highlight/prismjs/diff.styl",
    "content": "if $highlight_theme == 'light'\r\n  // prism-base16-ateliersulphurpool.light\r\n  pre[class*='language-']\r\n    .token.function\r\n      color: #ffb62c\r\n\r\n    .token.comment,\r\n    .token.prolog,\r\n    .token.doctype,\r\n    .token.cdata\r\n      color: rgba(149, 165, 166, .8)\r\n\r\n    .token.punctuation\r\n      color: #5e6687\r\n\r\n    .token.namespace\r\n      opacity: .7\r\n\r\n    .token.operator,\r\n    .token.boolean,\r\n    .token.number\r\n      color: #c76b29\r\n\r\n    .token.property\r\n      color: #c08b30\r\n\r\n    .token.tag\r\n      color: #3d8fd1\r\n\r\n    .token.string\r\n      color: #22a2c9\r\n\r\n    .token.selector\r\n      color: #6679cc\r\n\r\n    .token.attr-name\r\n      color: #c76b29\r\n\r\n    .token.entity,\r\n    .token.url,\r\n    .language-css .token.string,\r\n    .style .token.string\r\n      color: #22a2c9\r\n\r\n    .token.attr-value,\r\n    .token.keyword,\r\n    .token.control,\r\n    .token.directive,\r\n    .token.unit\r\n      color: #ac9739\r\n\r\n    .token.statement,\r\n    .token.regex,\r\n    .token.atrule\r\n      color: #22a2c9\r\n\r\n    .token.placeholder,\r\n    .token.variable\r\n      color: #3d8fd1\r\n\r\n    .token.deleted\r\n      text-decoration: line-through\r\n\r\n    .token.inserted\r\n      border-bottom: 1px dotted #202746\r\n      text-decoration: none\r\n\r\n    .token.italic\r\n      font-style: italic\r\n\r\n    .token.important,\r\n    .token.bold\r\n      font-weight: bold\r\n\r\n    .token.important\r\n      color: #c94922\r\n\r\n    .token.entity\r\n      cursor: help\r\n\r\n    pre > code.highlight\r\n      outline: .4em solid #c94922\r\n      outline-offset: .4em\r\n\r\nif $highlight_theme == 'darker'\r\n  // prism-atom-dark\r\n  pre[class*='language-']\r\n    .token.comment,\r\n    .token.prolog,\r\n    .token.doctype,\r\n    .token.cdata\r\n      color: #7C7C7C\r\n\r\n    .token.punctuation\r\n      color: #c5c8c6\r\n\r\n    .namespace\r\n      opacity: .7\r\n\r\n    .token.property,\r\n    .token.keyword,\r\n    .token.tag\r\n      color: #96CBFE\r\n\r\n    .token.class-name\r\n      color: #FFFFB6\r\n\r\n    .token.boolean,\r\n    .token.constant\r\n      color: #99CC99\r\n\r\n    .token.symbol,\r\n    .token.deleted\r\n      color: #f92672\r\n\r\n    .token.number\r\n      color: #FF73FD\r\n\r\n    .token.selector,\r\n    .token.attr-name,\r\n    .token.string,\r\n    .token.char,\r\n    .token.builtin,\r\n    .token.inserted\r\n      color: #A8FF60\r\n\r\n    .token.variable\r\n      color: #C6C5FE\r\n\r\n    .token.operator\r\n      color: #EDEDED\r\n\r\n    .token.entity\r\n      color: #FFFFB6\r\n      cursor: help\r\n\r\n    .token.url\r\n      color: #96CBFE\r\n\r\n    .language-css .token.string,\r\n    .style .token.string\r\n      color: #87C38A\r\n\r\n    .token.atrule,\r\n    .token.attr-value\r\n      color: #F9EE98\r\n\r\n    .token.function\r\n      color: #DAD085\r\n\r\n    .token.regex\r\n      color: #E9C062\r\n\r\n    .token.important\r\n      color: #fd971f\r\n\r\n    .token.important,\r\n    .token.bold\r\n      font-weight: bold\r\n\r\n    .token.italic\r\n      font-style: italic\r\n\r\nif $highlight_theme == 'pale night'\r\n  // prism-dracula\r\n  pre[class*='language-']\r\n    .token.comment,\r\n    .token.prolog,\r\n    .token.doctype,\r\n    .token.cdata\r\n      color: #6272a4\r\n\r\n    .token.punctuation\r\n      color: #f8f8f2\r\n\r\n    .namespace\r\n      opacity: .7\r\n\r\n    .token.property,\r\n    .token.tag,\r\n    .token.constant,\r\n    .token.symbol,\r\n    .token.deleted\r\n      color: #ff79c6\r\n\r\n    .token.boolean,\r\n    .token.number\r\n      color: #bd93f9\r\n\r\n    .token.selector,\r\n    .token.attr-name,\r\n    .token.string,\r\n    .token.char,\r\n    .token.builtin,\r\n    .token.inserted\r\n      color: #50fa7b\r\n\r\n    .token.operator,\r\n    .token.entity,\r\n    .token.url,\r\n    .language-css .token.string,\r\n    .style .token.string,\r\n    .token.variable\r\n      color: #f8f8f2\r\n\r\n    .token.atrule,\r\n    .token.attr-value,\r\n    .token.function,\r\n    .token.class-name\r\n      color: #f1fa8c\r\n\r\n    .token.keyword\r\n      color: #8be9fd\r\n\r\n    .token.regex,\r\n    .token.important\r\n      color: #ffb86c\r\n\r\n    .token.important,\r\n    .token.bold\r\n      font-weight: bold\r\n\r\n    .token.italic\r\n      font-style: italic\r\n\r\n    .token.entity\r\n      cursor: help\r\n\r\nif $highlight_theme == 'ocean'\r\n  // prism-material-oceanic\r\n  pre[class*='language-']\r\n    &.language-css > code,\r\n    &.language-sass > code,\r\n    &.language-scss > code\r\n      color: #fd9170 !important\r\n\r\n    .namespace\r\n      opacity: .7\r\n\r\n    .token.atrule,\r\n    .token.symbol,\r\n    .token.constant,\r\n    .token.boolean,\r\n    .token.function\r\n      color: #c792ea\r\n\r\n    .token.attr-name,\r\n    .token.builtin,\r\n    .token.class\r\n      color: #ffcb6b\r\n\r\n    .token.attr-value,\r\n    .token.attribute,\r\n    .token.pseudo-class,\r\n    .token.pseudo-element,\r\n    .token.string\r\n      color: #c3e88d\r\n\r\n    .token.cdata,\r\n    .token.property,\r\n    .token.char,\r\n    .token.inserted\r\n      color: #80cbc4\r\n\r\n    .token.class-name,\r\n    .token.color,\r\n    .token.hexcode,\r\n    .token.regex\r\n      color: #f2ff00\r\n\r\n    .token.comment,\r\n    .token.prolog,\r\n    .token.doctype\r\n      color: #546e7a\r\n\r\n    .token.deleted,\r\n    .token.variable,\r\n    .token.entity,\r\n    .token.selector,\r\n    .token.tag,\r\n    .token.unit\r\n      color: #f07178\r\n\r\n    .token.id\r\n      color: #c792ea\r\n      font-weight: bold\r\n\r\n    .token.important\r\n      color: #c792ea\r\n      font-weight: bold\r\n\r\n    .token.keyword\r\n      color: #c792ea\r\n      font-style: italic\r\n\r\n    .token.number,\r\n    .token.url\r\n      color: #fd9170\r\n\r\n    .token.operator,\r\n    .token.punctuation\r\n      color: #89ddff"
  },
  {
    "path": "source/css/_highlight/prismjs/index.styl",
    "content": "if $prismjs_line_number\r\n  @require 'line-number'\r\n\r\nif $highlight_theme != false\r\n  @require 'diff'\r\n\r\n.container\r\n  pre[class*='language-']\r\n    @extend $scrollbar-style\r\n\r\n    &:not(.line-numbers)\r\n      padding: 10px 20px\r\n\r\n    .caption\r\n      margin-left: -3.8em\r\n      padding: 4px 16px !important\r\n\r\n      a\r\n        padding: 0 !important\r\n"
  },
  {
    "path": "source/css/_highlight/prismjs/line-number.styl",
    "content": ".container\r\n  pre[class*='language-']\r\n    &.line-numbers\r\n      position: relative\r\n      padding-left: 3.8em\r\n      counter-reset: linenumber\r\n      line-height: $line-height-code-block\r\n\r\n      > code\r\n        position: relative\r\n        line-height: $line-height-code-block\r\n\r\n        if hexo-config('code_blocks.word_wrap')\r\n          white-space: pre-wrap\r\n        else\r\n          white-space: inherit\r\n          word-wrap: normal\r\n          word-break: normal\r\n          overflow-wrap: normal\r\n\r\n      .line-numbers-rows\r\n        position: absolute\r\n        top: 0\r\n        left: -3.8em\r\n        width: 3em\r\n        letter-spacing: -1px\r\n        font-size: 100%\r\n        pointer-events: none\r\n        user-select: none\r\n        -webkit-user-select: none\r\n\r\n        & > span\r\n          display: block\r\n          counter-increment: linenumber\r\n          pointer-events: none\r\n\r\n          &:before\r\n            display: block\r\n            padding-right: .8em\r\n            color: var(--hlnumber-color)\r\n            content: counter(linenumber)\r\n            text-align: right\r\n"
  },
  {
    "path": "source/css/_highlight/theme.styl",
    "content": "if $highlight_theme == 'darker'\r\n  $highlight-background = #212121\r\n  $highlight-selection = #61616150\r\n  $highlight-foreground = #EEFFFF\r\n  $highlight-mac-border = rgba(0, 0, 0, .4)\r\n  $highlight-gutter = {\r\n    color: alpha($highlight-foreground, .5),\r\n    bg-color: $highlight-background\r\n  }\r\n  $highlight-tools = {\r\n    color: alpha($highlight-foreground, .8),\r\n    bg-color: darken($highlight-background, 2)\r\n  }\r\n  $highlight-scrollbar = lighten($highlight-background, 8)\r\n\r\n  if $highlight_enable\r\n    $highlight-comment = #969896\r\n    $highlight-red = #FF5370\r\n    $highlight-orange = #F78C6C\r\n    $highlight-yellow = #FFCB6B\r\n    $highlight-green = #C3E88D\r\n    $highlight-aqua = #89DDFF\r\n    $highlight-blue = #82AAFF\r\n    $highlight-purple = #C792EA\r\n    $highlight-deletion = #BF42BF\r\n    $highlight-addition = #105EDE\r\n\r\nif $highlight_theme == 'pale night'\r\n  $highlight-background = #292D3E\r\n  $highlight-selection = #717CB450\r\n  $highlight-foreground = #A6ACCD\r\n  $highlight-mac-border = rgba($highlight-background, .4)\r\n  $highlight-gutter = {\r\n    color: alpha($highlight-foreground, .5),\r\n    bg-color: $highlight-background\r\n  }\r\n  $highlight-tools = {\r\n    color: $highlight-foreground,\r\n    bg-color: darken($highlight-background, 2)\r\n  }\r\n  $highlight-scrollbar = lighten($highlight-background, 8)\r\n\r\n  if $highlight_enable\r\n    $highlight-comment = #676E95\r\n    $highlight-red = #FF5370\r\n    $highlight-orange = #F78C6C\r\n    $highlight-yellow = #FFCB6B\r\n    $highlight-green = #C3E88D\r\n    $highlight-aqua = #89DDFF\r\n    $highlight-blue = #82AAFF\r\n    $highlight-purple = #C792EA\r\n    $highlight-deletion = #BF42BF\r\n    $highlight-addition = #105EDE\r\n\r\nif $highlight_theme == 'ocean'\r\n  $highlight-background = #0F111A\r\n  $highlight-selection = #717CB450\r\n  $highlight-foreground = #8F93A2\r\n  $highlight-mac-border = rgba($highlight-background, .4)\r\n  $highlight-gutter = {\r\n    color: alpha($highlight-foreground, .5),\r\n    bg-color: $highlight-background\r\n  }\r\n  $highlight-tools = {\r\n    color: $highlight-foreground,\r\n    bg-color: darken($highlight-background, 2)\r\n  }\r\n  $highlight-scrollbar = lighten($highlight-background, 8)\r\n\r\n  if $highlight_enable\r\n    $highlight-comment = rgba(101, 115, 126, .8)\r\n    $highlight-red = #FF5370\r\n    $highlight-orange = #F78C6C\r\n    $highlight-yellow = #FFCB6B\r\n    $highlight-green = #C3E88D\r\n    $highlight-aqua = #89DDFF\r\n    $highlight-blue = #82AAFF\r\n    $highlight-purple = #C792EA\r\n    $highlight-deletion = #BF42BF\r\n    $highlight-addition = #105EDE\r\n\r\nif $highlight_theme == 'light'\r\n  $highlight-background = #F6F8FA\r\n  $highlight-selection = #80CBC440\r\n  $highlight-foreground = #90A4AE\r\n  $highlight-mac-border = rgba(144, 164, 174, .4)\r\n  $highlight-tools = {\r\n    color: $highlight-foreground,\r\n    bg-color: darken($highlight-background, 5)\r\n  }\r\n  $highlight-gutter = {\r\n    color: alpha($highlight-foreground, .5),\r\n    bg-color: $highlight-background\r\n  }\r\n  $highlight-scrollbar = darken($highlight-background, 8)\r\n\r\n  if $highlight_enable\r\n    $highlight-comment = rgba(149, 165, 166, .8)\r\n    $highlight-red = #E53935\r\n    $highlight-orange = #F76D47\r\n    $highlight-yellow = #FFB62C\r\n    $highlight-green = #91B859\r\n    $highlight-aqua = #39ADB5\r\n    $highlight-blue = #6182B8\r\n    $highlight-purple = #7C4DFF\r\n    $highlight-deletion = #BF42BF\r\n    $highlight-addition = #105EDE\r\n\r\nif $highlight_theme == false\r\n  $highlight-background = #F6F8FA\r\n  $highlight-foreground = #90A4AE\r\n  $highlight-selection = #80CBC440\r\n  $highlight-gutter = {\r\n    color: alpha($highlight-foreground, .5),\r\n    bg-color: $highlight-background\r\n  }\r\n  $highlight-tools = {\r\n    color: $highlight-foreground,\r\n    bg-color: darken($highlight-background, 5)\r\n  }\r\n  $highlight-scrollbar = darken($highlight-background, 8)\r\n"
  },
  {
    "path": "source/css/_layout/aside.styl",
    "content": "#aside-content\r\n  width: 26%\r\n\r\n  +minWidth900()\r\n    if hexo-config('aside.position') == 'right'\r\n      padding-left: 15px\r\n    else\r\n      padding-right: 15px\r\n\r\n  +maxWidth900()\r\n    margin-top: 20px\r\n    width: 100%\r\n\r\n  .card-widget\r\n    @extend .cardHover\r\n    position: relative\r\n    overflow: hidden\r\n    margin-bottom: 20px\r\n    padding: 20px 24px\r\n\r\n    if hexo-config('aside.mobile') == false\r\n      +maxWidth768()\r\n        &:not(#card-toc)\r\n          display: none\r\n\r\n    // &:last-child\r\n    //   margin-bottom: 0\r\n\r\n  .card-info\r\n    .author-info\r\n      &-name\r\n        font-weight: 500\r\n        font-size: 1.57em\r\n\r\n      &-description\r\n        margin-top: -.42em\r\n\r\n    .site-data\r\n      margin: 14px 0 4px\r\n\r\n    .card-info-social-icons\r\n      margin: 6px 0 -6px\r\n\r\n      .social-icon\r\n        margin: 0 10px\r\n        color: var(--font-color)\r\n        font-size: 1.4em\r\n\r\n      i\r\n        transition: all .3s\r\n\r\n        &:hover\r\n          transform: rotate(360deg)\r\n\r\n    #card-info-btn\r\n      display: block\r\n      margin-top: 14px\r\n      background-color: var(--btn-bg)\r\n      color: var(--btn-color)\r\n      text-align: center\r\n      line-height: 2.4\r\n      addBorderRadius(7)\r\n      @extend .btn-effects\r\n\r\n      &:hover\r\n        background-color: var(--btn-hover-color)\r\n\r\n      span\r\n        padding-left: 10px\r\n\r\n  .item-headline\r\n    padding-bottom: 6px\r\n    font-size: 1.2em\r\n\r\n    span\r\n      margin-left: 6px\r\n\r\n  .sticky_layout\r\n    +minWidth900()\r\n      position: sticky\r\n      position: -webkit-sticky\r\n      top: 20px\r\n      transition: top .3s\r\n\r\n  .card-tag-cloud\r\n    a\r\n      display: inline-block\r\n      padding: 0 4px\r\n      line-height: 1.8\r\n\r\n      &:hover\r\n        color: $text-hover !important\r\n\r\n  .aside-list\r\n    & > span\r\n      display: block\r\n      margin-bottom: 10px\r\n      text-align: center\r\n\r\n    & > .aside-list-item\r\n      display: flex\r\n      align-items: center\r\n      padding: 6px 0\r\n\r\n      &:first-child\r\n        padding-top: 0\r\n\r\n      &:not(:last-child)\r\n        border-bottom: 1px dashed #f5f5f5\r\n\r\n      &:last-child\r\n        padding-bottom: 0\r\n\r\n      .thumbnail\r\n        overflow: hidden\r\n        width: w = 4em\r\n        height: w\r\n        addBorderRadius()\r\n\r\n        :first-child\r\n          @extend .imgHover\r\n\r\n      .content\r\n        flex: 1\r\n        padding-left: 10px\r\n        word-break: break-all\r\n\r\n        & > .name\r\n          @extend .limit-more-line\r\n          -webkit-line-clamp: 1\r\n\r\n        & > time,\r\n        & > .name\r\n          display: block\r\n          color: var(--card-meta)\r\n          font-size: .85em\r\n\r\n        & > .title,\r\n        & > .comment\r\n          @extend .limit-more-line\r\n          color: var(--font-color)\r\n          // font-size: 95%\r\n          line-height: 1.5\r\n          -webkit-line-clamp: 2\r\n\r\n          &:hover\r\n            color: $text-hover\r\n\r\n      &.no-cover\r\n        min-height: 4.4em\r\n\r\n  .card-archives ul.card-archive-list,\r\n  .card-categories ul.card-category-list\r\n    margin: 0\r\n    padding: 0\r\n    list-style: none\r\n\r\n  .card-archives ul.card-archive-list > .card-archive-list-item,\r\n  .card-categories ul.card-category-list > .card-category-list-item\r\n    a\r\n      display: flex\r\n      flex-direction: row\r\n      margin: 2px 0\r\n      padding: 2px 8px\r\n      color: var(--font-color)\r\n      transition: all .3s\r\n      addBorderRadius()\r\n\r\n      &:hover\r\n        padding: 2px 12px\r\n        background-color: var(--text-bg-hover)\r\n        color: var(--white)\r\n\r\n      span\r\n        @extend .limit-one-line\r\n\r\n        &:first-child\r\n          flex: 1\r\n\r\n  .card-categories\r\n    .card-category-list\r\n      &.child\r\n        padding: 0 0 0 16px\r\n        overflow: hidden\r\n        max-height: 0\r\n        opacity: 0\r\n        visibility: hidden\r\n        transition: max-height .3s ease, opacity .3s ease\r\n\r\n      > .parent\r\n        > a\r\n          &.expand\r\n            i\r\n              transform: rotate(-90deg)\r\n\r\n            & + .child\r\n              max-height: 1000px\r\n              opacity: 1\r\n              visibility: visible\r\n\r\n          .card-category-list\r\n            &-name\r\n              width: 70% !important\r\n\r\n            &-count\r\n              width: calc(100% - 70% - 20px)\r\n              text-align: right\r\n\r\n          i\r\n            float: right\r\n            margin-right: -.5em\r\n            padding: .5em\r\n            transition: transform .3s\r\n            transform: rotate(0)\r\n\r\n        if hexo-config('aside.card_categories.expand') == false\r\n          > .child\r\n            max-height: 0\r\n            opacity: 0\r\n            visibility: hidden\r\n\r\n  .card-webinfo\r\n    .webinfo\r\n      .webinfo-item\r\n        display: flex\r\n        align-items: center\r\n        padding: 2px 10px 0\r\n\r\n        div\r\n          &:first-child\r\n            flex: 1\r\n            padding-right: 20px\r\n\r\n  // toc\r\n  #card-toc\r\n    +minWidth901()\r\n      right: 0 !important\r\n\r\n    +maxWidth900()\r\n      position: fixed\r\n      right: 55px\r\n      bottom: 30px\r\n      z-index: 100\r\n      max-width: $toc-mobile-maxWidth\r\n      max-height: calc(100% - 60px)\r\n      width: $toc-mobile-width\r\n      transition: none\r\n      transform: scale(0)\r\n      transform-origin: right bottom\r\n\r\n      &.open\r\n        transform: scale(1)\r\n\r\n    .toc-percentage\r\n      float: right\r\n      margin-top: -9px\r\n      color: #a9a9a9\r\n      font-style: italic\r\n      font-size: 140%\r\n\r\n    .toc-content\r\n      overflow-y: scroll\r\n      overflow-y: overlay\r\n      margin: 0 -24px\r\n      max-height: calc(100vh - 120px)\r\n      width: calc(100% + 48px)\r\n\r\n      +maxWidth900()\r\n        max-height: calc(100vh - 140px)\r\n\r\n      & > *\r\n        margin: 0 20px !important\r\n\r\n        & > .toc-item > .toc-child\r\n          margin-left: 10px\r\n          padding-left: 10px\r\n          border-left: 1px solid var(--dark-grey)\r\n\r\n      &:not(.is-expand)\r\n        .toc-child\r\n          display: none\r\n\r\n          +maxWidth900()\r\n            display: block !important\r\n\r\n        .toc-item\r\n          &.active\r\n            .toc-child\r\n              display: block\r\n\r\n      ol,\r\n      li\r\n        list-style: none\r\n\r\n      > ol\r\n        padding: 0 !important\r\n\r\n      ol\r\n        margin: 0\r\n        padding-left: 18px\r\n\r\n      .toc-link\r\n        display: block\r\n        margin: 4px 0\r\n        padding: 1px 8px\r\n        color: var(--toc-link-color)\r\n        transition: all .2s ease-in-out\r\n        addBorderRadius()\r\n\r\n        &:hover\r\n          color: $theme-color\r\n\r\n        &.active\r\n          background: $theme-toc-color\r\n          color: $toc-active-color\r\n\r\n  .sticky_layout:only-child\r\n    > :first-child\r\n      margin-top: 0\r\n\r\n  .card-more-btn\r\n    float: right\r\n    color: inherit\r\n\r\n    &:hover\r\n      animation: more-btn-move 1s infinite\r\n\r\n  .card-announcement\r\n    .item-headline\r\n      i\r\n        color: #FF0000\r\n\r\n.avatar-img\r\n  overflow: hidden\r\n  margin: 0 auto\r\n  width: 110px\r\n  height: 110px\r\n  border-radius: 70px\r\n\r\n  img\r\n    width: 100%\r\n    height: 100%\r\n    transition: filter 375ms ease-in .2s, transform .3s\r\n    object-fit: cover\r\n\r\n    &:hover\r\n      transform: rotate(360deg)\r\n\r\n.site-data\r\n  display: table\r\n  width: 100%\r\n  table-layout: fixed\r\n\r\n  & > a\r\n    display: table-cell\r\n\r\n    div\r\n      transition: all .3s\r\n\r\n    &:hover\r\n      div\r\n        color: $theme-color !important\r\n\r\n    .headline\r\n      @extend .limit-one-line\r\n      color: var(--font-color)\r\n      font-size: .95em\r\n\r\n    .length-num\r\n      margin-top: -.45em\r\n      color: var(--text-highlight-color)\r\n      font-size: 1.2em\r\n\r\n@keyframes more-btn-move\r\n  0%,\r\n  100%\r\n    transform: translateX(0)\r\n\r\n  50%\r\n    transform: translateX(3px)\r\n\r\n@keyframes toc-open\r\n  0%\r\n    transform: scale(.7)\r\n\r\n  100%\r\n    transform: scale(1)\r\n\r\n@keyframes toc-close\r\n  0%\r\n    transform: scale(1)\r\n\r\n  100%\r\n    transform: scale(.7)\r\n\r\n+minWidth900()\r\n  html.hide-aside\r\n    .layout\r\n      justify-content: center\r\n\r\n      > .aside-content\r\n        display: none\r\n\r\n      > div:first-child\r\n        width: 80%\r\n\r\n.page\r\n  .sticky_layout\r\n    display: flex\r\n    flex-direction: column\r\n\r\n  if hexo-config('aside.card_recent_post.sort_order')\r\n    .card-recent-post\r\n      order: hexo-config('aside.card_recent_post.sort_order')\r\n\r\n  if hexo-config('aside.card_newest_comments.sort_order')\r\n    #card-newest-comments\r\n      order: hexo-config('aside.card_newest_comments.sort_order')\r\n\r\n  if hexo-config('aside.card_categories.sort_order')\r\n    .card-categories\r\n      order: hexo-config('aside.card_categories.sort_order')\r\n\r\n  if hexo-config('aside.card_tags.sort_order')\r\n    .card-tags\r\n      order: hexo-config('aside.card_tags.sort_order')\r\n\r\n  if hexo-config('aside.card_archives.sort_order')\r\n    .card-archives\r\n      order: hexo-config('aside.card_archives.sort_order')\r\n\r\n  if hexo-config('aside.card_webinfo.sort_order')\r\n    .card-webinfo\r\n      order: hexo-config('aside.card_webinfo.sort_order')\r\n"
  },
  {
    "path": "source/css/_layout/chat.styl",
    "content": "// chat\r\nif hexo-config('chat.rightside_button') == true\r\n  if hexo-config('chat.use') == 'chatra'\r\n    #chatra:not(.chatra--expanded)\r\n      visibility: hidden !important\r\n      width: 1px !important\r\n      height: 1px !important\r\n      opacity: 0 !important\r\n      pointer-events: none"
  },
  {
    "path": "source/css/_layout/comments.styl",
    "content": "#post-comment\r\n  .comment-head\r\n    margin-bottom: 20px\r\n\r\n    &:after\r\n      display: block\r\n      clear: both\r\n      content: ''\r\n\r\n    .comment-headline\r\n      display: inline-block\r\n      vertical-align: middle\r\n      font-weight: 700\r\n      font-size: 1.43em\r\n\r\n    .comment-switch\r\n      display: inline-block\r\n\r\n      if hexo-config('comments.text')\r\n        float: right\r\n        margin: 2px auto 0\r\n        padding: 4px 16px\r\n        width: max-content\r\n        border-radius: 8px\r\n        background: $comments-switch-bg\r\n      else\r\n        vertical-align: middle\r\n\r\n        > span\r\n          display: none\r\n\r\n      .first-comment\r\n        color: $comments-switch-first-text\r\n\r\n      .second-comment\r\n        color: $comments-switch-second-text\r\n\r\n      #switch-btn\r\n        position: relative\r\n        display: inline-block\r\n        margin: -4px 8px 0\r\n        width: 42px\r\n        height: 22px\r\n        border-radius: 34px\r\n        background-color: $comments-switch-first-text\r\n        vertical-align: middle\r\n        cursor: pointer\r\n        transition: .4s\r\n\r\n        &:before\r\n          position: absolute\r\n          bottom: 4px\r\n          left: 4px\r\n          width: 14px\r\n          height: 14px\r\n          border-radius: 50%\r\n          background-color: $comments-switch-round\r\n          content: ''\r\n          transition: .4s\r\n\r\n  .comment-wrap\r\n    > div\r\n      animation: tabshow .5s\r\n\r\n      &:nth-child(2)\r\n        display: none\r\n\r\n  &.move\r\n    #switch-btn\r\n      background-color: $comments-switch-second-text\r\n\r\n      &:before\r\n        transform: translateX(20px)\r\n\r\n    .comment-wrap\r\n      > div\r\n        &:first-child\r\n          display: none\r\n\r\n        &:last-child\r\n          display: block"
  },
  {
    "path": "source/css/_layout/footer.styl",
    "content": "#footer\r\n  position: relative\r\n  background-color: $light-blue\r\n  background-attachment: scroll\r\n  background-position: bottom\r\n  background-size: cover\r\n\r\n  if hexo-config('footer_img') != false && hexo-config('mask.footer')\r\n    &:before\r\n      position: absolute\r\n      width: 100%\r\n      height: 100%\r\n      background-color: var(--mark-bg)\r\n      content: ''\r\n\r\n  & > *\r\n    position: relative\r\n    color: var(--light-grey)\r\n\r\n  a\r\n    color: var(--light-grey)\r\n    transition: all .3s ease-in-out\r\n\r\n    &:hover\r\n      color: $light-blue\r\n\r\n  .footer-separator\r\n    margin: 0 4px\r\n\r\n  .icp-icon\r\n    padding: 0 4px\r\n    max-height: 1.4em\r\n    width: auto\r\n    vertical-align: text-bottom\r\n\r\n  .footer-flex\r\n    display: flex\r\n    flex-direction: row\r\n    flex-wrap: wrap\r\n    justify-content: space-between\r\n    margin: 0 auto\r\n    padding: 40px 60px\r\n    max-width: 1200px\r\n    width: 100%\r\n    text-align: left\r\n    gap: 13px\r\n\r\n    +maxWidth768()\r\n      padding: 30px\r\n      gap: 10px\r\n\r\n    .footer-flex-items\r\n      flex-shrink: 0\r\n      min-width: 100px\r\n      text-align: left\r\n      white-space: nowrap\r\n\r\n    .footer-flex-title\r\n      margin-bottom: 5px\r\n      white-space: nowrap\r\n      font-weight: 600\r\n      font-size: 1.4em\r\n\r\n    .footer-flex-item\r\n      margin: 10px 0\r\n      white-space: nowrap\r\n\r\n    a\r\n      display: block\r\n      white-space: nowrap\r\n\r\n  .footer-other\r\n    padding: 40px 20px\r\n    width: 100%\r\n    text-align: center\r\n\r\n    if hexo-config('footer.nav')\r\n      padding: 10px 8px\r\n      background-color: rgba(0, 0, 0, .1)\r\n\r\n      .copyright,\r\n      .framework-info,\r\n      .footer_custom_text\r\n        font-size: .9em\r\n    else\r\n      .framework-info\r\n        display: block"
  },
  {
    "path": "source/css/_layout/head.styl",
    "content": "#page-header\r\n  position: relative\r\n  width: 100%\r\n  background-color: $light-blue\r\n  background-position: center center\r\n  background-size: cover\r\n  background-repeat: no-repeat\r\n  transition: all .5s\r\n\r\n  if hexo-config('mask.header')\r\n    &:not(.not-top-img):before\r\n      position: absolute\r\n      width: 100%\r\n      height: 100%\r\n      background-color: var(--mark-bg)\r\n      content: ''\r\n\r\n  // index\r\n  &.full_page\r\n    height: $index_top_img_height\r\n    background-attachment: fixed\r\n\r\n    #site-info\r\n      position: absolute\r\n      top: $index_site_info_top\r\n      padding: 0 10px\r\n      width: 100%\r\n\r\n  #site-title,\r\n  #site-subtitle,\r\n  #scroll-down .scroll-down-effects\r\n    text-align: center\r\n    text-shadow: 2px 2px 4px rgba(0, 0, 0, .15)\r\n    line-height: 1.5\r\n\r\n  #site-title\r\n    margin: 0\r\n    color: var(--white)\r\n    font-size: 1.85em\r\n    @extend .limit-more-line\r\n    -webkit-line-clamp: 3\r\n\r\n    +minWidth768()\r\n      font-size: 2.85em\r\n\r\n  #site-subtitle\r\n    color: var(--light-grey)\r\n    font-size: 1.15em\r\n\r\n    +minWidth768()\r\n      font-size: 1.72em\r\n\r\n  #site_social_icons\r\n    display: none\r\n    margin: 0 auto\r\n    text-align: center\r\n\r\n    +maxWidth768()\r\n      display: block\r\n\r\n    .social-icon\r\n      margin: 0 10px\r\n      color: var(--light-grey)\r\n      text-shadow: 2px 2px 4px rgba(0, 0, 0, .15)\r\n      font-size: 1.43em\r\n\r\n  #scroll-down\r\n    position: absolute\r\n    bottom: 10px\r\n    width: 100%\r\n    cursor: pointer\r\n\r\n    .scroll-down-effects\r\n      position: relative\r\n      width: 100%\r\n      color: var(--light-grey)\r\n      font-size: 20px\r\n\r\n  // page\r\n  &.not-home-page\r\n    height: 400px\r\n\r\n    +maxWidth768()\r\n      height: 280px\r\n\r\n  #page-site-info\r\n    position: absolute\r\n    top: 200px\r\n    padding: 0 10px\r\n    width: 100%\r\n\r\n    +maxWidth768()\r\n      top: 140px\r\n\r\n  // post\r\n  &.post-bg\r\n    height: 400px\r\n\r\n    +maxWidth768()\r\n      height: 360px\r\n\r\n  #post-info\r\n    position: absolute\r\n    width: 100%\r\n\r\n    if hexo-config('post_meta.post.position') == 'center'\r\n      top: calc(50% + 30px)\r\n      padding: 0 8%\r\n      text-align: center\r\n      transform: translateY(-50%)\r\n\r\n      +maxWidth768()\r\n        padding: 0 15px\r\n    else\r\n      bottom: 30px\r\n\r\n      & > *\r\n        margin: 0 auto\r\n        padding: 0 15px\r\n        max-width: 1200px\r\n\r\n        @media screen and (min-width: 768px) and (max-width: 1300px)\r\n          padding: 0 30px\r\n\r\n        +minWidth2000()\r\n          max-width: 70%\r\n\r\n  &.not-top-img\r\n    margin-bottom: 10px\r\n    height: 60px\r\n    background: 0\r\n\r\n    .title-seo\r\n      display: none\r\n\r\n    #nav\r\n      background: rgba(255, 255, 255, .8)\r\n      box-shadow: 0 5px 6px -5px rgba(133, 133, 133, .6)\r\n\r\n      a,\r\n      span.site-page,\r\n      .site-name\r\n        color: var(--font-color)\r\n        text-shadow: none\r\n\r\n  &.nav-fixed\r\n    #nav\r\n      position: fixed\r\n      top: -60px\r\n      z-index: 91\r\n      background: rgba(255, 255, 255, .7)\r\n      box-shadow: 0 5px 6px -5px alpha($grey, .6)\r\n      transition: transform .2s ease-in-out, opacity .2s ease-in-out\r\n      will-change: transform\r\n      backdrop-filter: blur(7px)\r\n\r\n      #blog-info\r\n        color: var(--font-color)\r\n\r\n        &:hover\r\n          color: $light-blue\r\n\r\n        .site-name\r\n          text-shadow: none\r\n\r\n        & > a:first-child\r\n          display: none\r\n\r\n        & > a:last-child\r\n          display: inline\r\n\r\n      a,\r\n      span.site-page,\r\n      #toggle-menu\r\n        color: var(--font-color)\r\n        text-shadow: none\r\n\r\n        &:hover\r\n          color: $light-blue\r\n\r\n    &.fixed\r\n      #nav\r\n        top: 0\r\n        transition: all .5s\r\n\r\n  &.nav-visible:not(.fixed)\r\n    #nav\r\n      transition: all .5s\r\n      transform: translate3d(0, 100%, 0)\r\n\r\n    & + .layout\r\n      & > .aside-content > .sticky_layout\r\n        top: 70px\r\n        transition: top .5s\r\n\r\n  &.fixed\r\n    #nav\r\n      position: fixed\r\n\r\n    & + .layout\r\n      & > .aside-content > .sticky_layout\r\n        top: 70px\r\n        transition: top .5s\r\n\r\n      #card-toc\r\n        .toc-content\r\n          max-height: calc(100vh - 170px)\r\n\r\n#page\r\n  .page-title\r\n    margin: 0 0 10px\r\n    font-weight: bold\r\n    font-size: 2em\r\n\r\n// for not top_img\r\n#post\r\n  & > #post-info\r\n    margin-bottom: 30px\r\n\r\n    .post-title\r\n      padding-bottom: 4px\r\n      border-bottom: 1px solid var(--light-grey)\r\n      color: var(--text-highlight-color)\r\n\r\n      .post-edit-link\r\n        float: right\r\n\r\n    #post-meta,\r\n    #post-meta a\r\n      color: #78818a\r\n\r\n#post-info\r\n  .post-title\r\n    @extend .limit-more-line\r\n    margin-bottom: 8px\r\n    color: var(--white)\r\n    font-weight: normal\r\n    font-size: 2.5em\r\n    line-height: 1.5\r\n    -webkit-line-clamp: 3\r\n\r\n    +maxWidth768()\r\n      font-size: 2.1em\r\n\r\n    .post-edit-link\r\n      padding-left: 10px\r\n\r\n  #post-meta\r\n    color: var(--light-grey)\r\n    font-size: 95%\r\n\r\n    +minWidth768()\r\n      > .meta-secondline\r\n        > span:first-child\r\n          display: none\r\n\r\n    +maxWidth768()\r\n      font-size: 90%\r\n\r\n      > .meta-firstline,\r\n      > .meta-secondline\r\n        display: inline\r\n\r\n    .post-meta\r\n      &-separator\r\n        margin: 0 5px\r\n\r\n      &-icon\r\n        margin-right: 4px\r\n\r\n      &-label\r\n        if hexo-config('post_meta.post.label')\r\n          margin-right: 4px\r\n        else\r\n          display: none\r\n\r\n    a\r\n      color: var(--light-grey)\r\n      transition: all .3s ease-out\r\n\r\n      &:hover\r\n        color: $text-hover\r\n        text-decoration: underline\r\n\r\n    if hexo-config('post_meta.post.date_format') == 'relative'\r\n      time\r\n        display: none\r\n\r\n#nav\r\n  position: absolute\r\n  top: 0\r\n  z-index: 90\r\n  display: flex\r\n  align-items: center\r\n  padding: 0 36px\r\n  width: 100%\r\n  height: 60px\r\n  font-size: 1.3em\r\n  opacity: 0\r\n  transition: all .5s\r\n\r\n  +maxWidth768()\r\n    padding: 0 16px\r\n\r\n  &.show\r\n    opacity: 1\r\n\r\n  #blog-info\r\n    flex: 1\r\n    color: var(--light-grey)\r\n    @extend .limit-one-line\r\n\r\n    .site-icon\r\n      margin-right: 6px\r\n      height: 36px\r\n      vertical-align: middle\r\n\r\n    .nav-page-title\r\n      display: none\r\n\r\n  #toggle-menu\r\n    display: none\r\n    padding: 2px 0 0 6px\r\n    vertical-align: top\r\n\r\n    &:hover\r\n      color: var(--white)\r\n\r\n  a,\r\n  span.site-page\r\n    color: var(--light-grey)\r\n\r\n    &:hover\r\n      color: var(--white)\r\n\r\n  .site-name\r\n    text-shadow: 2px 2px 4px rgba($dark-black, .15)\r\n    font-weight: bold\r\n\r\n  .menus_items\r\n    display: inline\r\n\r\n    .menus_item\r\n      position: relative\r\n      display: inline-block\r\n      padding: 0 0 0 14px\r\n\r\n      &:hover\r\n        .menus_item_child\r\n          display: block\r\n\r\n        & > span > i:last-child\r\n          transform: rotate(180deg)\r\n\r\n      & > span > i:last-child\r\n        padding: 4px\r\n        transition: transform .3s\r\n\r\n      .menus_item_child\r\n        position: absolute\r\n        right: 0\r\n        display: none\r\n        margin-top: 8px\r\n        padding: 0\r\n        width: max-content\r\n        background-color: var(--sidebar-bg)\r\n        box-shadow: 0 5px 20px -4px rgba($dark-black, .5)\r\n        animation: sub_menus .3s .1s ease both\r\n        addBorderRadius(5)\r\n\r\n        &:before\r\n          position: absolute\r\n          top: -8px\r\n          left: 0\r\n          width: 100%\r\n          height: 20px\r\n          content: ''\r\n\r\n        li\r\n          list-style: none\r\n\r\n          &:hover\r\n            background: var(--text-bg-hover)\r\n\r\n          if hexo-config('rounded_corners_ui')\r\n            &:first-child\r\n              border-top-left-radius: 5px\r\n              border-top-right-radius: 5px\r\n\r\n            &:last-child\r\n              border-bottom-right-radius: 5px\r\n              border-bottom-left-radius: 5px\r\n\r\n          a\r\n            display: inline-block\r\n            padding: 8px 16px\r\n            width: 100%\r\n            color: var(--font-color) !important\r\n            text-shadow: none !important\r\n\r\n  &.hide-menu\r\n    #toggle-menu\r\n      display: inline-block !important\r\n\r\n      .site-page\r\n        font-size: inherit\r\n\r\n    .menus_items\r\n      display: none\r\n\r\n    #search-button span:not(.site-page)\r\n      display: none\r\n\r\n  #search-button\r\n    display: inline\r\n    padding: 0 0 0 14px\r\n\r\n  .site-page\r\n    position: relative\r\n    padding-bottom: 6px\r\n    text-shadow: 1px 1px 2px rgba($dark-black, .3)\r\n    font-size: .78em\r\n    cursor: pointer\r\n\r\n    &:not(.child)\r\n      &:after\r\n        position: absolute\r\n        bottom: 0\r\n        left: 0\r\n        z-index: -1\r\n        width: 0\r\n        height: 3px\r\n        background-color: lighten($theme-color, 30%)\r\n        content: ''\r\n        transition: all .3s ease-in-out\r\n        addBorderRadius()\r\n\r\n      &:hover\r\n        &:after\r\n          width: 100%\r\n\r\n  .nav-page-title\r\n    position: relative\r\n    overflow: hidden\r\n\r\n    & > :first-child,\r\n    & > :last-child\r\n      display: inline-block\r\n      transition: all .3s ease-in-out\r\n\r\n    & > :last-child\r\n      position: absolute\r\n      top: 50%\r\n      left: 0\r\n      opacity: 0\r\n      transform: translateY(-50%) translateY(-10px)\r\n\r\n    &:hover\r\n      & > :last-child\r\n        opacity: 1\r\n        transform: translateY(-50%) translateY(0)\r\n\r\n      & > :first-child\r\n        opacity: 0\r\n        transform: translateY(10px)"
  },
  {
    "path": "source/css/_layout/loading.styl",
    "content": "if hexo-config('preloader.enable') && hexo-config('preloader.source') == 1\r\n  .loading-bg\r\n    position: fixed\r\n    z-index: 1000\r\n    width: 50%\r\n    height: 100%\r\n    background-color: var(--preloader-bg)\r\n\r\n  #loading-box\r\n    .loading-left-bg\r\n      @extend .loading-bg\r\n\r\n    .loading-right-bg\r\n      @extend .loading-bg\r\n      right: 0\r\n\r\n    .spinner-box\r\n      position: fixed\r\n      z-index: 1001\r\n      display: flex\r\n      justify-content: center\r\n      align-items: center\r\n      width: 100%\r\n      height: 100vh\r\n\r\n      .configure-border-1\r\n        position: absolute\r\n        padding: 3px\r\n        width: 115px\r\n        height: 115px\r\n        background: #ffab91\r\n        animation: configure-clockwise 3s ease-in-out 0s infinite alternate\r\n\r\n      .configure-border-2\r\n        left: -115px\r\n        padding: 3px\r\n        width: 115px\r\n        height: 115px\r\n        background: rgb(63, 249, 220)\r\n        transform: rotate(45deg)\r\n        animation: configure-xclockwise 3s ease-in-out 0s infinite alternate\r\n\r\n      .loading-word\r\n        position: absolute\r\n        color: var(--preloader-color)\r\n        font-size: 16px\r\n\r\n      .configure-core\r\n        width: 100%\r\n        height: 100%\r\n        background-color: var(--preloader-bg)\r\n\r\n    &.loaded\r\n      .loading-left-bg\r\n        transition: all .5s\r\n        transform: translate(-100%, 0)\r\n\r\n      .loading-right-bg\r\n        transition: all .5s\r\n        transform: translate(100%, 0)\r\n\r\n      .spinner-box\r\n        display: none\r\n\r\n  @keyframes configure-clockwise\r\n    0%\r\n      transform: rotate(0)\r\n\r\n    25%\r\n      transform: rotate(90deg)\r\n\r\n    50%\r\n      transform: rotate(180deg)\r\n\r\n    75%\r\n      transform: rotate(270deg)\r\n\r\n    100%\r\n      transform: rotate(360deg)\r\n\r\n  @keyframes configure-xclockwise\r\n    0%\r\n      transform: rotate(45deg)\r\n\r\n    25%\r\n      transform: rotate(-45deg)\r\n\r\n    50%\r\n      transform: rotate(-135deg)\r\n\r\n    75%\r\n      transform: rotate(-225deg)\r\n\r\n    100%\r\n      transform: rotate(-315deg)\r\n"
  },
  {
    "path": "source/css/_layout/pagination.styl",
    "content": "#pagination\r\n  .pagination\r\n    margin-top: 20px\r\n    text-align: center\r\n\r\n  .page-number\r\n    &.current\r\n      background: $theme-paginator-color\r\n      color: var(--white)\r\n\r\n  .full-width\r\n    width: 100% !important\r\n\r\n  .pagination-related\r\n    height: 150px\r\n\r\n    +minWidth768()\r\n      flex: 1\r\n\r\n    .info-1\r\n      .info-item-2\r\n        -webkit-line-clamp: 1\r\n\r\n    .info-2\r\n      .info-item-1\r\n        -webkit-line-clamp: 2\r\n\r\n  &.pagination-post\r\n    overflow: hidden\r\n    margin-top: 40px\r\n    width: 100%\r\n    addBorderRadius()\r\n    display: flex\r\n\r\n    +maxWidth768()\r\n      flex-direction: column\r\n\r\n.layout\r\n  .pagination\r\n    & > *\r\n      display: inline-block\r\n      margin: 0 6px\r\n      width: w = 2.5em\r\n      height: w\r\n      line-height: w\r\n\r\n    & > *:not(.space)\r\n      @extend .cardHover\r\n\r\n      &:hover\r\n        background: var(--btn-hover-color)\r\n        color: var(--btn-color)\r\n\r\n#archive\r\n  .pagination\r\n    margin-top: 30px\r\n\r\n    & > *:not(.space)\r\n      box-shadow: none\r\n\r\n.pagination-related\r\n  position: relative\r\n  display: inline-block\r\n  overflow: hidden\r\n  background: $dark-black\r\n  vertical-align: bottom\r\n  @extend .postImgHover\r\n\r\n  &.next-post\r\n    .info\r\n      text-align: right\r\n\r\n  .info\r\n    .info-1,\r\n    .info-2\r\n      @extend .verticalCenter\r\n      padding: 20px 40px\r\n      color: var(--white)\r\n      transition: transform .3s, opacity .3s\r\n\r\n    .info-1\r\n      .info-item-1\r\n        color: var(--light-grey)\r\n        text-transform: uppercase\r\n        font-size: 90%\r\n\r\n      .info-item-2\r\n        @extend .limit-more-line\r\n        color: var(--white)\r\n        font-weight: 500\r\n\r\n    .info-2\r\n      opacity: 0\r\n      transform: translate(0, 0)\r\n\r\n      .info-item-1\r\n        @extend .limit-more-line\r\n\r\n  &:not(.no-desc):hover\r\n    .info-1\r\n      opacity: 0\r\n      transform: translate(0, -100%)\r\n\r\n    .info-2\r\n      opacity: 1\r\n      transform: translate(0, -50%)"
  },
  {
    "path": "source/css/_layout/post.styl",
    "content": "beautify()\r\n  headStyle(fontsize)\r\n    padding-left: unit(fontsize + 8, 'px')\r\n\r\n    &:before\r\n      font-size: unit(fontsize - 2, 'px')\r\n\r\n    &:hover\r\n      padding-left: unit(fontsize + 12, 'px')\r\n\r\n  h1,\r\n  h2,\r\n  h3,\r\n  h4,\r\n  h5,\r\n  h6\r\n    transition: all .2s ease-out\r\n\r\n    &:before\r\n      position: absolute\r\n      top: calc(50% - 7px)\r\n      left: 0\r\n      color: $title-prefix-icon-color\r\n      content: $title-prefix-icon\r\n      line-height: 1\r\n      transition: all .2s ease-out\r\n      @extend .fontawesomeIcon\r\n\r\n    &:hover\r\n      &:before\r\n        color: $light-blue\r\n\r\n  h1\r\n    headStyle(20)\r\n\r\n  h2\r\n    headStyle(18)\r\n\r\n  h3\r\n    headStyle(16)\r\n\r\n  h4\r\n    headStyle(14)\r\n\r\n  h5\r\n    headStyle(12)\r\n\r\n  h6\r\n    headStyle(12)\r\n\r\n  ol,\r\n  ul\r\n    p\r\n      margin: 0 0 8px\r\n\r\n  li\r\n    &::marker\r\n      color: $light-blue\r\n      font-weight: 600\r\n      font-size: 1.05em\r\n\r\n    &:hover\r\n      &::marker\r\n        color: var(--pseudo-hover)\r\n\r\n  ul > li\r\n    list-style-type: circle\r\n\r\n  hr\r\n    @extend .custom-hr\r\n\r\n.container\r\n  word-wrap: break-word\r\n  overflow-wrap: break-word\r\n\r\n  if hexo-config('text_align_justify')\r\n    text-align: justify\r\n\r\n  a\r\n    color: $theme-link-color\r\n\r\n    &:hover\r\n      text-decoration: underline\r\n\r\n  img\r\n    display: block\r\n    margin: 0 auto 20px\r\n    max-width: 100%\r\n    transition: filter 375ms ease-in .2s\r\n    addBorderRadius()\r\n\r\n  p\r\n    margin: 0 0 16px\r\n\r\n  iframe\r\n    margin: 0 0 20px\r\n\r\n  kbd\r\n    margin: 0 3px\r\n    padding: 3px 5px\r\n    border: 1px solid #b4b4b4\r\n    background-color: #f8f8f8\r\n    box-shadow: 0 1px 3px rgba(0, 0, 0, .25), 0 2px 1px 0 rgba(255, 255, 255, .6) inset\r\n    color: #34495e\r\n    white-space: nowrap\r\n    font-weight: 600\r\n    font-size: .9em\r\n    font-family: Monaco, 'Ubuntu Mono', monospace\r\n    line-height: 1em\r\n    addBorderRadius(3)\r\n\r\n  if hexo-config('anchor.click_to_scroll')\r\n    h1,\r\n    h2,\r\n    h3,\r\n    h4,\r\n    h5,\r\n    h6\r\n      width: fit-content\r\n\r\n      a:not(.headerlink)\r\n        position: relative\r\n        z-index: 10\r\n\r\n      a.headerlink\r\n        position: absolute\r\n        top: 0\r\n        right: 0\r\n        bottom: 0\r\n        left: 0\r\n        width: 100%\r\n        height: 100%\r\n\r\n  ol,\r\n  ul\r\n    ol,\r\n    ul\r\n      padding-left: 20px\r\n\r\n    li\r\n      margin: 4px 0\r\n\r\n    p\r\n      margin: 0 0 8px\r\n\r\n  > :last-child\r\n    margin-bottom: 0 !important\r\n\r\n  hr\r\n    margin: 20px 0\r\n\r\n  if hexo-config('beautify.enable')\r\n    if hexo-config('beautify.field') == 'site'\r\n      beautify()\r\n    else if hexo-config('beautify.field') == 'post'\r\n      &.post-content\r\n        beautify()\r\n\r\n#post\r\n  .tag_share\r\n    &:after\r\n      display: block\r\n      clear: both\r\n      content: ''\r\n\r\n    .post-meta\r\n      &__tag-list\r\n        display: inline-block\r\n\r\n      &__tags\r\n        display: inline-block\r\n        margin: 8px 8px 8px 0\r\n        padding: 0 12px\r\n        width: fit-content\r\n        border: 1px solid $light-blue\r\n        border-radius: 12px\r\n        color: $light-blue\r\n        font-size: .85em\r\n        transition: all .2s ease-in-out\r\n\r\n        &:hover\r\n          background: $light-blue\r\n          color: var(--white)\r\n\r\n    .post-share\r\n      display: inline-block\r\n      float: right\r\n      margin: 8px 0 0\r\n      width: fit-content\r\n\r\n      .social-share\r\n        font-size: .85em\r\n\r\n        .social-share-icon\r\n          margin: 0 4px\r\n          width: w = 1.85em\r\n          height: w\r\n          font-size: 1.2em\r\n          line-height: w\r\n\r\n  .post-copyright\r\n    position: relative\r\n    margin: 40px 0 10px\r\n    padding: 10px 16px\r\n    border: 1px solid var(--light-grey)\r\n    transition: box-shadow .3s ease-in-out\r\n    addBorderRadius()\r\n\r\n    &:before\r\n      @extend .fontawesomeIcon\r\n      position: absolute\r\n      top: 2px\r\n      right: 12px\r\n      color: $theme-color\r\n      content: '\\f1f9'\r\n      font-size: 1.3em\r\n\r\n    &:hover\r\n      box-shadow: 0 0 8px 0 rgba(232, 237, 250, .6), 0 2px 4px 0 rgba(232, 237, 250, .5)\r\n\r\n    .post-copyright\r\n      &-meta\r\n        color: $light-blue\r\n        font-weight: bold\r\n\r\n        i\r\n          margin-right: 3px\r\n\r\n      &-info\r\n        padding-left: 6px\r\n\r\n        a\r\n          text-decoration: underline\r\n          word-break: break-word\r\n\r\n          &:hover\r\n            text-decoration: none\r\n\r\n  #post-outdate-notice\r\n    position: relative\r\n    margin: 0 0 20px\r\n    padding: .5em 1.2em\r\n    background-color: $noticeOutdate-bg\r\n    color: $noticeOutdate-color\r\n    addBorderRadius(3)\r\n\r\n    .num\r\n      padding: 0 4px\r\n\r\n    if hexo-config('noticeOutdate.style') == 'flat'\r\n      padding: .5em 1em .5em 2.6em\r\n      border-left: 5px solid $noticeOutdate-border\r\n\r\n      &:before\r\n        @extend .fontawesomeIcon\r\n        position: absolute\r\n        top: 50%\r\n        left: .9em\r\n        color: $noticeOutdate-border\r\n        content: '\\f071'\r\n        transform: translateY(-50%)\r\n\r\n  .ads-wrap\r\n    margin: 40px 0\r\n"
  },
  {
    "path": "source/css/_layout/relatedposts.styl",
    "content": ".relatedPosts\r\n  margin-top: 40px\r\n\r\n  & > .headline\r\n    margin-bottom: 5px\r\n    font-weight: 700\r\n    font-size: 1.43em\r\n\r\n  & > .relatedPosts-list\r\n    & > a\r\n      margin: 3px\r\n      width: calc(33.333% - 6px)\r\n      height: 200px\r\n      addBorderRadius()\r\n\r\n      +maxWidth768()\r\n        margin: 2px\r\n        width: calc(50% - 4px)\r\n        height: 150px\r\n\r\n      +maxWidth600()\r\n        width: calc(100% - 4px)\r\n\r\n    .info\r\n      .info-1\r\n        .info-item-2\r\n          -webkit-line-clamp: 2\r\n\r\n      .info-2\r\n        .info-item-1\r\n          -webkit-line-clamp: 3"
  },
  {
    "path": "source/css/_layout/reward.styl",
    "content": ".post-reward\r\n  position: relative\r\n  margin-top: 80px\r\n  width: 100%\r\n  text-align: center\r\n  pointer-events: none\r\n\r\n  & > *\r\n    pointer-events: auto\r\n\r\n  .reward-button\r\n    display: inline-block\r\n    padding: 4px 24px\r\n    background: var(--btn-bg)\r\n    color: var(--btn-color)\r\n    cursor: pointer\r\n    addBorderRadius()\r\n    @extend .btn-effects\r\n    @extend .btn-effects-large\r\n\r\n    i\r\n      margin-right: 5px\r\n      vertical-align: baseline\r\n\r\n  &:hover\r\n    .reward-button\r\n      background: var(--btn-hover-color)\r\n\r\n    & > .reward-main\r\n      display: block\r\n\r\n  .reward-main\r\n    position: absolute\r\n    bottom: 50px\r\n    left: 0\r\n    z-index: 100\r\n    display: none\r\n    padding: 0 0 15px\r\n    width: 100%\r\n\r\n    .reward-all\r\n      display: inline-block\r\n      margin: 0\r\n      padding: 20px 10px\r\n      background: var(--reward-pop)\r\n      addBorderRadius()\r\n\r\n      &:before\r\n        position: absolute\r\n        bottom: -10px\r\n        left: 0\r\n        width: 100%\r\n        height: 20px\r\n        content: ''\r\n\r\n      &:after\r\n        position: absolute\r\n        right: 0\r\n        bottom: 2px\r\n        left: 0\r\n        margin: 0 auto\r\n        width: 0\r\n        height: 0\r\n        border-top: 13px solid var(--reward-pop)\r\n        border-right: 13px solid transparent\r\n        border-left: 13px solid transparent\r\n        content: ''\r\n\r\n      .reward-item\r\n        display: inline-block\r\n        padding: 0 8px\r\n        list-style-type: none\r\n        vertical-align: top\r\n\r\n        img\r\n          width: 130px\r\n          height: 130px\r\n\r\n        .post-qr-code-desc\r\n          width: 130px\r\n          color: $reward-pop-up-color\r\n"
  },
  {
    "path": "source/css/_layout/rightside.styl",
    "content": "#rightside\r\n  position: fixed\r\n  right: -48px\r\n  bottom: $rightside-bottom\r\n  z-index: 100\r\n  opacity: 0\r\n  transition: all .5s\r\n\r\n  &.rightside-show\r\n    opacity: .8\r\n    transform: translate(-58px, 0)\r\n\r\n  #rightside-config-hide\r\n    height: 0\r\n    opacity: 0\r\n    transition: transform .4s\r\n    transform: translate(45px, 0)\r\n\r\n    &.show\r\n      height: auto\r\n      opacity: 1\r\n      transform: translate(0, 0)\r\n\r\n    &.status\r\n      height: auto\r\n      opacity: 1\r\n\r\n  & > div\r\n    & > button,\r\n    & > a\r\n      display: block\r\n      margin-bottom: 5px\r\n      width: w = 35px\r\n      height: w\r\n      background-color: var(--btn-bg)\r\n      color: var(--btn-color)\r\n      text-align: center\r\n      font-size: 16px\r\n      line-height: w\r\n      addBorderRadius(5)\r\n      @extend .btn-effects\r\n\r\n      &:hover\r\n        background-color: var(--btn-hover-color)\r\n      \r\n      i\r\n        vertical-align: baseline\r\n\r\n  #mobile-toc-button\r\n    display: none\r\n\r\n    +maxWidth900()\r\n      display: block\r\n\r\n  +maxWidth900()\r\n    #hide-aside-btn\r\n      display: none\r\n\r\n  if hexo-config('rightside_scroll_percent')\r\n    #go-up\r\n      position: relative\r\n\r\n      .scroll-percent\r\n        position: absolute\r\n        top: 0\r\n        left: 0\r\n        display: none\r\n        width: 100%\r\n        height: 100%\r\n        opacity: 0\r\n        transition: all .4s cubic-bezier(.4, 0, .2, 1)\r\n        transform: scale(.8)\r\n\r\n      i\r\n        position: relative\r\n        z-index: 1\r\n        width: 100%\r\n        opacity: 1\r\n        transition: all .4s cubic-bezier(.4, 0, .2, 1)\r\n        transform: scale(1)\r\n\r\n      &.show-percent\r\n        .scroll-percent\r\n          display: block\r\n          opacity: 1\r\n          transform: scale(1)\r\n          animation: fadeInScale .4s ease-out\r\n\r\n        i\r\n          opacity: 0\r\n          transform: scale(.8)\r\n\r\n      &:hover.show-percent\r\n        .scroll-percent\r\n          opacity: 0\r\n          transform: scale(.8)\r\n\r\n        i\r\n          opacity: 1 !important\r\n          transform: scale(1) !important\r\n\r\n@keyframes fadeInScale\r\n  from\r\n    opacity: 0\r\n    transform: scale(.8)\r\n\r\n  to\r\n    opacity: 1\r\n    transform: scale(1)"
  },
  {
    "path": "source/css/_layout/sidebar.styl",
    "content": "#sidebar\r\n  #menu-mask\r\n    position: fixed\r\n    z-index: 102\r\n    display: none\r\n    width: 100%\r\n    height: 100%\r\n    background: alpha($dark-black, .8)\r\n\r\n  #sidebar-menus\r\n    position: fixed\r\n    top: 0\r\n    right: -($sidebar-width)\r\n    z-index: 103\r\n    overflow-x: hidden\r\n    overflow-y: scroll\r\n    padding-left: 5px\r\n    width: $sidebar-width\r\n    height: 100%\r\n    background: var(--sidebar-bg)\r\n    transition: all .5s\r\n\r\n    &.open\r\n      transform: translate3d(-100%, 0, 0)\r\n\r\n    & > .avatar-img\r\n      margin: 20px auto\r\n\r\n    .site-data\r\n      padding: 0 10px\r\n\r\n    hr\r\n      margin: 20px auto\r\n\r\n    .menus_items\r\n      margin: 20px\r\n      padding: 15px\r\n      background: var(--sidebar-menu-bg)\r\n      box-shadow: 0 0 1px 1px rgba(7, 17, 27, .05)\r\n      addBorderRadius(10)\r\n\r\n      .site-page\r\n        @extend .limit-one-line\r\n        position: relative\r\n        display: block\r\n        margin: 4px 0\r\n        padding: 2px 23px 2px 15px\r\n        color: var(--font-color)\r\n        font-size: 1.15em\r\n        cursor: pointer\r\n        addBorderRadius(6)\r\n\r\n        &:hover\r\n          background: var(--text-bg-hover)\r\n          box-shadow: 0 2px 8px rgba(0, 0, 0, .1)\r\n          color: var(--white)\r\n          transition: all .2s ease\r\n          transform: translateX(3px)\r\n\r\n        i:first-child\r\n          width: 15%\r\n          text-align: left\r\n\r\n        &.group\r\n          & > i:last-child\r\n            position: absolute\r\n            top: .6em\r\n            right: 10px\r\n            transition: transform .3s\r\n\r\n          &.hide\r\n            & > i:last-child\r\n              transform: rotate(90deg)\r\n\r\n            & + .menus_item_child\r\n              overflow: hidden\r\n              max-height: 0\r\n              opacity: 0\r\n              transform: scaleY(0)\r\n              transform-origin: top\r\n\r\n      .menus_item_child\r\n        margin: 0\r\n        padding-left: 25px\r\n        max-height: 0\r\n        list-style: none\r\n        opacity: 0\r\n        transition: transform .3s ease, opacity .3s ease, max-height .3s ease\r\n        transform: scaleY(0)\r\n        transform-origin: top\r\n        will-change: transform, opacity, max-height\r\n\r\n    // 當父元素沒有 .hide 類時，顯示子目錄\r\n    .site-page.group:not(.hide) + .menus_item_child\r\n      max-height: 1000px\r\n      opacity: 1\r\n      transform: scaleY(1)"
  },
  {
    "path": "source/css/_layout/third-party.styl",
    "content": "#vcomment\r\n  font-size: 1.1em\r\n\r\n  .vbtn\r\n    border: none\r\n    background: var(--btn-bg)\r\n    color: var(--btn-color)\r\n\r\n    &:hover\r\n      background: var(--btn-hover-color)\r\n\r\n  .vimg\r\n    transition: all .3s\r\n\r\n    &:hover\r\n      transform: rotate(360deg)\r\n\r\n  .vcards .vcard .vcontent.expand\r\n    &:before,\r\n    &:after\r\n      z-index: 22\r\n\r\n#waline-wrap\r\n  --waline-font-size: 1.1em\r\n  --waline-theme-color: $button-bg\r\n  --waline-active-color: $button-hover-color\r\n\r\n  .wl-comment-actions > button:not(last-child)\r\n    padding-right: 4px\r\n\r\nif hexo-config('valine.bg')\r\n  #vcomment\r\n    textarea\r\n      background: url(hexo-config('valine.bg')) 100% 100% no-repeat\r\n\r\n      &:focus\r\n        background-image: none\r\n\r\nif hexo-config('waline.bg')\r\n  #waline-wrap\r\n    textarea\r\n      background: url(hexo-config('waline.bg')) 100% 100% no-repeat\r\n\r\n      &:focus\r\n        background-image: none\r\n\r\n.twikoo\r\n  .tk-content\r\n    p\r\n      margin: 3px 0\r\n\r\n.fireworks\r\n  position: fixed\r\n  top: 0\r\n  left: 0\r\n  z-index: $fireworks-zIndex\r\n  pointer-events: none\r\n\r\n.medium-zoom-image--opened\r\n  z-index: 99999 !important\r\n  margin: 0 !important\r\n\r\n.medium-zoom-overlay\r\n  z-index: 99999 !important\r\n\r\nif hexo-config('mermaid.enable')\r\n  .mermaid-wrap\r\n    position: relative\r\n    margin: 0 0 20px\r\n    background: var(--card-bg)\r\n    text-align: center\r\n\r\n    if hexo-config('mermaid.open_in_new_tab')\r\n      .mermaid-open-btn\r\n        position: absolute\r\n        top: 8px\r\n        right: 8px\r\n        z-index: 2\r\n        display: flex\r\n        justify-content: center\r\n        align-items: center\r\n        padding: 0\r\n        width: 34px\r\n        height: 25px\r\n        border: none\r\n        border-radius: 20%\r\n        background: #D3D3D3\r\n        box-shadow: 0 4px 10px rgba(0, 0, 0, .15)\r\n        color: #fff\r\n        font-size: 0\r\n        line-height: 1\r\n        cursor: pointer\r\n        transition: background .2s ease, transform .2s ease\r\n\r\n        i\r\n          font-size: 16px\r\n          line-height: 1\r\n\r\n        &:hover,\r\n        &:focus-visible\r\n          outline: none\r\n          background: #C0C0C0\r\n          transform: translateY(-1px)\r\n\r\n    & > svg\r\n      max-width: 100%\r\n      height: 100%\r\n\r\n      if hexo-config('mermaid.zoom_pan')\r\n        cursor: grab\r\n        user-select: none\r\n        touch-action: none\r\n\r\n        &:active\r\n          cursor: grabbing\r\n\r\n  if hexo-config('mermaid.code_write')\r\n    pre > code.mermaid\r\n      display: none\r\n\r\nif hexo-config('chartjs.enable')\r\n  .chartjs-container\r\n    display: flex\r\n    flex-direction: column\r\n    justify-content: center\r\n    align-items: center\r\n    margin: 0 0 20px\r\n    text-align: center\r\n    gap: 20px\r\n\r\n    +maxWidth600()\r\n      .chartjs-wrap\r\n        width: 100% !important\r\n\r\n    &.chartjs-abreast\r\n      flex-direction: row\r\n\r\n      +maxWidth600()\r\n        flex-direction: column\r\n\r\n    .chartjs-wrap\r\n      width: -webkit-fill-available\r\n\r\n    canvas\r\n      display: inline-block !important\r\n\r\n.utterances,\r\n.fb-comments iframe\r\n  width: 100% !important\r\n\r\n#gitalk-container\r\n  .gt-meta\r\n    margin: 0 0 .8em\r\n    padding: 6px 0 16px\r\n\r\nif hexo-config('math.use')\r\n  .katex-display\r\n    overflow: auto hidden\r\n    padding: 5px\r\n\r\n    .katex-show\r\n      display: block\r\n\r\n  .katex\r\n    display: none\r\n\r\n    &.katex-show\r\n      display: inline\r\n\r\n  if hexo-config('math.hide_scrollbar')\r\n    .katex-display,\r\n    mjx-container\r\n      scrollbar-width: none\r\n\r\n      &::-webkit-scrollbar\r\n        display: none\r\n\r\n  // Mathjax\r\n  mjx-container\r\n    overflow-x: auto\r\n    overflow-y: hidden\r\n    padding-bottom: 4px\r\n    max-width: 100%\r\n\r\n    &[display]\r\n      display: block !important\r\n      min-width: auto !important\r\n\r\n    &:not([display])\r\n      display: inline-grid !important\r\n\r\n  mjx-assistive-mml\r\n    right: 0\r\n    bottom: 0\r\n\r\n.aplayer\r\n  color: $font-black\r\n\r\n.container\r\n  .aplayer\r\n    margin: 0 0 20px\r\n\r\n    if hexo-config('beautify.enable')\r\n      ol,\r\n      ul\r\n        margin: 0\r\n        padding: 0\r\n\r\n        li\r\n          margin: 0\r\n          padding: 0 15px\r\n\r\n          &:before\r\n            content: none\r\n\r\n.snackbar-container.snackbar-css\r\n  addBorderRadius(5)\r\n  opacity: .85 !important\r\n\r\n.abc-music-sheet\r\n  margin: 0 0 20px\r\n  opacity: 0\r\n  transition: opacity .3s\r\n\r\n  &.abcjs-container\r\n    opacity: 1\r\n\r\n+maxWidth768()\r\n  .fancybox__toolbar__column.is-middle\r\n    display: none\r\n"
  },
  {
    "path": "source/css/_mode/darkmode.styl",
    "content": "if hexo-config('darkmode.enable') || hexo-config('display_mode') == 'dark'\r\n  [data-theme='dark']\r\n    --global-bg: darken(#121212, 2)\r\n    --font-color: alpha(#FFFFFF, .7)\r\n    --hr-border: alpha(#FFFFFF, .4)\r\n    --hr-before-color: alpha(#FFFFFF, .7)\r\n    --search-bg: #121212\r\n    --search-input-color: alpha(#FFFFFF, .7)\r\n    --search-a-color: alpha(#FFFFFF, .7)\r\n    --preloader-bg: darken(#121212, 2)\r\n    --preloader-color: alpha(#FFFFFF, .7)\r\n    --tab-border-color: #2c2c2c\r\n    --tab-button-bg: #2c2c2c\r\n    --tab-button-color: alpha(#FFFFFF, .7)\r\n    --tab-button-hover-bg: lighten(#121212, 15)\r\n    --tab-button-active-bg: #121212\r\n    --card-bg: #121212\r\n    --sidebar-bg: #121212\r\n    --sidebar-menu-bg: lighten(#121212, 5)\r\n    --btn-hover-color: lighten(#121212, 40)\r\n    --btn-color: alpha(#FFFFFF, .7)\r\n    --btn-bg: lighten(#121212, 5)\r\n    --text-bg-hover: lighten(#121212, 15)\r\n    --light-grey: alpha(#FFFFFF, .7)\r\n    --dark-grey: alpha(#FFFFFF, .2)\r\n    --white: alpha(#FFFFFF, .9)\r\n    --text-highlight-color: alpha(#FFFFFF, .9)\r\n    --blockquote-color: alpha(#FFFFFF, .7)\r\n    --blockquote-bg: lighten(#121212, 10)\r\n    --reward-pop: lighten(#121212, 10)\r\n    --toc-link-color: alpha(#FFFFFF, .6)\r\n    --scrollbar-color: lighten(#121212, 25)\r\n    --timeline-bg: lighten(#121212, 5)\r\n    --zoom-bg: #121212\r\n    --mark-bg: alpha($dark-black, .6)\r\n    --btn-color: darken($btn-color, 20%)\r\n    --btn-default-color: lighten($btn-default-color, 20%)\r\n    --tags-blue-color: desaturate(darken($tagsP-blue-color, 20%), 25%)\r\n    --tags-blue-color-lighten: rgba(66, 139, 202, .15)\r\n    --tags-pink-color: desaturate(darken($tagsP-pink-color, 22%), 30%)\r\n    --tags-pink-color-lighten: rgba(255, 105, 180, .15)\r\n    --tags-red-color: desaturate(darken($tagsP-red-color, 25%), 28%)\r\n    --tags-red-color-lighten: rgba(255, 0, 0, .15)\r\n    --tags-orange-color: desaturate(darken($tagsP-orange-color, 22%), 32%)\r\n    --tags-orange-color-lighten: rgba(255, 140, 0, .15)\r\n    --tags-purple-color: desaturate(darken($tagsP-purple-color, 18%), 30%)\r\n    --tags-purple-color-lighten: rgba(111, 66, 193, .15)\r\n    --tags-green-color: desaturate(darken($tagsP-green-color, 20%), 28%)\r\n    --tags-green-color-lighten: rgba(92, 184, 92, .15)\r\n    --note-default-border: $note-dark-default-border\r\n    --note-default-bg: $note-dark-default-bg\r\n    --note-default-text: $note-dark-default-text\r\n    --note-modern-default-border: $note-dark-modern-default-border\r\n    --note-modern-default-bg: $note-dark-modern-default-bg\r\n    --note-modern-default-text: $note-dark-modern-default-text\r\n    --note-primary-border: $note-dark-primary-border\r\n    --note-primary-bg: $note-dark-primary-bg\r\n    --note-primary-text: $note-dark-primary-text\r\n    --note-modern-primary-border: $note-dark-modern-primary-border\r\n    --note-modern-primary-bg: $note-dark-modern-primary-bg\r\n    --note-modern-primary-text: $note-dark-modern-primary-text\r\n    --note-info-border: $note-dark-info-border\r\n    --note-info-bg: $note-dark-info-bg\r\n    --note-info-text: $note-dark-info-text\r\n    --note-modern-info-border: $note-dark-modern-info-border\r\n    --note-modern-info-bg: $note-dark-modern-info-bg\r\n    --note-modern-info-text: $note-dark-modern-info-text\r\n    --note-success-border: $note-dark-success-border\r\n    --note-success-bg: $note-dark-success-bg\r\n    --note-success-text: $note-dark-success-text\r\n    --note-modern-success-border: $note-dark-modern-success-border\r\n    --note-modern-success-bg: $note-dark-modern-success-bg\r\n    --note-modern-success-text: $note-dark-modern-success-text\r\n    --note-warning-border: $note-dark-warning-border\r\n    --note-warning-bg: $note-dark-warning-bg\r\n    --note-warning-text: $note-dark-warning-text\r\n    --note-modern-warning-border: $note-dark-modern-warning-border\r\n    --note-modern-warning-bg: $note-dark-modern-warning-bg\r\n    --note-modern-warning-text: $note-dark-modern-warning-text\r\n    --note-danger-border: $note-dark-danger-border\r\n    --note-danger-bg: $note-dark-danger-bg\r\n    --note-danger-text: $note-dark-danger-text\r\n    --note-modern-danger-border: $note-dark-modern-danger-border\r\n    --note-modern-danger-bg: $note-dark-modern-danger-bg\r\n    --note-modern-danger-text: $note-dark-modern-danger-text\r\n\r\n    #web_bg:before\r\n      position: absolute\r\n      width: 100%\r\n      height: 100%\r\n      background-color: alpha($dark-black, .7)\r\n      content: ''\r\n\r\n    .container\r\n      code\r\n        background: #2c2c2c\r\n\r\n      pre > code\r\n        background: lighten(#121212, 2)\r\n\r\n      figure.highlight\r\n        box-shadow: none\r\n\r\n      .note\r\n        code\r\n          background: $code-background\r\n\r\n      .aplayer\r\n        filter: brightness(.8)\r\n\r\n      kbd\r\n        border-color: #696969\r\n        background-color: #525252\r\n        color: #e2f1ff\r\n\r\n    // 頭部\r\n    #page-header\r\n      &.nav-fixed > #nav,\r\n      &.not-top-img > #nav\r\n        background: alpha(#121212, .8)\r\n        box-shadow: 0 5px 6px -5px rgba(133, 133, 133, 0)\r\n\r\n    #post-comment\r\n      .comment-switch\r\n        if hexo-config('comments.text')\r\n          background: #2c2c2c !important\r\n\r\n        #switch-btn\r\n          filter: brightness(.8)\r\n\r\n    // hide-tags\r\n    .hide-button,\r\n    .toggle-button,\r\n    #post-outdate-notice,\r\n    .error-img,\r\n    .container iframe,\r\n    .gist,\r\n    .ads-wrap,\r\n    .tag-cloud-list > a\r\n      filter: brightness(.8)\r\n\r\n    img:not(.cover)\r\n      if hexo-config('lazyload.enable') && hexo-config('lazyload.blur') && !hexo-config('lazyload.placeholder')\r\n        filter: blur(0) brightness(.88) contrast(.95)\r\n      else\r\n        filter: brightness(.88) contrast(.95)\r\n\r\n    #aside-content .aside-list > .aside-list-item:not(:last-child)\r\n      border-bottom: 1px dashed alpha(#FFFFFF, .1)\r\n\r\n    // Gitalk\r\n    #gitalk-container\r\n      filter: brightness(.8)\r\n\r\n      svg\r\n        fill: alpha(#FFFFFF, .9) !important\r\n\r\n    // Disqusjs 反代模式下的適配\r\n    #disqusjs\r\n      #dsqjs\r\n        &:hover,\r\n        &:focus,\r\n        .dsqjs-tab-active,\r\n        .dsqjs-no-comment\r\n          color: alpha(#FFFFFF, .7)\r\n\r\n        .dsqjs-order-label\r\n          background-color: lighten(#121212, 5)\r\n\r\n        .dsqjs-post-body\r\n          color: alpha(#FFFFFF, .7)\r\n\r\n          code,\r\n          pre\r\n            background: #2c2c2c\r\n\r\n          blockquote\r\n            color: alpha(#FFFFFF, .7)\r\n\r\n    #artitalk_main #lazy\r\n      background: #121212\r\n\r\n    #operare_artitalk .c2\r\n      background: #121212\r\n\r\n    #card-toc\r\n      +maxWidth900()\r\n        background: lighten(#121212, 5)\r\n\r\n    // artalk\r\n    .artalk.atk-dark-mode,\r\n    .atk-layer-wrap.atk-dark-mode\r\n      --at-color-font: alpha(#FFFFFF, .7)\r\n      --at-color-meta: alpha(#FFFFFF, .7)\r\n      --at-color-grey: alpha(#FFFFFF, .7)\r\n\r\n    .atk-send-btn,\r\n    .atk-badge\r\n      color: alpha(#FFFFFF, .7) !important\r\n\r\n    // waline\r\n    #waline-wrap\r\n      --waline-color: alpha(#FFFFFF, .7)\r\n      --waline-dark-grey: alpha(#FFFFFF, .7)\r\n      --waline-info-color: alpha(#FFFFFF, .5)"
  },
  {
    "path": "source/css/_mode/readmode.styl",
    "content": "if hexo-config('readmode')\r\n  .read-mode\r\n    --font-color: #4c4948\r\n    --readmode-light-color: #fff\r\n    --white: #4c4948\r\n    --light-grey: #4c4948\r\n    --gray: #d6dbdf\r\n    --hr-border: #d6dbdf\r\n    --hr-before-color: darken(#d6dbdf, 10)\r\n    --highlight-bg: #f7f7f7\r\n    --exit-btn-bg: #C0C0C0\r\n    --exit-btn-color: #fff\r\n    --exit-btn-hover: darken(#C0C0C0, 20)\r\n    --pseudo-hover: none\r\n\r\n  [data-theme='dark']\r\n    .read-mode\r\n      --font-color: rgba(255, 255, 255, .7)\r\n      --readmode-light-color: #0d0d0d\r\n      --white: rgba(255, 255, 255, .9)\r\n      --light-grey: rgba(255, 255, 255, .7)\r\n      --gray: rgba(255, 255, 255, .7)\r\n      --hr-border: rgba(255, 255, 255, .5)\r\n      --hr-before-color: rgba(255, 255, 255, .7)\r\n      --highlight-bg: #171717\r\n      --exit-btn-bg: #1f1f1f\r\n      --exit-btn-color: rgba(255, 255, 255, .9)\r\n      --exit-btn-hover: lighten(#1f1f1f, 20)\r\n\r\n  .read-mode\r\n    background: var(--readmode-light-color)\r\n\r\n    .exit-readmode\r\n      position: fixed\r\n      top: 30px\r\n      right: 30px\r\n      z-index: 100\r\n      width: 40px\r\n      height: 40px\r\n      background: var(--exit-btn-bg)\r\n      color: var(--exit-btn-color)\r\n      font-size: 16px\r\n      transition: background .3s\r\n      addBorderRadius(8)\r\n      @extend .btn-effects\r\n\r\n      +maxWidth768()\r\n        top: initial\r\n        bottom: 30px\r\n\r\n      &:hover\r\n        background: var(--exit-btn-hover)\r\n\r\n    #aside-content\r\n      display: none\r\n\r\n    #page-header.post-bg\r\n      background: none !important\r\n\r\n      &:before\r\n        opacity: 0\r\n\r\n      & > #post-info\r\n        text-align: center\r\n\r\n    #post\r\n      margin: 0 auto\r\n      background: transparent\r\n      box-shadow: none\r\n\r\n      &:hover\r\n        box-shadow: none\r\n\r\n    & > canvas\r\n      display: none !important\r\n\r\n    .highlight-tools,\r\n    #footer,\r\n    #post > *:not(#post-info):not(.post-content),\r\n    #nav,\r\n    #post-outdate-notice,\r\n    #web_bg,\r\n    #rightside,\r\n    .not-top-img\r\n      display: none !important\r\n\r\n    .container\r\n      a\r\n        color: #99a9bf\r\n\r\n      pre,\r\n      .highlight:not(.js-file-line-container)\r\n        background: var(--highlight-bg) !important\r\n\r\n        *\r\n          color: var(--font-color) !important\r\n\r\n      figure.highlight\r\n        border-radius: 0 !important\r\n        box-shadow: none !important\r\n\r\n        & > :not(.highlight-tools)\r\n          display: block !important\r\n\r\n        .line:before\r\n          color: var(--font-color) !important\r\n\r\n        .hljs\r\n          background: var(--highlight-bg) !important\r\n\r\n      h1,\r\n      h2,\r\n      h3,\r\n      h4,\r\n      h5,\r\n      h6\r\n        padding: 0\r\n\r\n        &:before\r\n          content: ''\r\n\r\n        &:hover\r\n          padding: 0\r\n\r\n      ul,\r\n      li,\r\n      ol\r\n        &:hover:before\r\n          transform: none !important\r\n\r\n      ol,\r\n      li\r\n        &:before\r\n          background: transparent !important\r\n          color: var(--font-color) !important\r\n\r\n      ul\r\n        >li\r\n          &:before\r\n            border-color: var(--gray) !important\r\n\r\n      .tabs\r\n        border: 2px solid var(--tab-border-color)\r\n\r\n        > .nav-tabs\r\n          background: transparent\r\n\r\n          > .tab\r\n            border-top: none !important\r\n\r\n        > .tab-contents .tab-item-content.active\r\n          animation: none\r\n\r\n      code\r\n        color: var(--font-color)\r\n\r\n      blockquote\r\n        border-color: var(--gray)\r\n        background-color: var(--readmode-light-color)\r\n\r\n      kbd\r\n        border: 1px solid var(--gray)\r\n        background-color: transparent\r\n        box-shadow: none\r\n        color: var(--font-color)\r\n\r\n      .hide-toggle\r\n        border: 1px solid var(--gray) !important\r\n\r\n      .hide-button,\r\n      .btn-beautify,\r\n      .hl-label\r\n        border: 1px solid var(--gray) !important\r\n        background: var(--readmode-light-color) !important\r\n        color: var(--font-color) !important\r\n\r\n      .note\r\n        border: 2px solid var(--gray)\r\n        border-left-color: var(--gray) !important\r\n        filter: none\r\n        background-color: var(--readmode-light-color) !important\r\n        color: var(--font-color)\r\n\r\n        &:before,\r\n        .note-icon\r\n          color: var(--font-color)\r\n"
  },
  {
    "path": "source/css/_page/404.styl",
    "content": "if hexo-config('error_404.enable')\r\n  .type-404\r\n    .error-content\r\n      @extend .cardHover\r\n      overflow: hidden\r\n      margin: 0 20px\r\n      height: 360px\r\n\r\n      +maxWidth768()\r\n        margin: 0\r\n        height: 500px\r\n\r\n      .error-img\r\n        display: inline-block\r\n        overflow: hidden\r\n        width: 50%\r\n        height: 100%\r\n\r\n        +maxWidth768()\r\n          width: 100%\r\n          height: 45%\r\n\r\n        img\r\n          @extend .imgHover\r\n          background-color: $theme-color\r\n\r\n      .error-info\r\n        display: inline-flex\r\n        flex-direction: column\r\n        justify-content: center\r\n        align-content: center\r\n        width: 50%\r\n        height: 100%\r\n        vertical-align: top\r\n        text-align: center\r\n\r\n        if $site-name-font\r\n          font-family: $site-name-font\r\n\r\n        +maxWidth768()\r\n          width: 100%\r\n          height: 55%\r\n\r\n        .error_title\r\n          margin-top: -.6em\r\n          font-size: 9em\r\n\r\n          +maxWidth768()\r\n            font-size: 8em\r\n\r\n        .error_subtitle\r\n          @extend .limit-more-line\r\n          margin-top: -3em\r\n          word-break: break-word\r\n          font-size: 1.6em\r\n          -webkit-line-clamp: 2\r\n\r\n    .nc\r\n      margin-top: 5%\r\n      padding: 0 20px\r\n\r\n    #footer\r\n      display: none\r\n\r\n    & + #rightside\r\n      display: none"
  },
  {
    "path": "source/css/_page/archives.styl",
    "content": ".article-sort\r\n  margin-left: 10px\r\n  padding-left: 20px\r\n  border-left: 2px solid lighten($light-blue, 20)\r\n\r\n  &-title\r\n    position: relative\r\n    margin-left: 10px\r\n    padding-bottom: 20px\r\n    padding-left: 20px\r\n    font-size: 1.72em\r\n\r\n    &:hover\r\n      &:before\r\n        border-color: var(--pseudo-hover)\r\n\r\n    &:before\r\n      position: absolute\r\n      top: calc(((100% - 36px) / 2))\r\n      left: -9px\r\n      z-index: 1\r\n      width: w = 10px\r\n      height: h = w\r\n      border: .5 * w solid $light-blue\r\n      border-radius: w\r\n      background: var(--card-bg)\r\n      content: ''\r\n      line-height: h\r\n      transition: all .2s ease-in-out\r\n\r\n    &:after\r\n      position: absolute\r\n      bottom: 0\r\n      left: 0\r\n      z-index: 0\r\n      width: 2px\r\n      height: 1.5em\r\n      background: lighten($light-blue, 20)\r\n      content: ''\r\n\r\n  &-item\r\n    position: relative\r\n    display: flex\r\n    align-items: center\r\n    margin: 0 0 20px 10px\r\n    transition: all .2s ease-in-out\r\n\r\n    &:hover\r\n      &:before\r\n        border-color: var(--pseudo-hover)\r\n\r\n    &:before\r\n      $w = 6px\r\n      position: absolute\r\n      left: calc(-20px - 17px)\r\n      width: w = $w\r\n      height: h = w\r\n      border: .5 * w solid $light-blue\r\n      border-radius: w\r\n      background: var(--card-bg)\r\n      content: ''\r\n      transition: all .2s ease-in-out\r\n\r\n    &.no-article-cover\r\n      height: 80px\r\n\r\n      .article-sort-item-info\r\n        padding: 0\r\n\r\n    &.year\r\n      font-size: 1.43em\r\n      margin-bottom: 10px\r\n\r\n      &:hover\r\n        &:before\r\n          border-color: $light-blue\r\n\r\n      &:before\r\n        border-color: var(--pseudo-hover)\r\n\r\n    &-time\r\n      color: var(--card-meta)\r\n      font-size: .85em\r\n\r\n      time\r\n        padding-left: 6px\r\n        cursor: default\r\n\r\n    &-title\r\n      @extend .limit-more-line\r\n      color: var(--font-color)\r\n      font-size: 1.05em\r\n      transition: all .3s\r\n      -webkit-line-clamp: 2\r\n\r\n      &:hover\r\n        color: $text-hover\r\n        transform: translateX(10px)\r\n\r\n    &-img\r\n      overflow: hidden\r\n      width: 100px\r\n      height: 70px\r\n      addBorderRadius()\r\n\r\n      +maxWidth768()\r\n        width: 70px\r\n        height: 70px\r\n\r\n      :first-child\r\n        @extend .imgHover\r\n\r\n    &-info\r\n      flex: 1\r\n      padding: 0 16px\r\n"
  },
  {
    "path": "source/css/_page/categories.styl",
    "content": ".category-lists\r\n  .category-title\r\n    font-size: 2.57em\r\n\r\n    +maxWidth768()\r\n      font-size: 2em\r\n\r\n  .category-list\r\n    margin-bottom: 0\r\n\r\n    a\r\n      color: var(--font-color)\r\n\r\n      &:hover\r\n        color: $text-hover\r\n\r\n    .category-list-count\r\n      margin-left: 8px\r\n      color: var(--card-meta)\r\n\r\n      &:before\r\n        content: '('\r\n\r\n      &:after\r\n        content: ')'\r\n\r\n  ul\r\n    padding: 0 0 0 20px\r\n    @extend .list-beauty\r\n\r\n    ul\r\n      padding-left: 4px\r\n\r\n    li\r\n      position: relative\r\n      margin: 6px 0\r\n      padding: .12em .4em .12em 1.4em\r\n"
  },
  {
    "path": "source/css/_page/common.styl",
    "content": "#body-wrap\r\n  display: flex\r\n  flex-direction: column\r\n  min-height: 100vh\r\n\r\n.layout\r\n  display: flex\r\n  flex: 1 auto\r\n  margin: 0 auto\r\n  padding: 40px 15px\r\n  max-width: 1200px\r\n  width: 100%\r\n\r\n  +maxWidth900()\r\n    flex-direction: column\r\n\r\n  +maxWidth768()\r\n    padding: 20px 5px\r\n\r\n  +minWidth2000()\r\n    max-width: 60%\r\n\r\n  & > div:first-child:not(.nc)\r\n    @extend .cardHover\r\n    align-self: flex-start\r\n    padding: 50px 40px\r\n\r\n    +maxWidth768()\r\n      padding: 36px 14px\r\n\r\n  & > div:first-child\r\n    width: 74%\r\n    transition: all .3s\r\n\r\n    +maxWidth900()\r\n      width: 100% !important\r\n\r\n    if hexo-config('aside.position') == 'left'\r\n      +minWidth900()\r\n        order: 2\r\n\r\n  // 隱藏aside\r\n  &.hide-aside\r\n    max-width: 1000px\r\n\r\n    +minWidth2000()\r\n      max-width: 1300px\r\n\r\n    & > div\r\n      width: 100% !important\r\n\r\n// for apple device\r\n.apple\r\n  #page-header.full_page\r\n    background-attachment: scroll !important\r\n\r\n  .recent-post-item,\r\n  .avatar-img,\r\n  .flink-item-icon\r\n    transform: translateZ(0)\r\n"
  },
  {
    "path": "source/css/_page/flink.styl",
    "content": ".container\r\n  .flink\r\n    margin-bottom: 20px\r\n\r\n    .flink-list\r\n      overflow: auto\r\n      padding: 10px 10px 0\r\n      text-align: center\r\n\r\n      & > .flink-list-item\r\n        position: relative\r\n        float: left\r\n        overflow: hidden\r\n        margin: 15px 7px\r\n        width: calc(100% / 3 - 15px)\r\n        height: 90px\r\n        line-height: 17px\r\n        -webkit-transform: translateZ(0)\r\n        addBorderRadius(8)\r\n\r\n        +maxWidth1024()\r\n          width: calc(50% - 15px) !important\r\n\r\n        +maxWidth600()\r\n          width: calc(100% - 15px) !important\r\n\r\n        &:hover\r\n          .flink-item-icon\r\n            margin-left: -10px\r\n            width: 0\r\n\r\n        &:before\r\n          position: absolute\r\n          top: 0\r\n          right: 0\r\n          bottom: 0\r\n          left: 0\r\n          z-index: -1\r\n          background: var(--text-bg-hover)\r\n          content: ''\r\n          transition: transform .3s ease-out\r\n          transform: scale(0)\r\n\r\n        &:hover:before,\r\n        &:focus:before,\r\n        &:active:before\r\n          transform: scale(1)\r\n\r\n        a\r\n          color: var(--font-color)\r\n          text-decoration: none\r\n\r\n          .flink-item-icon\r\n            float: left\r\n            overflow: hidden\r\n            margin: 15px 10px\r\n            width: 60px\r\n            height: 60px\r\n            border-radius: 7px\r\n            transition: width .3s ease-out\r\n\r\n            img\r\n              width: 100%\r\n              height: 100%\r\n              transition: filter 375ms ease-in .2s, transform .3s\r\n              object-fit: cover\r\n\r\n          .img-alt\r\n            display: none\r\n\r\n    .flink-item-name\r\n      @extend .limit-one-line\r\n      padding: 16px 10px 0 0\r\n      height: 40px\r\n      font-weight: bold\r\n      font-size: 1.43em\r\n\r\n    .flink-item-desc\r\n      @extend .limit-one-line\r\n      padding: 16px 10px 16px 0\r\n      height: 50px\r\n      font-size: .93em\r\n\r\n    .flink-name\r\n      margin-bottom: 5px\r\n      font-weight: bold\r\n      font-size: 1.5em"
  },
  {
    "path": "source/css/_page/homepage.styl",
    "content": "$indexLayout = hexo-config('index_layout') || 1\r\n$indexEnable = hexo-config('cover.index_enable')\r\n\r\n#recent-posts\r\n  .recent-post-item\r\n    @extend .cardHover\r\n    position: relative\r\n    overflow: hidden\r\n    margin-bottom: 20px\r\n\r\n    if $indexLayout == 6 || ($indexLayout == 7)\r\n      display: inline-block\r\n      width: calc(100% / 2 - 8px)\r\n      vertical-align: top\r\n\r\n      +maxWidth768()\r\n        width: 100%\r\n\r\n      +minWidth2000()\r\n        width: calc(100% / 3 - 8px)\r\n\r\n    if $indexLayout == 1 || ($indexLayout == 2 || ($indexLayout == 3))\r\n      display: flex\r\n      flex-direction: row\r\n      align-items: center\r\n      height: 16.8em\r\n\r\n      +maxWidth768()\r\n        flex-direction: column\r\n        height: auto\r\n\r\n      +minWidth2000()\r\n        height: 18.8em\r\n\r\n    &:hover\r\n      .post-bg\r\n        transform: scale(1.1)\r\n\r\n    &.ads-wrap\r\n      display: block !important\r\n      height: auto !important\r\n\r\n    .post_cover\r\n      overflow: hidden\r\n\r\n      if ($indexLayout != 5 && ($indexLayout != 7))\r\n        +maxWidth768()\r\n          width: 100%\r\n          height: 230px\r\n\r\n      if $indexLayout == 1 || ($indexLayout == 2 || ($indexLayout == 3))\r\n        width: 42%\r\n        height: 100%\r\n\r\n        &.right\r\n          order: 1\r\n\r\n          +maxWidth768()\r\n            order: 0\r\n\r\n      if $indexLayout == 4 || ($indexLayout == 5 || ($indexLayout == 6 || ($indexLayout == 7)))\r\n        width: 100%\r\n\r\n        if ($indexLayout == 5 || ($indexLayout == 7))\r\n          height: 17em\r\n        else\r\n          height: 230px\r\n\r\n      if ($indexLayout == 5 || ($indexLayout == 7)) && $indexEnable\r\n        &:before\r\n          position: absolute\r\n          z-index: 1\r\n          width: 100%\r\n          height: 100%\r\n          background-color: rgba(18, 18, 18, .4)\r\n          content: ''\r\n          backdrop-filter: blur(3px)\r\n\r\n      .post-bg\r\n        z-index: -4\r\n        @extend .imgHover\r\n\r\n    & >.recent-post-info\r\n      +maxWidth768()\r\n        padding: 20px 20px 30px\r\n        width: 100%\r\n\r\n      if $indexLayout == 1 || ($indexLayout == 2 || ($indexLayout == 3))\r\n        padding: 0 40px\r\n        width: 58%\r\n\r\n      if $indexLayout == 4 || ($indexLayout == 5 || ($indexLayout == 6 || ($indexLayout == 7)))\r\n        padding: 30px 30px 25px\r\n\r\n      if ($indexLayout == 5 || ($indexLayout == 7)) && $indexEnable\r\n        &:not(.no-cover)\r\n          position: absolute\r\n          top: 50%\r\n          z-index: 2\r\n          width: 100%\r\n          color: var(--text-highlight-color)\r\n          transform: translateY(-50%)\r\n          --text-highlight-color: rgba(255, 255, 255, 1)\r\n          --card-meta: rgba(255, 255, 255, .7)\r\n\r\n      &.no-cover\r\n        +maxWidth768()\r\n          padding: 30px 20px\r\n\r\n        if $indexLayout == 1 || ($indexLayout == 2 || ($indexLayout == 3))\r\n          width: 100%\r\n\r\n        if $indexLayout == 4 || ($indexLayout == 5 || ($indexLayout == 6 || ($indexLayout == 7)))\r\n          padding: 35px 30px\r\n\r\n      & > .article-title\r\n        @extend .limit-more-line\r\n        color: var(--text-highlight-color)\r\n        font-size: 1.55em\r\n        line-height: 1.4\r\n        transition: all .2s ease-in-out\r\n        -webkit-line-clamp: 2\r\n\r\n        .sticky\r\n          margin-right: 10px\r\n          color: $sticky-color\r\n          transform: rotate(45deg)\r\n\r\n        +maxWidth768()\r\n          font-size: 1.43em\r\n\r\n        &:hover\r\n          color: $text-hover\r\n\r\n      & > .article-meta-wrap\r\n        margin: 6px 0\r\n        color: var(--card-meta)\r\n        font-size: .9em\r\n\r\n        & > .post-meta-date\r\n          cursor: default\r\n\r\n        i\r\n          margin: 0 4px 0 0\r\n\r\n        .fa-spinner\r\n          margin: 0\r\n\r\n        .article-meta-label\r\n          if hexo-config('post_meta.page.label')\r\n            padding-right: 4px\r\n          else\r\n            display: none\r\n\r\n        .article-meta-separator\r\n          margin: 0 6px\r\n\r\n        .article-meta-link\r\n          margin: 0 4px\r\n\r\n        if hexo-config('post_meta.page.date_format') == 'relative'\r\n          time\r\n            display: none\r\n\r\n        a\r\n          color: var(--card-meta)\r\n\r\n          &:hover\r\n            color: $text-hover\r\n            text-decoration: underline\r\n\r\n      & > .content\r\n        @extend .limit-more-line\r\n        -webkit-line-clamp: 2\r\n        word-break: break-word"
  },
  {
    "path": "source/css/_page/shuoshuo.styl",
    "content": "#article-container\r\n  .shuoshuo-item\r\n    @extend .cardHover\r\n    margin-bottom: 20px\r\n    padding: 35px 30px 30px\r\n\r\n    +maxWidth768()\r\n      padding: 25px 20px 20px\r\n\r\n  .shuoshuo-item-header\r\n    display: flex\r\n    align-items: center\r\n    cursor: default\r\n\r\n  .shuoshuo-avatar\r\n    overflow: hidden\r\n    width: 40px\r\n    height: 40px\r\n    border-radius: 40px\r\n\r\n    img\r\n      margin: 0\r\n      width: 100%\r\n      height: 100%\r\n\r\n  .shuoshuo-info\r\n    margin-left: 10px\r\n    line-height: 1.5\r\n\r\n  .shuoshuo-date\r\n    color: #858585\r\n    font-size: .8em\r\n\r\n  .shuoshuo-content\r\n    padding: 15px 0 10px\r\n\r\n    & > *:last-child\r\n      margin-bottom: 0\r\n\r\n  .shuoshuo-footer\r\n    display: flex\r\n    align-items: center\r\n\r\n    &.flex-between\r\n      justify-content: space-between\r\n\r\n    &.flex-end\r\n      justify-content: flex-end\r\n\r\n    .shuoshuo-tag\r\n      display: inline-block\r\n      margin-right: 8px\r\n      padding: 0 8px\r\n      width: fit-content\r\n      border: 1px solid $light-blue\r\n      border-radius: 12px\r\n      color: $light-blue\r\n      font-size: .85em\r\n      cursor: default\r\n      transition: all .2s ease-in-out\r\n\r\n      &:hover\r\n        background: $light-blue\r\n        color: var(--white)\r\n\r\n    .shuoshuo-comment-btn\r\n      padding: 2px\r\n      color: #90a4ae\r\n      cursor: pointer\r\n\r\n      &:hover\r\n        color: $light-blue\r\n\r\n  .shuoshuo-comment\r\n    padding-top: 10px\r\n\r\n    &.no-comment\r\n      display: none\r\n\r\n.shuoshuo-navigation\r\n  display: flex\r\n  justify-content: center\r\n  align-items: center\r\n  margin-top: 20px\r\n  padding: 20px 0\r\n\r\n  button\r\n    display: flex\r\n    justify-content: center\r\n    align-items: center\r\n    width: 2.7em\r\n    height: 2.7em\r\n    background-color: var(--btn-bg)\r\n    color: var(--btn-color)\r\n    font-size: .9em\r\n    line-height: 2.5em\r\n    transition: all .2s ease-in-out\r\n    addBorderRadius(6)\r\n\r\n    &:not(:disabled)\r\n      @extend .btn-effects\r\n\r\n    &:hover:not(:disabled)\r\n      background-color: var(--btn-hover-color)\r\n\r\n    &:disabled\r\n      background: #f5f5f5\r\n      color: #ccc\r\n      opacity: .5\r\n      cursor: not-allowed\r\n\r\n  .shuoshuo-page-info\r\n    margin: 0 15px\r\n    color: #858585\r\n    white-space: nowrap\r\n    font-size: .9em\r\n\r\n  .shuoshuo-page-input\r\n    margin-right: 12px\r\n    padding: 0 15px\r\n    height: 2.7em\r\n    border: 1px solid var(--btn-bg)\r\n    background: var(--card-bg)\r\n    color: #858585\r\n    text-align: center\r\n    font-size: .9em\r\n    transition: all .2s ease-in-out\r\n    addBorderRadius(6)\r\n\r\n    &:focus\r\n      outline: none\r\n      border-width: 2px\r\n\r\n      &::placeholder\r\n        color: transparent\r\n\r\n    /* 隱藏 number 輸入框的上下箭頭 */\r\n    &::-webkit-outer-spin-button,\r\n    &::-webkit-inner-spin-button\r\n      margin: 0\r\n      -webkit-appearance: none\r\n\r\n    /* Firefox */\r\n    -moz-appearance: textfield\r\n\r\n    /* 當作為頁碼按鈕時的樣式 */\r\n    &.shuoshuo-page-num\r\n      min-width: 40px\r\n      width: 40px\r\n      border: none\r\n      background: $light-blue\r\n      color: var(--white)\r\n      font-weight: 500\r\n      cursor: text\r\n\r\n      &:focus\r\n        border: 1px solid $light-blue\r\n        background: var(--white)\r\n        color: #333\r\n\r\n    /* 超出範圍時的紅色樣式 */\r\n    &.invalid\r\n      border-color: #ff4757\r\n      background-color: #ffeaea\r\n      color: #ff4757\r\n      animation: shake .5s ease-in-out\r\n\r\n  /* 震動動畫 */\r\n  @keyframes shake\r\n    0%,\r\n    100%\r\n      transform: translateX(0)\r\n\r\n    10%,\r\n    30%,\r\n    50%,\r\n    70%,\r\n    90%\r\n      transform: translateX(-2px)\r\n\r\n    20%,\r\n    40%,\r\n    60%,\r\n    80%\r\n      transform: translateX(2px)\r\n"
  },
  {
    "path": "source/css/_page/tags.styl",
    "content": ".tag-cloud\r\n  &-list\r\n    animation: tagsFadeIn .6s cubic-bezier(.4, 0, .2, 1)\r\n\r\n    &:hover a:not(:hover)\r\n      opacity: .7\r\n      transform: scale(.98)\r\n\r\n    a\r\n      position: relative\r\n      display: inline-block\r\n      margin: 5px\r\n      padding: 3px 12px\r\n      line-height: 1.7\r\n      transition: all .3s cubic-bezier(.4, 0, .2, 1)\r\n      addBorderRadius(7)\r\n      overflow: hidden\r\n      color: white\r\n      transform: translateY(0) scale(1)\r\n      will-change: transform, background-color, box-shadow\r\n\r\n      &::before\r\n        position: absolute\r\n        top: 0\r\n        left: -100%\r\n        z-index: -1\r\n        width: 100%\r\n        height: 100%\r\n        background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .1), transparent)\r\n        content: ''\r\n        transition: left .6s cubic-bezier(.4, 0, .2, 1)\r\n\r\n      &:hover\r\n        background: var(--btn-hover-color) !important\r\n        box-shadow:\r\n          0 6px 20px rgba(0, 0, 0, .12),\r\n          0 4px 8px rgba(0, 0, 0, .08),\r\n          0 0 0 1px rgba(255, 255, 255, .05)\r\n        color: var(--btn-color) !important\r\n        transform: translateY(-2px) scale(1.02)\r\n\r\n        &::before\r\n          left: 100%\r\n\r\n      &:active\r\n        box-shadow:\r\n          0 3px 8px rgba(0, 0, 0, .15),\r\n          0 1px 3px rgba(0, 0, 0, .1)\r\n        transition: all .15s cubic-bezier(.4, 0, .2, 1)\r\n        transform: translateY(-1px) scale(.98)\r\n\r\n      +maxWidth768()\r\n        zoom: .85\r\n\r\n        &:hover\r\n          transform: translateY(-1px) scale(1.01)\r\n\r\n        &:active\r\n          transform: translateY(0) scale(.99)\r\n\r\n        &::before\r\n          display: none\r\n\r\n  &-title\r\n    font-size: 2.57em\r\n    animation: titleSlideIn .8s cubic-bezier(.4, 0, .2, 1)\r\n\r\n    +maxWidth768()\r\n      font-size: 2em\r\n\r\n@keyframes tagsFadeIn\r\n  from\r\n    opacity: 0\r\n    transform: translateY(20px)\r\n\r\n  to\r\n    opacity: 1\r\n    transform: translateY(0)\r\n\r\n@keyframes titleSlideIn\r\n  from\r\n    opacity: 0\r\n    transform: translateX(-30px)\r\n\r\n  to\r\n    opacity: 1\r\n    transform: translateX(0)\r\n\r\n.page-title\r\n  & + .tag-cloud-list\r\n    text-align: left\r\n"
  },
  {
    "path": "source/css/_search/algolia.styl",
    "content": "#algolia-search\r\n  .search-dialog\r\n    .ais-Hits-list\r\n      +maxWidth768()\r\n        min-height: calc(var(--search-height) - 245px)\r\n\r\n    #algolia-info\r\n      .algolia-poweredBy\r\n        float: right\r\n        padding-top: 2px\r\n\r\n        svg\r\n          height: 1.1em"
  },
  {
    "path": "source/css/_search/index.styl",
    "content": ".search-dialog\r\n  position: fixed\r\n  top: 10%\r\n  left: 50%\r\n  z-index: 1001\r\n  display: none\r\n  margin-left: -300px\r\n  padding: 20px\r\n  width: 600px\r\n  background: var(--search-bg)\r\n  --search-height: 100vh\r\n  addBorderRadius(8)\r\n\r\n  +maxWidth768()\r\n    top: 0\r\n    left: 0\r\n    margin: 0\r\n    width: 100%\r\n    height: 100%\r\n    border-radius: 0\r\n\r\n  .search-nav\r\n    display: flex\r\n    justify-content: space-between\r\n    align-items: center\r\n    margin-bottom: 14px\r\n    color: $search-color\r\n    font-size: 1.4em\r\n    line-height: 1\r\n\r\n    .search-dialog-title\r\n      margin-right: 4px\r\n\r\n    #loading-status\r\n      &[hidden]\r\n        display: none !important\r\n\r\n    .search-close-button\r\n      flex: 1\r\n      color: $grey\r\n      text-align: right\r\n      transition: all .2s ease\r\n\r\n      &:hover\r\n        color: $search-color\r\n\r\n  .local-search-input,\r\n  #algolia-search-input\r\n    margin: 0 auto\r\n    max-width: 100%\r\n    width: 100%\r\n\r\n    input,\r\n    .ais-SearchBox-input\r\n      padding: 5px 14px\r\n      width: 100%\r\n      outline: none\r\n      border: 2px solid $search-color\r\n      border-radius: 40px\r\n      background: var(--search-bg)\r\n      color: var(--search-input-color)\r\n      -webkit-appearance: none\r\n\r\n      &::placeholder\r\n        color: var(--text-color)\r\n\r\n  .search-result-list,\r\n  .ais-Hits-list\r\n    overflow-y: overlay\r\n    margin: 0 -20px\r\n    padding: 0 22px\r\n    max-height: calc(80vh - 220px)\r\n\r\n    .local-search-hit-item,\r\n    .ais-Hits-item\r\n      display: flex\r\n      align-items: flex-start\r\n      margin: 3px 0\r\n      line-height: 1.8\r\n      transition: all .2s ease-in-out\r\n\r\n      &:hover\r\n        transform: translateY(-1px)\r\n\r\n      &:not([value])::before\r\n        display: none\r\n\r\n      &[value]::before\r\n        display: inline-flex\r\n        flex-shrink: 0\r\n        justify-content: center\r\n        align-items: center\r\n        margin-right: 6px\r\n        margin-top: 3px\r\n        min-width: 24px\r\n        color: $search-color\r\n        content: attr(value) '.'\r\n        font-weight: bold\r\n        font-style: italic\r\n        font-size: .9em\r\n\r\n      &::marker\r\n        content: none\r\n\r\n      a\r\n        flex: 1\r\n        color: var(--search-a-color)\r\n\r\n        &:hover\r\n          color: $search-color\r\n\r\n      .search-result-title,\r\n      .algolia-hits-item-title\r\n        font-weight: 600\r\n\r\n      .search-result,\r\n      .algolia-hit-item-content\r\n        margin: 0 0 8px\r\n        word-break: break-all\r\n        font-size: .9em\r\n\r\n  .ais-Pagination\r\n    margin: 15px 0 0\r\n    padding: 0\r\n    text-align: center\r\n\r\n    .ais-Pagination-list\r\n      display: flex\r\n      flex-wrap: wrap\r\n      justify-content: center\r\n      align-items: center\r\n      margin: 0\r\n      padding: 0\r\n      list-style: none\r\n      gap: 6px\r\n\r\n    .ais-Pagination-item\r\n      display: flex\r\n      padding: 0\r\n\r\n      &:not(.ais-Pagination-item--selected):not(.ais-Pagination-item--ellipsis):not(.ais-Pagination-item--disabled)\r\n        .ais-Pagination-link:hover\r\n          background: var(--btn-hover-color)\r\n          transform: translateY(-1px)\r\n\r\n      .ais-Pagination-link\r\n        display: inline-flex\r\n        justify-content: center\r\n        align-items: center\r\n        padding: 4px 8px\r\n        min-width: 28px\r\n        height: 28px\r\n        border-radius: 6px\r\n        background: var(--btn-bg)\r\n        color: var(--btn-color)\r\n        transition: all .2s ease\r\n\r\n        &.ais-Pagination-link--disabled\r\n          opacity: .3\r\n          cursor: not-allowed\r\n\r\n        i\r\n          font-size: 12px\r\n\r\n    .ais-Pagination-item--selected\r\n      .ais-Pagination-link\r\n        background: $theme-paginator-color\r\n        font-weight: 600\r\n        cursor: default\r\n\r\n    .ais-Pagination-item--ellipsis\r\n      .ais-Pagination-link\r\n        padding: 4px 2px\r\n        border: none\r\n        background: transparent\r\n        color: var(--text-color)\r\n        cursor: default\r\n\r\n        &:hover\r\n          background: transparent\r\n          transform: none\r\n\r\n    .ais-Pagination-item--disabled\r\n      .ais-Pagination-link\r\n        opacity: .4\r\n\r\n    +maxWidth768()\r\n      .ais-Pagination-list\r\n        gap: 4px\r\n\r\n  hr\r\n    margin: 15px auto\r\n    @extend .custom-hr\r\n\r\n#search-mask\r\n  position: fixed\r\n  top: 0\r\n  right: 0\r\n  bottom: 0\r\n  left: 0\r\n  z-index: 1000\r\n  display: none\r\n  background: rgba($dark-black, .6)\r\n\r\n.search-result-stats,\r\n.ais-Stats-text\r\n  margin: 15px 0 0\r\n  color: var(--text-color)\r\n  text-align: center\r\n  font-size: .9em\r\n\r\n.search-keyword\r\n  background: transparent\r\n  color: $search-keyword-highlight\r\n  font-weight: 600\r\n\r\n.search-loading\r\n  display: flex\r\n  justify-content: center\r\n  align-items: center\r\n  padding: 20px\r\n  color: var(--text-color)\r\n\r\n  &::before\r\n    width: 16px\r\n    height: 16px\r\n    border: 2px solid var(--text-color)\r\n    border-top-color: transparent\r\n    border-radius: 50%\r\n    content: ''\r\n    animation: spin 1s linear infinite\r\n\r\n@keyframes spin\r\n  to\r\n    transform: rotate(360deg)\r\n\r\nif hexo-config('search.use') == 'algolia_search'\r\n  @require 'algolia'\r\nelse if hexo-config('search.use') == 'local_search'\r\n  @require 'local-search'"
  },
  {
    "path": "source/css/_search/local-search.styl",
    "content": "#local-search\r\n  .search-dialog\r\n    .search-result-list\r\n      +maxWidth768()\r\n        if hexo-config('search.local_search.pagination.enable')\r\n          min-height: calc(var(--search-height) - 255px) !important\r\n        else\r\n          max-height: calc(var(--search-height) - 200px) !important\r\n\r\n  #local-search-stats\r\n    .search-result-stats\r\n      text-align: left\r\n\r\n  .search-keyword\r\n    font-weight: 600\r\n\r\n  #loading-database ~ *\r\n    visibility: hidden"
  },
  {
    "path": "source/css/_tags/button.styl",
    "content": ".container\r\n  .btn-center\r\n    margin: 0 0 20px\r\n    text-align: center\r\n\r\n  .btn-beautify\r\n    display: inline-block\r\n    margin: 0 4px 6px\r\n    padding: 0 15px\r\n    background-color: var(--btn-beautify-color, var(--btn-default-color))\r\n    color: var(--btn-color, $btn-color)\r\n    vertical-align: top\r\n    line-height: 2\r\n    addBorderRadius()\r\n    @extend .btn-effects\r\n\r\n    for $type in $color-types\r\n      &.{$type}\r\n        --btn-beautify-color: unquote('var(--tags-' + $type + '-color)')\r\n\r\n    &:hover\r\n      background-color: var(--btn-hover-color)\r\n\r\n    &:not(.block) + .btn-beautify:not(.block)\r\n      margin: 0 4px 20px\r\n\r\n    &.block\r\n      display: block\r\n      margin: 0 0 20px\r\n      width: fit-content\r\n      width: -moz-fit-content\r\n\r\n      &.center\r\n        margin: 0 auto 20px\r\n\r\n      &.right\r\n        margin: 0 0 20px auto\r\n\r\n    &.larger\r\n      padding: 6px 15px\r\n      @extend .btn-effects-large\r\n\r\n    &.outline\r\n      border: 1px solid transparent\r\n      border-color: var(--btn-beautify-color, var(--btn-default-color))\r\n      background-color: transparent\r\n      color: var(--btn-beautify-color, var(--btn-default-color))\r\n\r\n      i,\r\n      span\r\n        transition: color .3s cubic-bezier(.4, 0, .2, 1)\r\n\r\n      &::before\r\n        background: linear-gradient(90deg, transparent, rgba(0, 0, 0, .1), transparent)\r\n\r\n      &:hover\r\n        border-color: var(--btn-beautify-color, var(--btn-default-color))\r\n        background-color: var(--btn-beautify-color, var(--btn-default-color))\r\n        color: var(--btn-color) !important\r\n\r\n        i,\r\n        span\r\n          color: var(--btn-color)\r\n"
  },
  {
    "path": "source/css/_tags/gallery.styl",
    "content": ".container\r\n  figure.gallery-group\r\n    position: relative\r\n    float: left\r\n    overflow: hidden\r\n    margin: 6px 4px\r\n    width: calc(50% - 8px)\r\n    height: 250px\r\n    border-radius: 10px\r\n    background: $dark-black\r\n    -webkit-transform: translate3d(0, 0, 0)\r\n\r\n    +maxWidth600()\r\n      width: calc(100% - 8px)\r\n\r\n    +minWidth1024()\r\n      width: calc(100% / 3 - 8px)\r\n\r\n    &:hover\r\n      img\r\n        opacity: .4\r\n        transform: translate3d(0, 0, 0)\r\n\r\n      .gallery-group-name::after\r\n        transform: translate3d(0, 0, 0)\r\n\r\n      p\r\n        opacity: 1\r\n        transform: translate3d(0, 0, 0)\r\n\r\n    img\r\n      position: relative\r\n      margin: 0\r\n      max-width: none\r\n      width: calc(100% + 20px)\r\n      height: 250px\r\n      backface-visibility: hidden\r\n      opacity: .8\r\n      transition: all .3s, filter 375ms ease-in .2s\r\n      transform: translate3d(-10px, 0, 0)\r\n      object-fit: cover\r\n\r\n    figcaption\r\n      position: absolute\r\n      top: 0\r\n      left: 0\r\n      padding: 30px\r\n      width: 100%\r\n      height: 100%\r\n      color: $gallery-color\r\n      text-transform: uppercase\r\n      backface-visibility: hidden\r\n\r\n      & > a\r\n        position: absolute\r\n        top: 0\r\n        right: 0\r\n        bottom: 0\r\n        left: 0\r\n        z-index: 1000\r\n        opacity: 0\r\n\r\n    p\r\n      @extend .limit-more-line\r\n      margin: 0\r\n      padding: 8px 0 0\r\n      letter-spacing: 1px\r\n      font-size: 1.1em\r\n      line-height: 1.5\r\n      opacity: 0\r\n      transition: opacity .35s, transform .35s\r\n      transform: translate3d(100%, 0, 0)\r\n      -webkit-line-clamp: 4\r\n\r\n    .gallery-group-name\r\n      @extend .limit-more-line\r\n      position: relative\r\n      margin: 0\r\n      padding: 8px 0\r\n      font-weight: bold\r\n      font-size: 1.65em\r\n      line-height: 1.5\r\n      -webkit-line-clamp: 2\r\n\r\n      &:after\r\n        position: absolute\r\n        bottom: 0\r\n        left: 0\r\n        width: 100%\r\n        height: 2px\r\n        background: $gallery-color\r\n        content: ''\r\n        transition: transform .35s\r\n        transform: translate3d(-100%, 0, 0)\r\n\r\n  .gallery-group-main\r\n    overflow: auto\r\n    padding: 0 0 16px\r\n\r\n  .gallery-container\r\n    margin: 0 0 20px\r\n    text-align: center\r\n    opacity: 0\r\n\r\n    &.loaded\r\n      opacity: 1\r\n\r\n    img\r\n      display: initial\r\n      margin: 0\r\n      width: 100%\r\n      height: 100%\r\n\r\n    .gallery-data\r\n      display: none\r\n\r\n    button\r\n      margin-top: 25px\r\n      padding: 8px 14px\r\n      background: var(--btn-bg)\r\n      color: var(--btn-color)\r\n      font-weight: bold\r\n      font-size: 1.1em\r\n      transition: all .3s\r\n      addBorderRadius(5)\r\n\r\n      &:hover\r\n        background: var(--btn-hover-color)\r\n\r\n        i\r\n          margin-left: 8px\r\n\r\n      i\r\n        margin-left: 4px\r\n        transition: all .3s\r\n\r\n  .loading-container\r\n    display: inline-block\r\n    overflow: hidden\r\n    width: 154px\r\n    height: 154px\r\n\r\n    .loading-item\r\n      position: relative\r\n      width: 100%\r\n      height: 100%\r\n      backface-visibility: hidden\r\n      transform: translateZ(0) scale(1)\r\n      transform-origin: 0 0\r\n\r\n      div\r\n        position: absolute\r\n        width: 30.8px\r\n        height: 30.8px\r\n        border-radius: 50%\r\n        background: #e15b64\r\n        transform: translate(61.6px, 61.6px) scale(1)\r\n        animation: loading-ball 1.92s infinite cubic-bezier(0, .5, .5, 1)\r\n\r\n        &:nth-child(1)\r\n          background: #f47e60\r\n          transform: translate(113.96px, 61.6px) scale(1)\r\n          animation: loading-ball-r .48s infinite cubic-bezier(0, .5, .5, 1), loading-ball-c 1.92s infinite step-start\r\n\r\n        &:nth-child(2)\r\n          background: #e15b64\r\n          animation-delay: -.48s\r\n\r\n        &:nth-child(3)\r\n          background: #f47e60\r\n          animation-delay: -.96s\r\n\r\n        &:nth-child(4)\r\n          background: #f8b26a\r\n          animation-delay: -1.44s\r\n\r\n        &:nth-child(5)\r\n          background: #abbd81\r\n          animation-delay: -1.92s\r\n\r\n@keyframes loading-ball\r\n  0%\r\n    transform: translate(9.24px, 61.6px) scale(0)\r\n\r\n  25%\r\n    transform: translate(9.24px, 61.6px) scale(0)\r\n\r\n  50%\r\n    transform: translate(9.24px, 61.6px) scale(1)\r\n\r\n  75%\r\n    transform: translate(61.6px, 61.6px) scale(1)\r\n\r\n  100%\r\n    transform: translate(113.96px, 61.6px) scale(1)\r\n\r\n@keyframes loading-ball-r\r\n  0%\r\n    transform: translate(113.96px, 61.6px) scale(1)\r\n\r\n  100%\r\n    transform: translate(113.96px, 61.6px) scale(0)\r\n\r\n@keyframes loading-ball-c\r\n  0%\r\n    background: #e15b64\r\n\r\n  25%\r\n    background: #abbd81\r\n\r\n  50%\r\n    background: #f8b26a\r\n\r\n  75%\r\n    background: #f47e60\r\n\r\n  100%\r\n    background: #e15b64\r\n"
  },
  {
    "path": "source/css/_tags/hexo.styl",
    "content": "// pullquote\r\nblockquote\r\n  &.pullquote\r\n    position: relative\r\n    max-width: 45%\r\n    font-size: 110%\r\n\r\n    &.left\r\n      float: left\r\n      margin: 1em .5em 0 0\r\n\r\n    &.right\r\n      float: right\r\n      margin: 1em 0 0 .5em\r\n\r\n// hexo tag video\r\n.video-container\r\n  position: relative\r\n  overflow: hidden\r\n  margin-bottom: 16px\r\n  padding-top: 56.25%\r\n  height: 0\r\n\r\n  iframe\r\n    position: absolute\r\n    top: 0\r\n    left: 0\r\n    margin-top: 0\r\n    width: 100%\r\n    height: 100%\r\n"
  },
  {
    "path": "source/css/_tags/hide.styl",
    "content": "// tag-hide\r\n.hide-inline,\r\n.hide-block\r\n  & > .hide-button\r\n    display: inline-block\r\n    padding: 5px 18px\r\n    background: $tag-hide-bg\r\n    color: var(--white)\r\n    addBorderRadius()\r\n    @extend .btn-effects\r\n\r\n    &:hover\r\n      background-color: var(--btn-hover-color)\r\n\r\n    &.open\r\n      display: none\r\n\r\n      & + div\r\n        display: block\r\n\r\n      & + span\r\n        display: inline\r\n\r\n  & > .hide-content\r\n    display: none\r\n\r\n.hide-inline\r\n  & > .hide-button\r\n    margin: 0 6px\r\n\r\n  & > .hide-content\r\n    margin: 0 6px\r\n\r\n.hide-block\r\n  margin: 0 0 16px\r\n\r\n.toggle\r\n  margin-bottom: 20px\r\n  border: 1px solid $tag-hide-toggle-bg\r\n  addBorderRadius(5, true)\r\n\r\n  & > .toggle-content\r\n    margin: 30px 24px\r\n\r\n  & > .toggle-button\r\n    padding: 6px 15px\r\n    background: $tag-hide-toggle-bg\r\n    color: #1F2D3D\r\n    list-style: none\r\n    cursor: pointer\r\n\r\n    &::-webkit-details-marker\r\n      display: none\r\n\r\n    &::before\r\n      @extend .fontawesomeIcon\r\n      margin-right: 8px\r\n      content: '\\f0d7'\r\n      transition: transform .3s ease\r\n      transform: rotate(-90deg)\r\n      transform-origin: center center\r\n\r\n  &[open] summary::before\r\n    transform: rotate(0)"
  },
  {
    "path": "source/css/_tags/inlineImg.styl",
    "content": ".container\r\n  .inline-img\r\n    display: inline\r\n    margin: 0 3px\r\n    height: 1.1em\r\n    vertical-align: text-bottom"
  },
  {
    "path": "source/css/_tags/label.styl",
    "content": ".hl-label\r\n  padding: 2px 4px\r\n  color: var(--btn-color, $btn-color)\r\n  addBorderRadius(3)\r\n\r\n  &.default\r\n    background-color: var(--btn-default-color)\r\n\r\n  for $type in $color-types\r\n    &.{$type}\r\n      background-color: unquote('var(--tags-' + $type + '-color)')\r\n"
  },
  {
    "path": "source/css/_tags/note.styl",
    "content": ".note\r\n  $note-icons = hexo-config('note.icons')\r\n  position: relative\r\n  margin: 0 0 20px\r\n  padding: 15px\r\n\r\n  if hexo-config('note.border_radius') is a 'unit'\r\n    border-radius: unit(hexo-config('note.border_radius'), px)\r\n\r\n  &.icon-padding\r\n    padding-left: 3em\r\n\r\n  & > .note-icon\r\n    position: absolute\r\n    top: calc(50% - .5em)\r\n    left: .8em\r\n    font-size: larger\r\n\r\n  for $type in $color-types\r\n    &.{$type}\r\n      &:not(.disabled)\r\n        border-left-color: unquote('var(--tags-' + $type + '-color)') !important\r\n\r\n        &.modern\r\n          border-left-color: transparent !important\r\n          color: unquote('var(--tags-' + $type + '-color)')\r\n\r\n        &:not(.simple)\r\n          background: unquote('var(--tags-' + $type + '-color-lighten)')\r\n\r\n      & > .note-icon\r\n        color: unquote('var(--tags-' + $type + '-color)')\r\n\r\n  &.simple\r\n    border: 1px solid var(--note-default-border)\r\n    border-left-width: 5px\r\n\r\n  &.modern\r\n    border: 1px solid transparent !important\r\n    background-color: var(--note-modern-default-bg)\r\n    color: var(--note-modern-default-text)\r\n\r\n  &.flat\r\n    border: initial\r\n    border-left: 5px solid var(--note-default-border)\r\n    background-color: var(--note-default-bg)\r\n    color: var(--note-default-text)\r\n\r\n  h2,\r\n  h3,\r\n  h4,\r\n  h5,\r\n  h6\r\n    if $note-icons\r\n      margin-top: 3px\r\n    else\r\n      margin-top: 0\r\n\r\n    margin-bottom: 0\r\n    padding-top: 0 !important\r\n    border-bottom: initial\r\n\r\n  p,\r\n  ul,\r\n  ol,\r\n  table,\r\n  pre,\r\n  blockquote,\r\n  img\r\n    &:first-child\r\n      margin-top: 0 !important\r\n\r\n    &:last-child\r\n      margin-bottom: 0 !important\r\n\r\n  .img-alt\r\n    margin: 5px 0 10px\r\n\r\n  if $note-icons\r\n    &:not(.no-icon)\r\n      padding-left: 3em\r\n\r\n      &::before\r\n        position: absolute\r\n        top: calc(50% - .95em)\r\n        left: .8em\r\n        font-size: larger\r\n        @extend .fontawesomeIcon\r\n\r\n  for $type in $note-types\r\n    &.{$type}\r\n      &.flat\r\n        background: unquote('var(--note-' + $type + '-bg)')\r\n        color: var(--font-color)\r\n\r\n      &.modern\r\n        border-color: unquote('var(--note-modern-' + $type + '-border)') !important\r\n        background: unquote('var(--note-modern-' + $type + '-bg)')\r\n        color: unquote('var(--note-modern-' + $type + '-text)')\r\n\r\n        a\r\n          &:not(.btn)\r\n            color: unquote('var(--note-modern-' + $type + '-text)')\r\n\r\n            &:hover\r\n              color: unquote('var(--note-modern-' + $type + '-hover)')\r\n\r\n      &:not(.modern)\r\n        border-left-color: unquote('var(--note-' + $type + '-border)')\r\n\r\n        h2,\r\n        h3,\r\n        h4,\r\n        h5,\r\n        h6\r\n          color: unquote('var(--note-' + $type + '-text)')\r\n\r\n      if $note-icons\r\n        &:not(.no-icon)\r\n          &::before\r\n            content: lookup('$note-' + $type + '-icon')\r\n\r\n          &:not(.modern)\r\n            &::before\r\n              color: unquote('var(--note-' + $type + '-text)')\r\n"
  },
  {
    "path": "source/css/_tags/series.styl",
    "content": ".container\r\n  .series-items\r\n    a\r\n      &:hover\r\n        color: var(--pseudo-hover)"
  },
  {
    "path": "source/css/_tags/tabs.styl",
    "content": "\r\n.container\r\n  .tabs\r\n    position: relative\r\n    margin: 0 0 20px\r\n    border-right: 1px solid var(--tab-border-color)\r\n    border-bottom: 1px solid var(--tab-border-color)\r\n    border-left: 1px solid var(--tab-border-color)\r\n    addBorderRadius()\r\n    overflow: hidden\r\n\r\n    > .nav-tabs\r\n      display: flex\r\n      flex-wrap: wrap\r\n      margin: 0\r\n      padding: 0\r\n      background: var(--tab-button-bg)\r\n\r\n      > .tab\r\n        flex-grow: 1\r\n        padding: 8px 18px\r\n        border-top: 2px solid var(--tab-border-color)\r\n        background: var(--tab-button-bg)\r\n        color: var(--tab-button-color)\r\n        line-height: 2\r\n        transition: all .4s\r\n\r\n        i\r\n          width: 1.5em\r\n\r\n        &.active\r\n          border-top: 2px solid $tab-active-border-color\r\n          background: var(--tab-button-active-bg)\r\n          cursor: default\r\n\r\n        &:not(.active)\r\n          &:hover\r\n            border-top: 2px solid var(--tab-button-hover-bg)\r\n            background: var(--tab-button-hover-bg)\r\n\r\n      &.no-default\r\n        & ~ .tab-to-top\r\n          display: none\r\n\r\n    > .tab-contents\r\n      .tab-item-content\r\n        position: relative\r\n        display: none\r\n        padding: 36px 24px 10px\r\n\r\n        +maxWidth768()\r\n          padding: 24px 14px\r\n\r\n        &.active\r\n          display: block\r\n          animation: tabshow .5s\r\n\r\n        > :last-child\r\n          margin-bottom: 0\r\n\r\n    > .tab-to-top\r\n      padding: 0 16px 10px 0\r\n      width: 100%\r\n      text-align: right\r\n\r\n      button\r\n        color: $tab-to-top-color\r\n\r\n        &:hover\r\n          color: $tab-to-top-hover-color\r\n\r\n@keyframes tabshow\r\n  0%\r\n    transform: translateY(15px)\r\n\r\n  100%\r\n    transform: translateY(0)\r\n"
  },
  {
    "path": "source/css/_tags/timeline.styl",
    "content": ".container\r\n  .timeline\r\n    margin: 0 10px 20px\r\n    padding: 14px 0 5px 20px\r\n    border-left: 2px solid var(--timeline-color, $theme-color)\r\n\r\n    for $type in $color-types\r\n      &.{$type}\r\n        --timeline-color: lookup('$tagsP-' + $type + '-color')\r\n        --timeline-bg: s('rgba(%s,%s,%s, 0.2)', red(lookup('$tagsP-' + $type + '-color')), green(lookup('$tagsP-' + $type + '-color')), blue(lookup('$tagsP-' + $type + '-color')))\r\n\r\n    .timeline-item\r\n      margin: 0 0 15px\r\n\r\n      &:hover\r\n        .item-circle\r\n          &:before\r\n            border-color: var(--timeline-color, $theme-color)\r\n\r\n      &.headline\r\n        .timeline-item-title\r\n          .item-circle\r\n            > p\r\n              font-weight: 600\r\n              font-size: 1.2em\r\n\r\n            &:before\r\n              left: -28px\r\n              border: 4px solid var(--timeline-color, $theme-color)\r\n\r\n        &:hover\r\n          .item-circle\r\n            &:before\r\n              border-color: var(--pseudo-hover)\r\n\r\n      .timeline-item-title\r\n        position: relative\r\n\r\n      .item-circle\r\n        &:before\r\n          position: absolute\r\n          top: 50%\r\n          left: -27px\r\n          width: 6px\r\n          height: 6px\r\n          border: 3px solid var(--pseudo-hover)\r\n          border-radius: 50%\r\n          background: var(--card-bg)\r\n          content: ''\r\n          transition: all .3s\r\n          transform: translate(0, -50%)\r\n\r\n        > p\r\n          margin: 0 0 8px\r\n          font-weight: 500\r\n\r\n      .timeline-item-content\r\n        position: relative\r\n        padding: 12px 15px\r\n        border-radius: 8px\r\n        background: var(--timeline-bg, lighten($theme-color, 85%))\r\n        font-size: .93em\r\n\r\n        & > :last-child\r\n          margin-bottom: 0\r\n\r\n    & + .timeline\r\n      margin-top: -20px"
  },
  {
    "path": "source/css/index.styl",
    "content": "if hexo-config('css_prefix')\r\n  @import 'nib'\r\n\r\n@import '_third-party/normalize.min.css'\r\n// project\r\n@import 'var'\r\n@import '_global/*'\r\n@import '_highlight/highlight'\r\n@import '_page/*'\r\n@import '_layout/*'\r\n@import '_tags/*'\r\n@import '_mode/*'\r\n\r\n// search\r\n@import '_search/index'"
  },
  {
    "path": "source/css/var.styl",
    "content": "// color\r\n$bright-blue = #49B1F5\r\n$strong-cyan = #00c4b6\r\n$light-orange = #FF7242\r\n$light-red = #F47466\r\n$themeColorEnable = hexo-config('theme_color') && hexo-config('theme_color.enable')\r\n$theme-color = $themeColorEnable && hexo-config('theme_color.main') ? convert(hexo-config('theme_color.main')) : $bright-blue\r\n$theme-paginator-color = $themeColorEnable && hexo-config('theme_color.paginator') ? convert(hexo-config('theme_color.paginator')) : $strong-cyan\r\n$theme-text-selection-color = $themeColorEnable && hexo-config('theme_color.text_selection') ? convert(hexo-config('theme_color.text_selection')) : $strong-cyan\r\n$theme-link-color = $themeColorEnable && hexo-config('theme_color.link_color') ? convert(hexo-config('theme_color.link_color')) : $bright-blue\r\n$theme-hr-color = $themeColorEnable && hexo-config('theme_color.hr_color') ? convert(hexo-config('theme_color.hr_color')) : $bright-blue\r\n$code-foreground = $themeColorEnable && hexo-config('theme_color.code_foreground') ? convert(hexo-config('theme_color.code_foreground')) : $light-red\r\n$code-background = $themeColorEnable && hexo-config('theme_color.code_background') ? convert(hexo-config('theme_color.code_background')) : rgba(27, 31, 35, .05)\r\n$theme-toc-color = $themeColorEnable && hexo-config('theme_color.toc_color') ? convert(hexo-config('theme_color.toc_color')) : $strong-cyan\r\n// font\r\n$chineseFont = $language == 'zh-CN' ? 'Microsoft YaHei' : 'Microsoft JhengHei'\r\n$default-font-family = -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Lato, Roboto, 'PingFang SC', $chineseFont, sans-serif\r\n$default-code-font = consolas, Menlo, monospace, 'PingFang SC', $chineseFont, sans-serif\r\n$font-family = hexo-config('font.font_family') ? unquote(hexo-config('font.font_family')) : $default-font-family\r\n$code-font-family = hexo-config('font.code_font_family') ? unquote(hexo-config('font.code_font_family')) : $default-code-font\r\n$site-name-font = hexo-config('blog_title_font.font_family') && unquote(hexo-config('blog_title_font.font_family'))\r\n// hr\r\n$hrEnable = hexo-config('hr_icon') && hexo-config('hr_icon.enable')\r\n$hr-icon = $hrEnable && hexo-config('hr_icon.icon') ? hexo-config('hr_icon.icon') : '\\f0c4'\r\n$hr-icon-top = $hrEnable && hexo-config('hr_icon.icon_top') ? convert(hexo-config('hr_icon.icon_top')) : -10px\r\n// page beautify\r\n$beautifyEnable = hexo-config('beautify.enable')\r\n$title-prefix-icon = $beautifyEnable && hexo-config('beautify.title_prefix_icon') ? hexo-config('beautify.title_prefix_icon') : '\\f0c1'\r\n$title-prefix-icon-color = $beautifyEnable && hexo-config('beautify.title_prefix_icon_color') ? convert(hexo-config('beautify.title_prefix_icon_color')) : $light-red\r\n// Global Variables\r\n$font-size = hexo-config('font.global_font_size') ? convert(hexo-config('font.global_font_size')) : 14px\r\n$code-font-size = hexo-config('font.code_font_size') ? convert(hexo-config('font.code_font_size')) : var(--global-font-size)\r\n$font-color = #1F2D3D\r\n$text-line-height = 2\r\n$index_top_img_height = hexo-config('index_top_img_height') ? convert(hexo-config('index_top_img_height')) : 100vh\r\n$index_site_info_top = hexo-config('index_site_info_top') ? convert(hexo-config('index_site_info_top')) : 43%\r\n// Global color & SVG\r\n$light-blue = $theme-color\r\n$dark-black = #000000\r\n$light-grey = #EEEEEE\r\n$grey = #858585\r\n$dark-grey = #cacaca\r\n$white = #FFFFFF\r\n$whitesmoke = #f5f5f5\r\n$font-black = #4C4948\r\n$card-bg = $white\r\n$text-highlight-color = $font-color\r\n$text-hover = $theme-color\r\n$text-bg-hover = $theme-color\r\n// code\r\n$line-height-code-block = 1.6\r\n$blockquote-color = #6a737d\r\n$blockquote-padding-color = $themeColorEnable && hexo-config('theme_color.blockquote_padding_color') ? convert(hexo-config('theme_color.blockquote_padding_color')) : #49B1F5\r\n$blockquote-background-color = $themeColorEnable && hexo-config('theme_color.blockquote_background_color') ? alpha(convert(hexo-config('theme_color.blockquote_background_color')), .1) : alpha($blockquote-padding-color, .1)\r\n// page\r\n$body-bg = #fff\r\n$a-link-color = #99a9bf\r\n$sticky-color = $light-orange\r\n$theme-meta-color = $themeColorEnable && hexo-config('theme_color.meta_color') ? convert(hexo-config('theme_color.meta_color')) : #858585\r\n// sidebar\r\n$sidebar-background = #f6f8fa\r\n$sidebar-width = 330px\r\n// aside\r\n$toc-link-color = #666261\r\n$toc-mobile-width = calc(100% - 80px)\r\n$toc-mobile-maxWidth = 380px\r\n$toc-active-color = #fff\r\n// Button\r\n$button-color = #fff\r\n$button-hover-color = $themeColorEnable && hexo-config('theme_color.button_hover') ? convert(hexo-config('theme_color.button_hover')) : $light-orange\r\n$button-bg = $theme-color\r\n$pseudo-hover = $button-hover-color\r\n// scrollbar\r\n$scrollbar-color = $themeColorEnable && hexo-config('theme_color.scrollbar_color') ? convert(hexo-config('theme_color.scrollbar_color')) : $theme-color\r\n// table\r\n$table-thead-bg = #99a9bf\r\n// reward\r\n$reward-pop-up-bg = #f5f5f5\r\n$reward-pop-up-color = #858585\r\n// search\r\n$search-bg = #f6f8fa\r\n$search-input-color = $font-black\r\n$search-color = $theme-color\r\n$search-keyword-highlight = #F47466\r\n$search-a-color = $font-black\r\n// comments\r\n$comments-switch-first-text = $bright-blue\r\n$comments-switch-second-text = $light-orange\r\n$comments-switch-round = #fff\r\n$comments-switch-bg = #f6f8fa\r\n// noticeOutdate\r\n$noticeOutdate-bg = #ffe6e6\r\n$noticeOutdate-color = #ff6666\r\n$noticeOutdate-border = #ff8080\r\n// gallery\r\n$gallery-color = #fff\r\n// tag-hide\r\n$tag-hide-bg = $theme-color\r\n$tag-hide-toggle-bg = #f0f0f0\r\n// preloader\r\n$preloader-bg = #37474f\r\n$preloader-word-color = #fff\r\n// rightside\r\n$rightside-bottom = hexo-config('rightside_bottom') ? convert(hexo-config('rightside_bottom')) : 40px\r\n// fireworks\r\n$fireworks-zIndex = hexo-config('fireworks.zIndex') ? hexo-config('fireworks.zIndex') : 99999\r\n// Tag Plugins - Note\r\nhexo-config('note.light_bg_offset') is a 'unit' ? ($lbg = unit(hexo-config('note.light_bg_offset'), '%')) : ($lbg = 0)\r\n$note-types = 'default' 'primary' 'info' 'success' 'warning' 'danger'\r\n// Default\r\n$note-default-border = #777\r\n$note-default-bg = lighten(spin($note-default-border, 0), 94% + $lbg)\r\n$note-default-text = $note-default-border\r\n$note-default-icon = '\\f0a9'\r\n$note-modern-default-border = #e1e1e1\r\n$note-modern-default-bg = lighten(spin($note-modern-default-border, 10), 60% + ($lbg * 4))\r\n$note-modern-default-text = #666\r\n$note-modern-default-hover = darken(spin($note-modern-default-text, -10), 32%)\r\n// Primary\r\n$note-primary-border = #6f42c1\r\n$note-primary-bg = lighten(spin($note-primary-border, 10), 92% + $lbg)\r\n$note-primary-text = $note-primary-border\r\n$note-primary-icon = '\\f055'\r\n$note-modern-primary-border = #e1c2ff\r\n$note-modern-primary-bg = lighten(spin($note-modern-primary-border, 10), 40% + ($lbg * 4))\r\n$note-modern-primary-text = #6f42c1\r\n$note-modern-primary-hover = darken(spin($note-modern-primary-text, -10), 22%)\r\n// Info\r\n$note-info-border = #428bca\r\n$note-info-bg = lighten(spin($note-info-border, -10), 91% + $lbg)\r\n$note-info-text = $note-info-border\r\n$note-info-icon = '\\f05a'\r\n$note-modern-info-border = #b3e5ef\r\n$note-modern-info-bg = lighten(spin($note-modern-info-border, 10), 50% + ($lbg * 4))\r\n$note-modern-info-text = #31708f\r\n$note-modern-info-hover = darken(spin($note-modern-info-text, -10), 32%)\r\n// Success\r\n$note-success-border = #5cb85c\r\n$note-success-bg = lighten(spin($note-success-border, 10), 90% + $lbg)\r\n$note-success-text = $note-success-border\r\n$note-success-icon = '\\f058'\r\n$note-modern-success-border = #d0e6be\r\n$note-modern-success-bg = lighten(spin($note-modern-success-border, 10), 40% + ($lbg * 4))\r\n$note-modern-success-text = #3c763d\r\n$note-modern-success-hover = darken(spin($note-modern-success-text, -10), 27%)\r\n// Warning\r\n$note-warning-border = #f0ad4e\r\n$note-warning-bg = lighten(spin($note-warning-border, 10), 88% + $lbg)\r\n$note-warning-text = $note-warning-border\r\n$note-warning-icon = '\\f06a'\r\n$note-modern-warning-border = #fae4cd\r\n$note-modern-warning-bg = lighten(spin($note-modern-warning-border, 10), 43% + ($lbg * 4))\r\n$note-modern-warning-text = #8a6d3b\r\n$note-modern-warning-hover = darken(spin($note-modern-warning-text, -10), 18%)\r\n// Danger\r\n$note-danger-border = #d9534f\r\n$note-danger-bg = lighten(spin($note-danger-border, -10), 92% + $lbg)\r\n$note-danger-text = $note-danger-border\r\n$note-danger-icon = '\\f056'\r\n$note-modern-danger-border = #ebcdd2\r\n$note-modern-danger-bg = lighten(spin($note-modern-danger-border, 10), 35% + ($lbg * 4))\r\n$note-modern-danger-text = #a94442\r\n$note-modern-danger-hover = darken(spin($note-modern-danger-text, -10), 22%)\r\n// Tag Plugins - Button/note\r\n$color-types = 'blue' 'pink' 'red' 'purple' 'orange' 'green'\r\n$btn-color = #fff\r\n$btn-default-color = #777\r\n$tagsP-blue-color = #428bca\r\n$tagsP-pink-color = #FF69B4\r\n$tagsP-red-color = #FF0000\r\n$tagsP-orange-color = #FF8C00\r\n$tagsP-purple-color = #6f42c1\r\n$tagsP-green-color = #5cb85c\r\n// Tag Plugins - Tab\r\n$tab-border-color = #f0f0f0\r\n$tab-button-bg = #f0f0f0\r\n$tab-button-color = $font-color\r\n$tab-button-hover-bg = darken($tab-border-color, 8)\r\n$tab-active-border-color = $theme-color\r\n$tab-button-active-bg = $card-bg\r\n$tab-to-top-color = #99a9bf\r\n$tab-to-top-hover-color = $theme-color\r\n// Tag Plugins - timeline\r\n$timeline-default-color = $theme-color\r\n// note - darkmode\r\n// Default\r\n$note-dark-default-border = #5a5a5a\r\n$note-dark-default-bg = #2b2b2b\r\n$note-dark-default-text = #b3b3b3\r\n$note-dark-modern-default-border = #9a9a9a\r\n$note-dark-modern-default-bg = #353535\r\n$note-dark-modern-default-text = #c4c4c4\r\n$note-dark-modern-default-hover = #d0d0d0\r\n// Primary\r\n$note-dark-primary-border = #5935a1\r\n$note-dark-primary-bg = #2e1c3e\r\n$note-dark-primary-text = #a47dd4\r\n$note-dark-modern-primary-border = #9985cc\r\n$note-dark-modern-primary-bg = #3c2d4c\r\n$note-dark-modern-primary-text = #b693e6\r\n$note-dark-modern-primary-hover = #c9a8f0\r\n// Info\r\n$note-dark-info-border = #346fa2\r\n$note-dark-info-bg = #1f2e3b\r\n$note-dark-info-text = #7bb3db\r\n$note-dark-modern-info-border = #7ca8b5\r\n$note-dark-modern-info-bg = #2b3c44\r\n$note-dark-modern-info-text = #8fc6e0\r\n$note-dark-modern-info-hover = #a3d4ea\r\n// Success\r\n$note-dark-success-border = #4a944a\r\n$note-dark-success-bg = #202e20\r\n$note-dark-success-text = #82c682\r\n$note-dark-modern-success-border = #8bb087\r\n$note-dark-modern-success-bg = #2c3d2c\r\n$note-dark-modern-success-text = #96d196\r\n$note-dark-modern-success-hover = #a8dca8\r\n// Warning\r\n$note-dark-warning-border = #c08a3e\r\n$note-dark-warning-bg = #3e301f\r\n$note-dark-warning-text = #e6ba6b\r\n$note-dark-modern-warning-border = #b8a285\r\n$note-dark-modern-warning-bg = #4b3c2b\r\n$note-dark-modern-warning-text = #d4b373\r\n$note-dark-modern-warning-hover = #e0c080\r\n// Danger\r\n$note-dark-danger-border = #b34440\r\n$note-dark-danger-bg = #3b201f\r\n$note-dark-danger-text = #e67572\r\n$note-dark-modern-danger-border = #c7898c\r\n$note-dark-modern-danger-bg = #4d2b2e\r\n$note-dark-modern-danger-text = #d98b8e\r\n$note-dark-modern-danger-hover = #e59fa2"
  },
  {
    "path": "source/js/main.js",
    "content": "document.addEventListener('DOMContentLoaded', () => {\r\n  let headerContentWidth, $nav\r\n  let mobileSidebarOpen = false\r\n\r\n  const adjustMenu = init => {\r\n    const getAllWidth = ele => Array.from(ele).reduce((width, i) => width + i.offsetWidth, 0)\r\n\r\n    if (init) {\r\n      const blogInfoWidth = getAllWidth(document.querySelector('#blog-info > a').children)\r\n      const menusWidth = getAllWidth(document.getElementById('menus').children)\r\n      headerContentWidth = blogInfoWidth + menusWidth\r\n      $nav = document.getElementById('nav')\r\n    }\r\n\r\n    const hideMenuIndex = window.innerWidth <= 768 || headerContentWidth > $nav.offsetWidth - 120\r\n    $nav.classList.toggle('hide-menu', hideMenuIndex)\r\n  }\r\n\r\n  // 初始化header\r\n  const initAdjust = () => {\r\n    adjustMenu(true)\r\n    $nav.classList.add('show')\r\n  }\r\n\r\n  // sidebar menus\r\n  const sidebarFn = {\r\n    open: () => {\r\n      btf.overflowPaddingR.add()\r\n      btf.animateIn(document.getElementById('menu-mask'), 'to_show 0.5s')\r\n      document.getElementById('sidebar-menus').classList.add('open')\r\n      mobileSidebarOpen = true\r\n    },\r\n    close: () => {\r\n      btf.overflowPaddingR.remove()\r\n      btf.animateOut(document.getElementById('menu-mask'), 'to_hide 0.5s')\r\n      document.getElementById('sidebar-menus').classList.remove('open')\r\n      mobileSidebarOpen = false\r\n    }\r\n  }\r\n\r\n  /**\r\n   * 首頁top_img底下的箭頭\r\n   */\r\n  const scrollDownInIndex = () => {\r\n    const handleScrollToDest = () => {\r\n      btf.scrollToDest(document.getElementById('content-inner').offsetTop, 300)\r\n    }\r\n\r\n    const $scrollDownEle = document.getElementById('scroll-down')\r\n    $scrollDownEle && btf.addEventListenerPjax($scrollDownEle, 'click', handleScrollToDest)\r\n  }\r\n\r\n  /**\r\n   * 代碼\r\n   * 只適用於Hexo默認的代碼渲染\r\n   */\r\n  const addHighlightTool = () => {\r\n    const highLight = GLOBAL_CONFIG.highlight\r\n    if (!highLight) return\r\n\r\n    const { highlightCopy, highlightLang, highlightHeightLimit, highlightFullpage, highlightMacStyle, plugin } = highLight\r\n    const isHighlightShrink = GLOBAL_CONFIG_SITE.isHighlightShrink\r\n    const isShowTool = highlightCopy || highlightLang || isHighlightShrink !== undefined || highlightFullpage || highlightMacStyle\r\n    const isNotHighlightJs = plugin !== 'highlight.js'\r\n    const isPrismjs = plugin === 'prismjs'\r\n    const $figureHighlight = isNotHighlightJs\r\n      ? Array.from(document.querySelectorAll('code[class*=\"language-\"]')).map(code => code.parentElement)\r\n      : document.querySelectorAll('figure.highlight')\r\n\r\n    if (!((isShowTool || highlightHeightLimit) && $figureHighlight.length)) return\r\n\r\n    const highlightShrinkClass = isHighlightShrink === true ? 'closed' : ''\r\n    const highlightShrinkEle = isHighlightShrink !== undefined ? '<i class=\"fas fa-angle-down expand\"></i>' : ''\r\n    const highlightCopyEle = highlightCopy ? '<i class=\"fas fa-paste copy-button\"></i>' : ''\r\n    const highlightMacStyleEle = '<div class=\"macStyle\"><div class=\"mac-close\"></div><div class=\"mac-minimize\"></div><div class=\"mac-maximize\"></div></div>'\r\n    const highlightFullpageEle = highlightFullpage ? '<i class=\"fa-solid fa-up-right-and-down-left-from-center fullpage-button\"></i>' : ''\r\n\r\n    const alertInfo = (ele, text) => {\r\n      if (GLOBAL_CONFIG.Snackbar !== undefined) {\r\n        btf.snackbarShow(text)\r\n      } else {\r\n        const newEle = document.createElement('div')\r\n        newEle.className = 'copy-notice'\r\n        newEle.textContent = text\r\n        document.body.appendChild(newEle)\r\n\r\n        const buttonRect = ele.getBoundingClientRect()\r\n        const scrollTop = window.pageYOffset || document.documentElement.scrollTop\r\n        const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft\r\n\r\n        // X-axis boundary check\r\n        const halfWidth = newEle.offsetWidth / 2\r\n        const centerLeft = buttonRect.left + scrollLeft + buttonRect.width / 2\r\n        const finalLeft = Math.max(halfWidth + 10, Math.min(window.innerWidth - halfWidth - 10, centerLeft))\r\n\r\n        // Show tooltip below button if too close to top\r\n        const normalTop = buttonRect.top + scrollTop - 40\r\n        const shouldShowBelow = buttonRect.top < 60 || normalTop < 10\r\n\r\n        const topValue = shouldShowBelow ? buttonRect.top + scrollTop + buttonRect.height + 10 : normalTop\r\n\r\n        newEle.style.cssText = `\r\n      top: ${topValue + 10}px;\r\n      left: ${finalLeft}px;\r\n      transform: translateX(-50%);\r\n      opacity: 0;\r\n      transition: opacity 0.3s ease, top 0.3s ease;\r\n    `\r\n\r\n        requestAnimationFrame(() => {\r\n          newEle.style.opacity = '1'\r\n          newEle.style.top = `${topValue}px`\r\n        })\r\n\r\n        setTimeout(() => {\r\n          newEle.style.opacity = '0'\r\n          newEle.style.top = `${topValue + 10}px`\r\n          setTimeout(() => {\r\n            newEle?.remove()\r\n          }, 300)\r\n        }, 800)\r\n      }\r\n    }\r\n\r\n    const copy = async (text, ctx) => {\r\n      try {\r\n        await navigator.clipboard.writeText(text)\r\n        alertInfo(ctx, GLOBAL_CONFIG.copy.success)\r\n      } catch (err) {\r\n        console.error('Failed to copy: ', err)\r\n        alertInfo(ctx, GLOBAL_CONFIG.copy.noSupport)\r\n      }\r\n    }\r\n\r\n    // click events\r\n    const highlightCopyFn = (ele, clickEle) => {\r\n      const $buttonParent = ele.parentNode\r\n      $buttonParent.classList.add('copy-true')\r\n      const preCodeSelector = isNotHighlightJs ? 'pre code' : 'table .code pre'\r\n      const codeElement = $buttonParent.querySelector(preCodeSelector)\r\n      if (!codeElement) return\r\n      copy(codeElement.innerText, clickEle)\r\n      $buttonParent.classList.remove('copy-true')\r\n    }\r\n\r\n    const highlightShrinkFn = ele => ele.classList.toggle('closed')\r\n\r\n    const codeFullpage = (item, clickEle) => {\r\n      const wrapEle = item.closest('figure.highlight')\r\n      const isFullpage = wrapEle.classList.toggle('code-fullpage')\r\n\r\n      document.body.style.overflow = isFullpage ? 'hidden' : ''\r\n      clickEle.classList.toggle('fa-down-left-and-up-right-to-center', isFullpage)\r\n      clickEle.classList.toggle('fa-up-right-and-down-left-from-center', !isFullpage)\r\n    }\r\n\r\n    const highlightToolsFn = e => {\r\n      const $target = e.target.classList\r\n      const currentElement = e.currentTarget\r\n      if ($target.contains('expand')) highlightShrinkFn(currentElement)\r\n      else if ($target.contains('copy-button')) highlightCopyFn(currentElement, e.target)\r\n      else if ($target.contains('fullpage-button')) codeFullpage(currentElement, e.target)\r\n    }\r\n\r\n    const expandCode = e => e.currentTarget.classList.toggle('expand-done')\r\n\r\n    // 獲取隱藏狀態下元素的真實高度\r\n    const getActualHeight = item => {\r\n      if (item.offsetHeight > 0) return item.offsetHeight\r\n      const hiddenElements = new Map()\r\n\r\n      const fix = () => {\r\n        let current = item\r\n        while (current !== document.body && current != null) {\r\n          if (window.getComputedStyle(current).display === 'none') {\r\n            hiddenElements.set(current, current.getAttribute('style') || '')\r\n          }\r\n          current = current.parentNode\r\n        }\r\n\r\n        const style = 'visibility: hidden !important; display: block !important;'\r\n        hiddenElements.forEach((originalStyle, elem) => {\r\n          elem.setAttribute('style', originalStyle ? originalStyle + ';' + style : style)\r\n        })\r\n      }\r\n\r\n      const restore = () => {\r\n        hiddenElements.forEach((originalStyle, elem) => {\r\n          if (originalStyle === '') elem.removeAttribute('style')\r\n          else elem.setAttribute('style', originalStyle)\r\n        })\r\n      }\r\n\r\n      fix()\r\n      const height = item.offsetHeight\r\n      restore()\r\n      return height\r\n    }\r\n\r\n    const createEle = (lang, item) => {\r\n      const fragment = document.createDocumentFragment()\r\n\r\n      if (isShowTool) {\r\n        const hlTools = document.createElement('div')\r\n        hlTools.className = `highlight-tools ${highlightShrinkClass}`\r\n        hlTools.innerHTML = highlightMacStyleEle + highlightShrinkEle + lang + highlightCopyEle + highlightFullpageEle\r\n        btf.addEventListenerPjax(hlTools, 'click', highlightToolsFn)\r\n        fragment.appendChild(hlTools)\r\n      }\r\n\r\n      if (highlightHeightLimit && getActualHeight(item) > highlightHeightLimit + 30) {\r\n        const ele = document.createElement('div')\r\n        ele.className = 'code-expand-btn'\r\n        ele.innerHTML = '<i class=\"fas fa-angle-double-down\"></i>'\r\n        btf.addEventListenerPjax(ele, 'click', expandCode)\r\n        fragment.appendChild(ele)\r\n      }\r\n\r\n      isNotHighlightJs ? item.parentNode.insertBefore(fragment, item) : item.insertBefore(fragment, item.firstChild)\r\n    }\r\n\r\n    $figureHighlight.forEach(item => {\r\n      let langName = ''\r\n      if (isNotHighlightJs) {\r\n        const newClassName = isPrismjs ? 'prismjs' : 'default'\r\n        btf.wrap(item, 'figure', { class: `highlight ${newClassName}` })\r\n      }\r\n\r\n      if (!highlightLang) {\r\n        createEle('', item)\r\n        return\r\n      }\r\n\r\n      if (isNotHighlightJs) {\r\n        langName = isPrismjs ? item.getAttribute('data-language') || 'Code' : item.querySelector('code').getAttribute('class').replace('language-', '')\r\n      } else {\r\n        langName = item.getAttribute('class').split(' ')[1]\r\n        if (langName === 'plain' || langName === undefined) langName = 'Code'\r\n      }\r\n      createEle(`<div class=\"code-lang\">${langName}</div>`, item)\r\n    })\r\n  }\r\n\r\n  /**\r\n   * PhotoFigcaption\r\n   */\r\n  const addPhotoFigcaption = () => {\r\n    if (!GLOBAL_CONFIG.isPhotoFigcaption) return\r\n    document.querySelectorAll('#article-container img').forEach(item => {\r\n      const altValue = item.title || item.alt\r\n      if (!altValue) return\r\n      const ele = document.createElement('div')\r\n      ele.className = 'img-alt text-center'\r\n      ele.textContent = altValue\r\n      item.insertAdjacentElement('afterend', ele)\r\n    })\r\n  }\r\n\r\n  /**\r\n   * Lightbox\r\n   */\r\n  const runLightbox = () => {\r\n    btf.loadLightbox(document.querySelectorAll('#article-container img:not(.no-lightbox)'))\r\n  }\r\n\r\n  /**\r\n   * justified-gallery 圖庫排版\r\n   */\r\n\r\n  const fetchUrl = async url => {\r\n    try {\r\n      const response = await fetch(url)\r\n      return await response.json()\r\n    } catch (error) {\r\n      console.error('Failed to fetch URL:', error)\r\n      return []\r\n    }\r\n  }\r\n\r\n  const runJustifiedGallery = (container, data, config) => {\r\n    const { isButton, limit, firstLimit, tabs } = config\r\n\r\n    const dataLength = data.length\r\n    const maxGroupKey = Math.ceil((dataLength - firstLimit) / limit + 1)\r\n\r\n    // Gallery configuration\r\n    const igConfig = {\r\n      gap: 5,\r\n      isConstantSize: true,\r\n      sizeRange: [150, 600],\r\n      // useResizeObserver: true,\r\n      // observeChildren: true,\r\n      useTransform: true\r\n      // useRecycle: false\r\n    }\r\n\r\n    const ig = new InfiniteGrid.JustifiedInfiniteGrid(container, igConfig)\r\n    let isLayoutHidden = false\r\n\r\n    // Utility functions\r\n    const sanitizeString = str => (str && str.replace(/\"/g, '&quot;')) || ''\r\n\r\n    const createImageItem = item => {\r\n      const alt = item.alt ? `alt=\"${sanitizeString(item.alt)}\"` : ''\r\n      const title = item.title ? `title=\"${sanitizeString(item.title)}\"` : ''\r\n      return `<div class=\"item\">\r\n        <img src=\"${item.url}\" data-grid-maintained-target=\"true\" ${alt} ${title} />\r\n      </div>`\r\n    }\r\n\r\n    const getItems = (nextGroupKey, count, isFirst = false) => {\r\n      const startIndex = isFirst ? (nextGroupKey - 1) * count : (nextGroupKey - 2) * count + firstLimit\r\n      return data.slice(startIndex, startIndex + count).map(createImageItem)\r\n    }\r\n\r\n    // Load more button\r\n    const addLoadMoreButton = container => {\r\n      const button = document.createElement('button')\r\n      button.innerHTML = `${GLOBAL_CONFIG.infinitegrid.buttonText}<i class=\"fa-solid fa-arrow-down\"></i>`\r\n\r\n      button.addEventListener('click', () => {\r\n        button.remove()\r\n        btf.setLoading.add(container)\r\n        appendItems(ig.getGroups().length + 1, limit)\r\n      }, { once: true })\r\n\r\n      container.insertAdjacentElement('afterend', button)\r\n    }\r\n\r\n    const appendItems = (nextGroupKey, count, isFirst) => {\r\n      ig.append(getItems(nextGroupKey, count, isFirst), nextGroupKey)\r\n    }\r\n\r\n    // Event handlers\r\n    const handleRenderComplete = e => {\r\n      if (tabs) {\r\n        const parentNode = container.parentNode\r\n        if (isLayoutHidden) {\r\n          parentNode.style.visibility = 'visible'\r\n        }\r\n        if (container.offsetHeight === 0) {\r\n          parentNode.style.visibility = 'hidden'\r\n          isLayoutHidden = true\r\n        }\r\n      }\r\n\r\n      const { updated, isResize, mounted } = e\r\n      if (!updated.length || !mounted.length || isResize) return\r\n\r\n      btf.loadLightbox(container.querySelectorAll('img:not(.medium-zoom-image)'))\r\n\r\n      if (ig.getGroups().length === maxGroupKey) {\r\n        btf.setLoading.remove(container)\r\n        !tabs && ig.off('renderComplete', handleRenderComplete)\r\n        return\r\n      }\r\n\r\n      if (isButton) {\r\n        btf.setLoading.remove(container)\r\n        addLoadMoreButton(container)\r\n      }\r\n    }\r\n\r\n    const handleRequestAppend = btf.debounce(e => {\r\n      const nextGroupKey = (+e.groupKey || 0) + 1\r\n\r\n      if (nextGroupKey === 1) appendItems(nextGroupKey, firstLimit, true)\r\n      else appendItems(nextGroupKey, limit)\r\n\r\n      if (nextGroupKey === maxGroupKey) ig.off('requestAppend', handleRequestAppend)\r\n    }, 300)\r\n\r\n    btf.setLoading.add(container)\r\n    ig.on('renderComplete', handleRenderComplete)\r\n\r\n    if (isButton) {\r\n      appendItems(1, firstLimit, true)\r\n    } else {\r\n      ig.on('requestAppend', handleRequestAppend)\r\n      ig.renderItems()\r\n    }\r\n\r\n    btf.addGlobalFn('pjaxSendOnce', () => ig.destroy())\r\n  }\r\n\r\n  const addJustifiedGallery = async (elements, tabs = false) => {\r\n    if (!elements.length) return\r\n\r\n    const initGallery = async () => {\r\n      for (const element of elements) {\r\n        if (btf.isHidden(element) || element.classList.contains('loaded')) continue\r\n\r\n        const config = {\r\n          isButton: element.getAttribute('data-button') === 'true',\r\n          limit: parseInt(element.getAttribute('data-limit'), 10),\r\n          firstLimit: parseInt(element.getAttribute('data-first'), 10),\r\n          tabs\r\n        }\r\n\r\n        const container = element.firstElementChild\r\n        const content = container.textContent\r\n        container.textContent = ''\r\n        element.classList.add('loaded')\r\n\r\n        try {\r\n          const data = element.getAttribute('data-type') === 'url' ? await fetchUrl(content) : JSON.parse(content)\r\n          runJustifiedGallery(container, data, config)\r\n        } catch (error) {\r\n          console.error('Gallery data parsing failed:', error)\r\n        }\r\n      }\r\n    }\r\n\r\n    if (typeof InfiniteGrid === 'function') {\r\n      await initGallery()\r\n    } else {\r\n      await btf.getScript(GLOBAL_CONFIG.infinitegrid.js)\r\n      await initGallery()\r\n    }\r\n  }\r\n\r\n  /**\r\n   * rightside scroll percent\r\n   */\r\n  const rightsideScrollPercent = currentTop => {\r\n    const scrollPercent = btf.getScrollPercent(currentTop, document.body)\r\n    const goUpElement = document.getElementById('go-up')\r\n\r\n    if (scrollPercent < 95) {\r\n      goUpElement.classList.add('show-percent')\r\n      goUpElement.querySelector('.scroll-percent').textContent = scrollPercent\r\n    } else {\r\n      goUpElement.classList.remove('show-percent')\r\n    }\r\n  }\r\n\r\n  /**\r\n   * 滾動處理\r\n   */\r\n  const scrollFn = () => {\r\n    const $rightside = document.getElementById('rightside')\r\n    const innerHeight = window.innerHeight + 56\r\n    let initTop = 0\r\n    const $header = document.getElementById('page-header')\r\n    const isChatBtn = typeof chatBtn !== 'undefined'\r\n    const isShowPercent = GLOBAL_CONFIG.percent.rightside\r\n\r\n    // 檢查文檔高度是否小於視窗高度\r\n    const checkDocumentHeight = () => {\r\n      if (document.body.scrollHeight <= innerHeight) {\r\n        $rightside.classList.add('rightside-show')\r\n        return true\r\n      }\r\n      return false\r\n    }\r\n\r\n    // 如果文檔高度小於視窗高度,直接返回\r\n    if (checkDocumentHeight()) return\r\n\r\n    // find the scroll direction\r\n    const scrollDirection = currentTop => {\r\n      const result = currentTop > initTop // true is down & false is up\r\n      initTop = currentTop\r\n      return result\r\n    }\r\n\r\n    let flag = ''\r\n    const scrollTask = btf.throttle(() => {\r\n      const currentTop = window.scrollY || document.documentElement.scrollTop\r\n      const isDown = scrollDirection(currentTop)\r\n      if (currentTop > 56) {\r\n        if (flag === '') {\r\n          $header.classList.add('nav-fixed')\r\n          $rightside.classList.add('rightside-show')\r\n        }\r\n\r\n        if (isDown) {\r\n          if (flag !== 'down') {\r\n            $header.classList.remove('nav-visible')\r\n            isChatBtn && window.chatBtn.hide()\r\n            flag = 'down'\r\n          }\r\n        } else {\r\n          if (flag !== 'up') {\r\n            $header.classList.add('nav-visible')\r\n            isChatBtn && window.chatBtn.show()\r\n            flag = 'up'\r\n          }\r\n        }\r\n      } else {\r\n        flag = ''\r\n        if (currentTop === 0) {\r\n          $header.classList.remove('nav-fixed', 'nav-visible')\r\n        }\r\n        $rightside.classList.remove('rightside-show')\r\n      }\r\n\r\n      isShowPercent && rightsideScrollPercent(currentTop)\r\n      checkDocumentHeight()\r\n    }, 300)\r\n\r\n    btf.addEventListenerPjax(window, 'scroll', scrollTask, { passive: true })\r\n  }\r\n\r\n  /**\r\n  * toc,anchor\r\n  */\r\n  const scrollFnToDo = () => {\r\n    const isToc = GLOBAL_CONFIG_SITE.isToc\r\n    const isAnchor = GLOBAL_CONFIG.isAnchor\r\n    const $article = document.getElementById('article-container')\r\n\r\n    if (!($article && (isToc || isAnchor))) return\r\n\r\n    let $tocLink, $cardToc, autoScrollToc, $tocPercentage, isExpand\r\n\r\n    if (isToc) {\r\n      const $cardTocLayout = document.getElementById('card-toc')\r\n      $cardToc = $cardTocLayout.querySelector('.toc-content')\r\n      $tocLink = $cardToc.querySelectorAll('.toc-link')\r\n      $tocPercentage = $cardTocLayout.querySelector('.toc-percentage')\r\n      isExpand = $cardToc.classList.contains('is-expand')\r\n\r\n      // toc元素點擊\r\n      const tocItemClickFn = e => {\r\n        const target = e.target.closest('.toc-link')\r\n        if (!target) return\r\n\r\n        e.preventDefault()\r\n        btf.scrollToDest(btf.getEleTop(document.getElementById(decodeURI(target.getAttribute('href')).replace('#', ''))), 300)\r\n        if (window.innerWidth < 900) {\r\n          $cardTocLayout.classList.remove('open')\r\n        }\r\n      }\r\n\r\n      btf.addEventListenerPjax($cardToc, 'click', tocItemClickFn)\r\n\r\n      autoScrollToc = item => {\r\n        const sidebarHeight = $cardToc.clientHeight\r\n        const itemOffsetTop = item.offsetTop\r\n        const itemHeight = item.clientHeight\r\n        const scrollTop = $cardToc.scrollTop\r\n        const offset = itemOffsetTop - scrollTop\r\n        const middlePosition = (sidebarHeight - itemHeight) / 2\r\n\r\n        if (offset !== middlePosition) {\r\n          $cardToc.scrollTop = scrollTop + (offset - middlePosition)\r\n        }\r\n      }\r\n\r\n      // 處理 hexo-blog-encrypt 事件\r\n      $cardToc.style.display = 'block'\r\n    }\r\n\r\n    // find head position & add active class\r\n    const $articleList = $article.querySelectorAll('h1,h2,h3,h4,h5,h6')\r\n    let detectItem = ''\r\n\r\n    // Optimization: Cache header positions\r\n    let headerList = []\r\n    const updateHeaderPositions = () => {\r\n      headerList = Array.from($articleList).map(ele => ({\r\n        ele,\r\n        top: btf.getEleTop(ele),\r\n        id: ele.id\r\n      }))\r\n    }\r\n\r\n    updateHeaderPositions()\r\n    btf.addEventListenerPjax(window, 'resize', btf.throttle(updateHeaderPositions, 200))\r\n\r\n    const findHeadPosition = top => {\r\n      if (top === 0) return false\r\n\r\n      let currentId = ''\r\n      let currentIndex = ''\r\n\r\n      for (let i = 0; i < headerList.length; i++) {\r\n        const item = headerList[i]\r\n        if (top > item.top - 80) {\r\n          currentId = item.id ? '#' + encodeURI(item.id) : ''\r\n          currentIndex = i\r\n        } else {\r\n          break\r\n        }\r\n      }\r\n\r\n      if (detectItem === currentIndex) return\r\n\r\n      if (isAnchor) btf.updateAnchor(currentId)\r\n\r\n      detectItem = currentIndex\r\n\r\n      if (isToc) {\r\n        $cardToc.querySelectorAll('.active').forEach(i => i.classList.remove('active'))\r\n\r\n        if (currentId) {\r\n          const currentActive = $tocLink[currentIndex]\r\n          currentActive.classList.add('active')\r\n\r\n          setTimeout(() => autoScrollToc(currentActive), 0)\r\n\r\n          if (!isExpand) {\r\n            let parent = currentActive.parentNode\r\n            while (!parent.matches('.toc')) {\r\n              if (parent.matches('li')) parent.classList.add('active')\r\n              parent = parent.parentNode\r\n            }\r\n          }\r\n        }\r\n      }\r\n    }\r\n\r\n    // main of scroll\r\n    const tocScrollFn = btf.throttle(() => {\r\n      const currentTop = window.scrollY || document.documentElement.scrollTop\r\n      if (isToc && GLOBAL_CONFIG.percent.toc) {\r\n        $tocPercentage.textContent = btf.getScrollPercent(currentTop, $article)\r\n      }\r\n      findHeadPosition(currentTop)\r\n    }, 100)\r\n\r\n    btf.addEventListenerPjax(window, 'scroll', tocScrollFn, { passive: true })\r\n  }\r\n\r\n  const handleThemeChange = mode => {\r\n    const globalFn = window.globalFn || {}\r\n    const themeChange = globalFn.themeChange || {}\r\n    if (!themeChange) {\r\n      return\r\n    }\r\n\r\n    Object.keys(themeChange).forEach(key => {\r\n      const themeChangeFn = themeChange[key]\r\n      if (['disqus', 'disqusjs'].includes(key)) {\r\n        setTimeout(() => themeChangeFn(mode), 300)\r\n      } else {\r\n        themeChangeFn(mode)\r\n      }\r\n    })\r\n  }\r\n\r\n  /**\r\n   * Rightside\r\n   */\r\n  const rightSideFn = {\r\n    readmode: () => { // read mode\r\n      const $body = document.body\r\n      const newEle = document.createElement('button')\r\n\r\n      const exitReadMode = () => {\r\n        $body.classList.remove('read-mode')\r\n        newEle.remove()\r\n        newEle.removeEventListener('click', exitReadMode)\r\n      }\r\n\r\n      $body.classList.add('read-mode')\r\n      newEle.type = 'button'\r\n      newEle.className = 'exit-readmode'\r\n      newEle.innerHTML = '<i class=\"fas fa-sign-out-alt\"></i>'\r\n      newEle.addEventListener('click', exitReadMode)\r\n      $body.appendChild(newEle)\r\n    },\r\n    darkmode: () => { // switch between light and dark mode\r\n      const willChangeMode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'\r\n      if (willChangeMode === 'dark') {\r\n        btf.activateDarkMode()\r\n        GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.day_to_night)\r\n      } else {\r\n        btf.activateLightMode()\r\n        GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.night_to_day)\r\n      }\r\n      btf.saveToLocal.set('theme', willChangeMode, 2)\r\n      handleThemeChange(willChangeMode)\r\n    },\r\n    'rightside-config': item => { // Show or hide rightside-hide-btn\r\n      const hideLayout = item.firstElementChild\r\n      if (hideLayout.classList.contains('show')) {\r\n        hideLayout.classList.add('status')\r\n        setTimeout(() => {\r\n          hideLayout.classList.remove('status')\r\n        }, 300)\r\n      }\r\n\r\n      hideLayout.classList.toggle('show')\r\n    },\r\n    'go-up': () => { // Back to top\r\n      btf.scrollToDest(0, 500)\r\n    },\r\n    'hide-aside-btn': () => { // Hide aside\r\n      const $htmlDom = document.documentElement.classList\r\n      const saveStatus = $htmlDom.contains('hide-aside') ? 'show' : 'hide'\r\n      btf.saveToLocal.set('aside-status', saveStatus, 2)\r\n      $htmlDom.toggle('hide-aside')\r\n    },\r\n    'mobile-toc-button': (p, item) => { // Show mobile toc\r\n      const tocEle = document.getElementById('card-toc')\r\n      tocEle.style.transition = 'transform 0.3s ease-in-out'\r\n\r\n      const tocEleHeight = tocEle.clientHeight\r\n      const btData = item.getBoundingClientRect()\r\n\r\n      const tocEleBottom = window.innerHeight - btData.bottom - 30\r\n      if (tocEleHeight > tocEleBottom) {\r\n        tocEle.style.transformOrigin = `right ${tocEleHeight - tocEleBottom - btData.height / 2}px`\r\n      }\r\n\r\n      tocEle.classList.toggle('open')\r\n      tocEle.addEventListener('transitionend', () => {\r\n        tocEle.style.cssText = ''\r\n      }, { once: true })\r\n    },\r\n    'chat-btn': () => { // Show chat\r\n      window.chatBtnFn()\r\n    },\r\n    translateLink: () => { // switch between traditional and simplified chinese\r\n      window.translateFn.translatePage()\r\n    }\r\n  }\r\n\r\n  document.getElementById('rightside').addEventListener('click', e => {\r\n    const $target = e.target.closest('[id]')\r\n    if ($target && rightSideFn[$target.id]) {\r\n      rightSideFn[$target.id](e.currentTarget, $target)\r\n    }\r\n  })\r\n\r\n  /**\r\n   * menu\r\n   * 側邊欄sub-menu 展開/收縮\r\n   */\r\n  const clickFnOfSubMenu = () => {\r\n    const handleClickOfSubMenu = e => {\r\n      const target = e.target.closest('.site-page.group')\r\n      if (!target) return\r\n      target.classList.toggle('hide')\r\n    }\r\n\r\n    const menusItems = document.querySelector('#sidebar-menus .menus_items')\r\n    menusItems && menusItems.addEventListener('click', handleClickOfSubMenu)\r\n  }\r\n\r\n  /**\r\n   * 手机端目录点击\r\n   */\r\n  const openMobileMenu = () => {\r\n    const toggleMenu = document.getElementById('toggle-menu')\r\n    if (!toggleMenu) return\r\n    btf.addEventListenerPjax(toggleMenu, 'click', () => { sidebarFn.open() })\r\n  }\r\n\r\n  /**\r\n * 複製時加上版權信息\r\n */\r\n  const addCopyright = () => {\r\n    const { limitCount, languages } = GLOBAL_CONFIG.copyright\r\n\r\n    const handleCopy = e => {\r\n      e.preventDefault()\r\n      const copyFont = window.getSelection(0).toString()\r\n      let textFont = copyFont\r\n      if (copyFont.length > limitCount) {\r\n        textFont = `${copyFont}\\n\\n\\n${languages.author}\\n${languages.link}${window.location.href}\\n${languages.source}\\n${languages.info}`\r\n      }\r\n      if (e.clipboardData) {\r\n        return e.clipboardData.setData('text', textFont)\r\n      } else {\r\n        return window.clipboardData.setData('text', textFont)\r\n      }\r\n    }\r\n\r\n    document.body.addEventListener('copy', handleCopy)\r\n  }\r\n\r\n  /**\r\n   * 網頁運行時間\r\n   */\r\n  const addRuntime = () => {\r\n    const $runtimeCount = document.getElementById('runtimeshow')\r\n    if ($runtimeCount) {\r\n      const publishDate = $runtimeCount.getAttribute('data-publishDate')\r\n      $runtimeCount.textContent = `${btf.diffDate(publishDate)} ${GLOBAL_CONFIG.runtime}`\r\n    }\r\n  }\r\n\r\n  /**\r\n   * 最後一次更新時間\r\n   */\r\n  const addLastPushDate = () => {\r\n    const $lastPushDateItem = document.getElementById('last-push-date')\r\n    if ($lastPushDateItem) {\r\n      const lastPushDate = $lastPushDateItem.getAttribute('data-lastPushDate')\r\n      $lastPushDateItem.textContent = btf.diffDate(lastPushDate, true)\r\n    }\r\n  }\r\n\r\n  /**\r\n   * table overflow\r\n   */\r\n  const addTableWrap = () => {\r\n    const $table = document.querySelectorAll('#article-container table')\r\n    if (!$table.length) return\r\n\r\n    $table.forEach(item => {\r\n      if (!item.closest('.highlight')) {\r\n        btf.wrap(item, 'div', { class: 'table-wrap' })\r\n      }\r\n    })\r\n  }\r\n\r\n  /**\r\n   * tag-hide\r\n   */\r\n  const clickFnOfTagHide = () => {\r\n    const hideButtons = document.querySelectorAll('#article-container .hide-button')\r\n    if (!hideButtons.length) return\r\n    hideButtons.forEach(item => item.addEventListener('click', e => {\r\n      const currentTarget = e.currentTarget\r\n      currentTarget.classList.add('open')\r\n      addJustifiedGallery(currentTarget.nextElementSibling.querySelectorAll('.gallery-container'))\r\n    }, { once: true }))\r\n  }\r\n\r\n  const tabsFn = () => {\r\n    const navTabsElements = document.querySelectorAll('#article-container .tabs')\r\n    if (!navTabsElements.length) return\r\n\r\n    const setActiveClass = (elements, activeIndex) => {\r\n      elements.forEach((el, index) => {\r\n        el.classList.toggle('active', index === activeIndex)\r\n      })\r\n    }\r\n\r\n    const handleNavClick = e => {\r\n      const target = e.target.closest('button')\r\n      if (!target || target.classList.contains('active')) return\r\n\r\n      const navItems = [...e.currentTarget.children]\r\n      const tabContents = [...e.currentTarget.nextElementSibling.children]\r\n      const indexOfButton = navItems.indexOf(target)\r\n      setActiveClass(navItems, indexOfButton)\r\n      e.currentTarget.classList.remove('no-default')\r\n      setActiveClass(tabContents, indexOfButton)\r\n      addJustifiedGallery(tabContents[indexOfButton].querySelectorAll('.gallery-container'), true)\r\n    }\r\n\r\n    const handleToTopClick = tabElement => e => {\r\n      if (e.target.closest('button')) {\r\n        btf.scrollToDest(btf.getEleTop(tabElement), 300)\r\n      }\r\n    }\r\n\r\n    navTabsElements.forEach(tabElement => {\r\n      btf.addEventListenerPjax(tabElement.firstElementChild, 'click', handleNavClick)\r\n      btf.addEventListenerPjax(tabElement.lastElementChild, 'click', handleToTopClick(tabElement))\r\n    })\r\n  }\r\n\r\n  const toggleCardCategory = () => {\r\n    const cardCategory = document.querySelector('#aside-cat-list.expandBtn')\r\n    if (!cardCategory) return\r\n\r\n    const handleToggleBtn = e => {\r\n      const target = e.target\r\n      if (target.nodeName === 'I') {\r\n        e.preventDefault()\r\n        target.parentNode.classList.toggle('expand')\r\n      }\r\n    }\r\n    btf.addEventListenerPjax(cardCategory, 'click', handleToggleBtn, true)\r\n  }\r\n\r\n  const addPostOutdateNotice = () => {\r\n    const ele = document.getElementById('post-outdate-notice')\r\n    if (!ele) return\r\n\r\n    const { limitDay, messagePrev, messageNext, postUpdate } = JSON.parse(ele.getAttribute('data'))\r\n    const diffDay = btf.diffDate(postUpdate)\r\n    if (diffDay >= limitDay) {\r\n      ele.textContent = `${messagePrev} ${diffDay} ${messageNext}`\r\n      ele.hidden = false\r\n    }\r\n  }\r\n\r\n  const lazyloadImg = () => {\r\n    window.lazyLoadInstance = new LazyLoad({\r\n      elements_selector: 'img',\r\n      threshold: 0,\r\n      data_src: 'lazy-src'\r\n    })\r\n\r\n    btf.addGlobalFn('pjaxComplete', () => {\r\n      window.lazyLoadInstance.update()\r\n    }, 'lazyload')\r\n  }\r\n\r\n  const relativeDate = selector => {\r\n    selector.forEach(item => {\r\n      item.textContent = btf.diffDate(item.getAttribute('datetime'), true)\r\n      item.style.display = 'inline'\r\n    })\r\n  }\r\n\r\n  const justifiedIndexPostUI = () => {\r\n    const recentPostsElement = document.getElementById('recent-posts')\r\n    if (!(recentPostsElement && recentPostsElement.classList.contains('masonry'))) return\r\n\r\n    const init = () => {\r\n      const masonryItem = new InfiniteGrid.MasonryInfiniteGrid('.recent-post-items', {\r\n        gap: { horizontal: 10, vertical: 20 },\r\n        useTransform: true,\r\n        useResizeObserver: true\r\n      })\r\n      masonryItem.renderItems()\r\n      btf.addGlobalFn('pjaxCompleteOnce', () => { masonryItem.destroy() }, 'removeJustifiedIndexPostUI')\r\n    }\r\n\r\n    typeof InfiniteGrid === 'function' ? init() : btf.getScript(`${GLOBAL_CONFIG.infinitegrid.js}`).then(init)\r\n  }\r\n\r\n  const unRefreshFn = () => {\r\n    window.addEventListener('resize', () => {\r\n      adjustMenu(false)\r\n      mobileSidebarOpen && btf.isHidden(document.getElementById('toggle-menu')) && sidebarFn.close()\r\n    })\r\n\r\n    const menuMask = document.getElementById('menu-mask')\r\n    menuMask && menuMask.addEventListener('click', () => { sidebarFn.close() })\r\n\r\n    clickFnOfSubMenu()\r\n    GLOBAL_CONFIG.islazyloadPlugin && lazyloadImg()\r\n    GLOBAL_CONFIG.copyright !== undefined && addCopyright()\r\n\r\n    if (GLOBAL_CONFIG.autoDarkmode) {\r\n      window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {\r\n        if (btf.saveToLocal.get('theme') !== undefined) return\r\n        e.matches ? handleThemeChange('dark') : handleThemeChange('light')\r\n      })\r\n    }\r\n  }\r\n\r\n  const forPostFn = () => {\r\n    addHighlightTool()\r\n    addPhotoFigcaption()\r\n    addJustifiedGallery(document.querySelectorAll('#article-container .gallery-container'))\r\n    runLightbox()\r\n    scrollFnToDo()\r\n    addTableWrap()\r\n    clickFnOfTagHide()\r\n    tabsFn()\r\n  }\r\n\r\n  const refreshFn = () => {\r\n    initAdjust()\r\n    justifiedIndexPostUI()\r\n\r\n    if (GLOBAL_CONFIG_SITE.pageType === 'post') {\r\n      addPostOutdateNotice()\r\n      GLOBAL_CONFIG.relativeDate.post && relativeDate(document.querySelectorAll('#post-meta time'))\r\n    } else {\r\n      GLOBAL_CONFIG.relativeDate.homepage && relativeDate(document.querySelectorAll('#recent-posts time'))\r\n      GLOBAL_CONFIG.runtime && addRuntime()\r\n      addLastPushDate()\r\n      toggleCardCategory()\r\n    }\r\n\r\n    GLOBAL_CONFIG_SITE.pageType === 'home' && scrollDownInIndex()\r\n    scrollFn()\r\n\r\n    forPostFn()\r\n    GLOBAL_CONFIG_SITE.pageType !== 'shuoshuo' && btf.switchComments(document)\r\n    openMobileMenu()\r\n  }\r\n\r\n  btf.addGlobalFn('pjaxComplete', refreshFn, 'refreshFn')\r\n  refreshFn()\r\n  unRefreshFn()\r\n\r\n  // 處理 hexo-blog-encrypt 事件\r\n  window.addEventListener('hexo-blog-decrypt', e => {\r\n    forPostFn()\r\n    window.translateFn.translateInitialization()\r\n    Object.values(window.globalFn.encrypt).forEach(fn => {\r\n      fn()\r\n    })\r\n  })\r\n})\r\n"
  },
  {
    "path": "source/js/search/algolia.js",
    "content": "window.addEventListener('load', () => {\r\n  const { algolia } = GLOBAL_CONFIG\r\n  const { appId, apiKey, indexName, hitsPerPage = 5, languages } = algolia\r\n\r\n  if (!appId || !apiKey || !indexName) {\r\n    return console.error('Algolia setting is invalid!')\r\n  }\r\n\r\n  const $searchMask = document.getElementById('search-mask')\r\n  const $searchDialog = document.querySelector('#algolia-search .search-dialog')\r\n\r\n  const animateElements = show => {\r\n    const action = show ? 'animateIn' : 'animateOut'\r\n    const maskAnimation = show ? 'to_show 0.5s' : 'to_hide 0.5s'\r\n    const dialogAnimation = show ? 'titleScale 0.5s' : 'search_close .5s'\r\n    btf[action]($searchMask, maskAnimation)\r\n    btf[action]($searchDialog, dialogAnimation)\r\n  }\r\n\r\n  const fixSafariHeight = () => {\r\n    if (window.innerWidth < 768) {\r\n      $searchDialog.style.setProperty('--search-height', `${window.innerHeight}px`)\r\n    }\r\n  }\r\n\r\n  const openSearch = () => {\r\n    btf.overflowPaddingR.add()\r\n    animateElements(true)\r\n    showLoading(false)\r\n\r\n    setTimeout(() => {\r\n      const searchInput = document.querySelector('#algolia-search-input .ais-SearchBox-input')\r\n      if (searchInput) searchInput.focus()\r\n    }, 100)\r\n\r\n    const handleEscape = event => {\r\n      if (event.code === 'Escape') {\r\n        closeSearch()\r\n        document.removeEventListener('keydown', handleEscape)\r\n      }\r\n    }\r\n\r\n    document.addEventListener('keydown', handleEscape)\r\n    fixSafariHeight()\r\n    window.addEventListener('resize', fixSafariHeight)\r\n  }\r\n\r\n  const closeSearch = () => {\r\n    btf.overflowPaddingR.remove()\r\n    animateElements(false)\r\n    window.removeEventListener('resize', fixSafariHeight)\r\n  }\r\n\r\n  const searchClickFn = () => {\r\n    btf.addEventListenerPjax(document.querySelector('#search-button > .search'), 'click', openSearch)\r\n  }\r\n\r\n  const searchFnOnce = () => {\r\n    $searchMask.addEventListener('click', closeSearch)\r\n    document.querySelector('#algolia-search .search-close-button').addEventListener('click', closeSearch)\r\n  }\r\n\r\n  const cutContent = content => {\r\n    if (!content) return ''\r\n\r\n    let contentStr = ''\r\n    if (typeof content === 'string') {\r\n      contentStr = content.trim()\r\n    } else if (typeof content === 'object') {\r\n      if (content.value !== undefined) {\r\n        contentStr = String(content.value).trim()\r\n        if (!contentStr) return ''\r\n      } else if (content.matchedWords || content.matchLevel || content.fullyHighlighted !== undefined) {\r\n        return ''\r\n      } else {\r\n        try {\r\n          contentStr = JSON.stringify(content).trim()\r\n          if (contentStr === '{}' || contentStr === '[]' || contentStr === '\"\"') {\r\n            return ''\r\n          }\r\n        } catch (e) {\r\n          return ''\r\n        }\r\n      }\r\n    } else if (content.toString && typeof content.toString === 'function') {\r\n      contentStr = content.toString().trim()\r\n      if (contentStr === '[object Object]' || contentStr === '[object Array]') {\r\n        return ''\r\n      }\r\n    } else {\r\n      return ''\r\n    }\r\n\r\n    const firstOccur = contentStr.indexOf('<mark>')\r\n    let start = firstOccur - 30\r\n    let end = firstOccur + 120\r\n    let pre = ''\r\n    let post = ''\r\n\r\n    if (start <= 0) {\r\n      start = 0\r\n      end = 140\r\n    } else {\r\n      pre = '...'\r\n    }\r\n\r\n    if (end > contentStr.length) {\r\n      end = contentStr.length\r\n    } else {\r\n      post = '...'\r\n    }\r\n\r\n    // Ensure we don't cut off HTML tags in the middle\r\n    let substr = contentStr.substring(start, end)\r\n\r\n    // Handle tag completeness\r\n    // Check for incomplete opening tags at the beginning\r\n    const firstCloseBracket = substr.indexOf('>')\r\n    const firstOpenBracket = substr.indexOf('<')\r\n\r\n    // If there's a closing bracket but no opening bracket before it, we've cut a tag\r\n    if (firstCloseBracket !== -1 && (firstOpenBracket === -1 || firstCloseBracket < firstOpenBracket)) {\r\n      substr = substr.substring(firstCloseBracket + 1)\r\n    }\r\n\r\n    // Check for incomplete closing tags at the end\r\n    const lastOpenBracket = substr.lastIndexOf('<')\r\n    const lastCloseBracket = substr.lastIndexOf('>')\r\n\r\n    // If there's an opening bracket after the last closing bracket, we've cut a tag\r\n    if (lastOpenBracket !== -1 && lastOpenBracket > lastCloseBracket) {\r\n      substr = substr.substring(0, lastOpenBracket)\r\n    }\r\n\r\n    // Balance tags in the substring\r\n    const tagStack = []\r\n    let balancedStr = ''\r\n    let i = 0\r\n\r\n    while (i < substr.length) {\r\n      if (substr[i] === '<') {\r\n        // Check if it's a closing tag\r\n        if (substr[i + 1] === '/') {\r\n          const closeTagEnd = substr.indexOf('>', i)\r\n          if (closeTagEnd !== -1) {\r\n            const closeTagName = substr.substring(i + 2, closeTagEnd)\r\n            // Remove matching opening tag from stack\r\n            for (let j = tagStack.length - 1; j >= 0; j--) {\r\n              if (tagStack[j] === closeTagName) {\r\n                tagStack.splice(j, 1)\r\n                break\r\n              }\r\n            }\r\n            balancedStr += substr.substring(i, closeTagEnd + 1)\r\n            i = closeTagEnd + 1\r\n            continue\r\n          }\r\n        } else if (substr.substr(i, 2) === '<!' || (substr.indexOf('/>', i) !== -1 && substr.indexOf('/>', i) < substr.indexOf('>', i))) {\r\n          const tagEnd = substr.indexOf('>', i)\r\n          if (tagEnd !== -1) {\r\n            balancedStr += substr.substring(i, tagEnd + 1)\r\n            i = tagEnd + 1\r\n            continue\r\n          }\r\n        } else {\r\n          const tagEnd = substr.indexOf('>', i)\r\n          if (tagEnd !== -1) {\r\n            const tagName = substr.substring(i + 1, (substr.indexOf(' ', i) > -1 && substr.indexOf(' ', i) < tagEnd)\r\n              ? substr.indexOf(' ', i)\r\n              : tagEnd).split(/\\s/)[0]\r\n            tagStack.push(tagName)\r\n            balancedStr += substr.substring(i, tagEnd + 1)\r\n            i = tagEnd + 1\r\n            continue\r\n          }\r\n        }\r\n      }\r\n      balancedStr += substr[i]\r\n      i++\r\n    }\r\n\r\n    // Close any unclosed tags\r\n    while (tagStack.length > 0) {\r\n      const tagName = tagStack.pop()\r\n      balancedStr += `</${tagName}>`\r\n    }\r\n\r\n    // If we removed content from the beginning, add prefix\r\n    if (start > 0 || pre) {\r\n      const actualFirstOpenBracket = contentStr.indexOf('<', start > 0 ? start - 30 : 0)\r\n      const actualFirstMark = contentStr.indexOf('<mark>', start > 0 ? start - 30 : 0)\r\n\r\n      if (actualFirstOpenBracket !== -1 &&\r\n          (actualFirstMark === -1 || actualFirstOpenBracket < actualFirstMark)) {\r\n        pre = '...'\r\n      }\r\n    }\r\n\r\n    substr = balancedStr\r\n    return `${pre}${substr}${post}`\r\n  }\r\n\r\n  // Helper function to handle Algolia highlight results\r\n  const extractHighlightValue = highlightObj => {\r\n    if (!highlightObj) return ''\r\n\r\n    if (typeof highlightObj === 'string') {\r\n      return highlightObj.trim()\r\n    }\r\n\r\n    if (typeof highlightObj === 'object' && highlightObj.value !== undefined) {\r\n      return String(highlightObj.value).trim()\r\n    }\r\n\r\n    return ''\r\n  }\r\n\r\n  // Initialize Algolia client\r\n  let searchClient\r\n\r\n  if (window['algoliasearch/lite'] && typeof window['algoliasearch/lite'].liteClient === 'function') {\r\n    searchClient = window['algoliasearch/lite'].liteClient(appId, apiKey)\r\n  } else if (typeof window.algoliasearch === 'function') {\r\n    searchClient = window.algoliasearch(appId, apiKey)\r\n  } else {\r\n    return console.error('Algolia search client not found!')\r\n  }\r\n\r\n  if (!searchClient) {\r\n    return console.error('Failed to initialize Algolia search client')\r\n  }\r\n\r\n  // Search state\r\n  let currentQuery = ''\r\n\r\n  // Show loading state\r\n  const showLoading = show => {\r\n    const loadingIndicator = document.getElementById('loading-status')\r\n    if (loadingIndicator) {\r\n      loadingIndicator.hidden = !show\r\n    }\r\n  }\r\n\r\n  // Cache frequently used elements\r\n  const elements = {\r\n    get searchInput () { return document.querySelector('#algolia-search-input .ais-SearchBox-input') },\r\n    get hits () { return document.getElementById('algolia-hits') },\r\n    get hitsEmpty () { return document.getElementById('algolia-hits-empty') },\r\n    get hitsList () { return document.querySelector('#algolia-hits .ais-Hits-list') },\r\n    get hitsWrapper () { return document.querySelector('#algolia-hits .ais-Hits') },\r\n    get pagination () { return document.getElementById('algolia-pagination') },\r\n    get paginationList () { return document.querySelector('#algolia-pagination .ais-Pagination-list') },\r\n    get stats () { return document.querySelector('#algolia-info .ais-Stats-text') },\r\n  }\r\n\r\n  // Show/hide search results area\r\n  const toggleResultsVisibility = hasResults => {\r\n    elements.pagination.style.display = hasResults ? '' : 'none'\r\n    elements.stats.style.display = hasResults ? '' : 'none'\r\n  }\r\n\r\n  // Render search results\r\n  const renderHits = (hits, query, page = 0) => {\r\n    if (hits.length === 0 && query) {\r\n      elements.hitsEmpty.textContent = languages.hits_empty.replace(/\\$\\{query}/, query)\r\n      elements.hitsEmpty.style.display = ''\r\n      elements.hitsWrapper.style.display = 'none'\r\n      elements.stats.style.display = 'none'\r\n      return\r\n    }\r\n\r\n    elements.hitsEmpty.style.display = 'none'\r\n\r\n    const hitsHTML = hits.map((hit, index) => {\r\n      const itemNumber = page * hitsPerPage + index + 1\r\n      const link = hit.permalink || (GLOBAL_CONFIG.root + hit.path)\r\n      const result = hit._highlightResult || hit\r\n\r\n      // Content extraction\r\n      let content = ''\r\n      try {\r\n        if (result.contentStripTruncate) {\r\n          content = cutContent(result.contentStripTruncate)\r\n        } else if (result.contentStrip) {\r\n          content = cutContent(result.contentStrip)\r\n        } else if (result.content) {\r\n          content = cutContent(result.content)\r\n        } else if (hit.contentStripTruncate) {\r\n          content = cutContent(hit.contentStripTruncate)\r\n        } else if (hit.contentStrip) {\r\n          content = cutContent(hit.contentStrip)\r\n        } else if (hit.content) {\r\n          content = cutContent(hit.content)\r\n        }\r\n      } catch (error) {\r\n        content = ''\r\n      }\r\n\r\n      // Title handling\r\n      let title = 'no-title'\r\n      try {\r\n        if (result.title) {\r\n          title = extractHighlightValue(result.title) || 'no-title'\r\n        } else if (hit.title) {\r\n          title = extractHighlightValue(hit.title) || 'no-title'\r\n        }\r\n\r\n        if (!title || title === 'no-title') {\r\n          if (typeof hit.title === 'string' && hit.title.trim()) {\r\n            title = hit.title.trim()\r\n          } else if (hit.title && typeof hit.title === 'object' && hit.title.value) {\r\n            title = String(hit.title.value).trim() || 'no-title'\r\n          } else {\r\n            title = 'no-title'\r\n          }\r\n        }\r\n      } catch (error) {\r\n        title = 'no-title'\r\n      }\r\n\r\n      return `\r\n        <li class=\"ais-Hits-item\" value=\"${itemNumber}\">\r\n          <a href=\"${link}\" class=\"algolia-hit-item-link\">\r\n            <span class=\"algolia-hits-item-title\">${title}</span>\r\n            ${content ? `<div class=\"algolia-hit-item-content\">${content}</div>` : ''}\r\n          </a>\r\n        </li>`\r\n    }).join('')\r\n\r\n    elements.hitsList.innerHTML = hitsHTML\r\n    elements.hitsWrapper.style.display = query ? '' : 'none'\r\n\r\n    if (hits.length > 0) {\r\n      elements.stats.style.display = ''\r\n    }\r\n  }\r\n\r\n  // Render pagination\r\n  const renderPagination = (page, nbPages) => {\r\n    if (nbPages <= 1) {\r\n      elements.pagination.style.display = 'none'\r\n      elements.paginationList.innerHTML = ''\r\n      return\r\n    }\r\n\r\n    elements.pagination.style.display = 'block'\r\n\r\n    const isFirstPage = page === 0\r\n    const isLastPage = page === nbPages - 1\r\n\r\n    // Responsive page display\r\n    const isMobile = window.innerWidth < 768\r\n    const maxVisiblePages = isMobile ? 3 : 5\r\n    let startPage = Math.max(0, page - Math.floor(maxVisiblePages / 2))\r\n    const endPage = Math.min(nbPages - 1, startPage + maxVisiblePages - 1)\r\n\r\n    // Adjust starting page to maintain max visible pages\r\n    if (endPage - startPage + 1 < maxVisiblePages) {\r\n      startPage = Math.max(0, endPage - maxVisiblePages + 1)\r\n    }\r\n\r\n    let pagesHTML = ''\r\n\r\n    // Only add ellipsis and first page when there are many pages\r\n    if (nbPages > maxVisiblePages && startPage > 0) {\r\n      pagesHTML += `\r\n        <li class=\"ais-Pagination-item ais-Pagination-item--page\">\r\n          <a class=\"ais-Pagination-link\" aria-label=\"Page 1\" href=\"#\" data-page=\"0\">1</a>\r\n        </li>`\r\n      if (startPage > 1) {\r\n        pagesHTML += `\r\n          <li class=\"ais-Pagination-item ais-Pagination-item--ellipsis\">\r\n            <span class=\"ais-Pagination-link\">...</span>\r\n          </li>`\r\n      }\r\n    }\r\n\r\n    // Add middle page numbers\r\n    for (let i = startPage; i <= endPage; i++) {\r\n      const isSelected = i === page\r\n      if (isSelected) {\r\n        pagesHTML += `\r\n          <li class=\"ais-Pagination-item ais-Pagination-item--page ais-Pagination-item--selected\">\r\n            <span class=\"ais-Pagination-link\" aria-label=\"Page ${i + 1}\">${i + 1}</span>\r\n          </li>`\r\n      } else {\r\n        pagesHTML += `\r\n          <li class=\"ais-Pagination-item ais-Pagination-item--page\">\r\n            <a class=\"ais-Pagination-link\" aria-label=\"Page ${i + 1}\" href=\"#\" data-page=\"${i}\">${i + 1}</a>\r\n          </li>`\r\n      }\r\n    }\r\n\r\n    // Only add ellipsis and last page when there are many pages\r\n    if (nbPages > maxVisiblePages && endPage < nbPages - 1) {\r\n      if (endPage < nbPages - 2) {\r\n        pagesHTML += `\r\n          <li class=\"ais-Pagination-item ais-Pagination-item--ellipsis\">\r\n            <span class=\"ais-Pagination-link\">...</span>\r\n          </li>`\r\n      }\r\n      pagesHTML += `\r\n        <li class=\"ais-Pagination-item ais-Pagination-item--page\">\r\n          <a class=\"ais-Pagination-link\" aria-label=\"Page ${nbPages}\" href=\"#\" data-page=\"${nbPages - 1}\">${nbPages}</a>\r\n        </li>`\r\n    }\r\n\r\n    if (nbPages > 1) {\r\n      elements.paginationList.innerHTML = `\r\n            <li class=\"ais-Pagination-item ais-Pagination-item--previousPage ${isFirstPage ? 'ais-Pagination-item--disabled' : ''}\">\r\n              ${isFirstPage\r\n                ? '<span class=\"ais-Pagination-link ais-Pagination-link--disabled\" aria-label=\"Previous Page\"><i class=\"fas fa-angle-left\"></i></span>'\r\n                : `<a class=\"ais-Pagination-link\" aria-label=\"Previous Page\" href=\"#\" data-page=\"${page - 1}\"><i class=\"fas fa-angle-left\"></i></a>`\r\n              }\r\n            </li>\r\n            ${pagesHTML}\r\n            <li class=\"ais-Pagination-item ais-Pagination-item--nextPage ${isLastPage ? 'ais-Pagination-item--disabled' : ''}\">\r\n              ${isLastPage\r\n                ? '<span class=\"ais-Pagination-link ais-Pagination-link--disabled\" aria-label=\"Next Page\"><i class=\"fas fa-angle-right\"></i></span>'\r\n                : `<a class=\"ais-Pagination-link\" aria-label=\"Next Page\" href=\"#\" data-page=\"${page + 1}\"><i class=\"fas fa-angle-right\"></i></a>`\r\n              }\r\n            </li>`\r\n      elements.pagination.style.display = currentQuery ? '' : 'none'\r\n    } else {\r\n      elements.pagination.style.display = 'none'\r\n    }\r\n  }\r\n\r\n  // Render statistics\r\n  const renderStats = (nbHits, processingTimeMS, query) => {\r\n    if (query) {\r\n      const stats = languages.hits_stats\r\n        .replace(/\\$\\{hits}/, nbHits)\r\n        .replace(/\\$\\{time}/, processingTimeMS)\r\n      elements.stats.innerHTML = `<hr>${stats}`\r\n      elements.stats.style.display = ''\r\n    } else {\r\n      elements.stats.style.display = 'none'\r\n    }\r\n  }\r\n\r\n  // Perform search\r\n  const performSearch = async (query, page = 0) => {\r\n    if (!query.trim()) {\r\n      currentQuery = ''\r\n      renderHits([], '', 0)\r\n      renderPagination(0, 0)\r\n      renderStats(0, 0, '')\r\n      toggleResultsVisibility(false)\r\n      return\r\n    }\r\n\r\n    showLoading(true)\r\n    currentQuery = query\r\n\r\n    try {\r\n      let result\r\n\r\n      if (searchClient && typeof searchClient.search === 'function') {\r\n        // v5 multi-index search\r\n        const searchResult = await searchClient.search([{\r\n          indexName,\r\n          query,\r\n          params: {\r\n            page,\r\n            hitsPerPage,\r\n            highlightPreTag: '<mark>',\r\n            highlightPostTag: '</mark>',\r\n            attributesToHighlight: ['title', 'content', 'contentStrip', 'contentStripTruncate']\r\n          }\r\n        }])\r\n        result = searchResult.results[0]\r\n      } else if (searchClient && typeof searchClient.initIndex === 'function') {\r\n        // v4 single-index search\r\n        const index = searchClient.initIndex(indexName)\r\n        result = await index.search(query, {\r\n          page,\r\n          hitsPerPage,\r\n          highlightPreTag: '<mark>',\r\n          highlightPostTag: '</mark>',\r\n          attributesToHighlight: ['title', 'content', 'contentStrip', 'contentStripTruncate']\r\n        })\r\n      } else {\r\n        throw new Error('Algolia: No compatible search method available')\r\n      }\r\n\r\n      renderHits(result.hits || [], query, page)\r\n\r\n      const actualNbPages = result.nbHits <= hitsPerPage ? 1 : (result.nbPages || 0)\r\n      renderPagination(page, actualNbPages)\r\n      renderStats(result.nbHits || 0, result.processingTimeMS || 0, query)\r\n\r\n      const hasResults = result.hits && result.hits.length > 0\r\n      toggleResultsVisibility(hasResults)\r\n\r\n      // Refresh Pjax links\r\n      if (window.pjax) {\r\n        window.pjax.refresh(document.getElementById('algolia-hits'))\r\n      }\r\n    } catch (error) {\r\n      console.error('Algolia search error:', error)\r\n      renderHits([], query, page)\r\n      renderPagination(0, 0)\r\n      renderStats(0, 0, query)\r\n    } finally {\r\n      showLoading(false)\r\n    }\r\n  }\r\n\r\n  // Debounced search\r\n  let searchTimeout\r\n  const debouncedSearch = (query, delay = 300) => {\r\n    clearTimeout(searchTimeout)\r\n    searchTimeout = setTimeout(() => performSearch(query), delay)\r\n  }\r\n\r\n  // Initialize search box and events\r\n  const initializeSearch = () => {\r\n    showLoading(false)\r\n\r\n    if (elements.searchInput) {\r\n      elements.searchInput.addEventListener('input', e => {\r\n        const query = e.target.value\r\n        debouncedSearch(query)\r\n      })\r\n    }\r\n\r\n    const searchForm = document.querySelector('#algolia-search-input .ais-SearchBox-form')\r\n    if (searchForm) {\r\n      searchForm.addEventListener('submit', e => {\r\n        e.preventDefault()\r\n        const query = elements.searchInput.value\r\n        performSearch(query)\r\n      })\r\n    }\r\n\r\n    // Pagination event delegation\r\n    elements.pagination.addEventListener('click', e => {\r\n      e.preventDefault()\r\n      const link = e.target.closest('a[data-page]')\r\n      if (link) {\r\n        const page = parseInt(link.dataset.page, 10)\r\n        if (!isNaN(page) && currentQuery) {\r\n          performSearch(currentQuery, page)\r\n        }\r\n      }\r\n    })\r\n\r\n    // Initial state\r\n    toggleResultsVisibility(false)\r\n  }\r\n\r\n  // Initialize\r\n  initializeSearch()\r\n  searchClickFn()\r\n  searchFnOnce()\r\n\r\n  window.addEventListener('pjax:complete', () => {\r\n    if (!btf.isHidden($searchMask)) closeSearch()\r\n    searchClickFn()\r\n  })\r\n})\r\n"
  },
  {
    "path": "source/js/search/local-search.js",
    "content": "/**\r\n * Refer to hexo-generator-searchdb\r\n * https://github.com/next-theme/hexo-generator-searchdb/blob/main/dist/search.js\r\n * Modified by hexo-theme-butterfly\r\n */\r\n\r\nclass LocalSearch {\r\n  constructor ({\r\n    path = '',\r\n    unescape = false,\r\n    top_n_per_article = 1\r\n  }) {\r\n    this.path = path\r\n    this.unescape = unescape\r\n    this.top_n_per_article = top_n_per_article\r\n    this.isfetched = false\r\n    this.datas = null\r\n  }\r\n\r\n  getIndexByWord (words, text, caseSensitive = false) {\r\n    const index = []\r\n    const included = new Set()\r\n\r\n    if (!caseSensitive) {\r\n      text = text.toLowerCase()\r\n    }\r\n    words.forEach(word => {\r\n      if (this.unescape) {\r\n        const div = document.createElement('div')\r\n        div.innerText = word\r\n        word = div.innerHTML\r\n      }\r\n      const wordLen = word.length\r\n      if (wordLen === 0) return\r\n      let startPosition = 0\r\n      let position = -1\r\n      if (!caseSensitive) {\r\n        word = word.toLowerCase()\r\n      }\r\n      while ((position = text.indexOf(word, startPosition)) > -1) {\r\n        index.push({ position, word })\r\n        included.add(word)\r\n        startPosition = position + wordLen\r\n      }\r\n    })\r\n    // Sort index by position of keyword\r\n    index.sort((left, right) => {\r\n      if (left.position !== right.position) {\r\n        return left.position - right.position\r\n      }\r\n      return right.word.length - left.word.length\r\n    })\r\n    return [index, included]\r\n  }\r\n\r\n  // Merge hits into slices\r\n  mergeIntoSlice (start, end, index) {\r\n    let item = index[0]\r\n    let { position, word } = item\r\n    const hits = []\r\n    const count = new Set()\r\n    while (position + word.length <= end && index.length !== 0) {\r\n      count.add(word)\r\n      hits.push({\r\n        position,\r\n        length: word.length\r\n      })\r\n      const wordEnd = position + word.length\r\n\r\n      // Move to next position of hit\r\n      index.shift()\r\n      while (index.length !== 0) {\r\n        item = index[0]\r\n        position = item.position\r\n        word = item.word\r\n        if (wordEnd > position) {\r\n          index.shift()\r\n        } else {\r\n          break\r\n        }\r\n      }\r\n    }\r\n    return {\r\n      hits,\r\n      start,\r\n      end,\r\n      count: count.size\r\n    }\r\n  }\r\n\r\n  // Highlight title and content\r\n  highlightKeyword (val, slice) {\r\n    let result = ''\r\n    let index = slice.start\r\n    for (const { position, length } of slice.hits) {\r\n      result += val.substring(index, position)\r\n      index = position + length\r\n      result += `<mark class=\"search-keyword\">${val.substr(position, length)}</mark>`\r\n    }\r\n    result += val.substring(index, slice.end)\r\n    return result\r\n  }\r\n\r\n  getResultItems (keywords) {\r\n    const resultItems = []\r\n    this.datas.forEach(({ title, content, url }) => {\r\n      // The number of different keywords included in the article.\r\n      const [indexOfTitle, keysOfTitle] = this.getIndexByWord(keywords, title)\r\n      const [indexOfContent, keysOfContent] = this.getIndexByWord(keywords, content)\r\n      const includedCount = new Set([...keysOfTitle, ...keysOfContent]).size\r\n\r\n      // Show search results\r\n      const hitCount = indexOfTitle.length + indexOfContent.length\r\n      if (hitCount === 0) return\r\n\r\n      const slicesOfTitle = []\r\n      if (indexOfTitle.length !== 0) {\r\n        slicesOfTitle.push(this.mergeIntoSlice(0, title.length, indexOfTitle))\r\n      }\r\n\r\n      let slicesOfContent = []\r\n      while (indexOfContent.length !== 0) {\r\n        const item = indexOfContent[0]\r\n        const { position } = item\r\n        // Cut out 120 characters. The maxlength of .search-input is 80.\r\n        const start = Math.max(0, position - 20)\r\n        const end = Math.min(content.length, position + 100)\r\n        slicesOfContent.push(this.mergeIntoSlice(start, end, indexOfContent))\r\n      }\r\n\r\n      // Sort slices in content by included keywords' count and hits' count\r\n      slicesOfContent.sort((left, right) => {\r\n        if (left.count !== right.count) {\r\n          return right.count - left.count\r\n        } else if (left.hits.length !== right.hits.length) {\r\n          return right.hits.length - left.hits.length\r\n        }\r\n        return left.start - right.start\r\n      })\r\n\r\n      // Select top N slices in content\r\n      const upperBound = parseInt(this.top_n_per_article, 10)\r\n      if (upperBound >= 0) {\r\n        slicesOfContent = slicesOfContent.slice(0, upperBound)\r\n      }\r\n\r\n      let resultItem = ''\r\n\r\n      url = new URL(url, location.origin)\r\n      url.searchParams.append('highlight', keywords.join(' '))\r\n\r\n      if (slicesOfTitle.length !== 0) {\r\n        resultItem += `<li class=\"local-search-hit-item\"><a href=\"${url.href}\"><span class=\"search-result-title\">${this.highlightKeyword(title, slicesOfTitle[0])}</span>`\r\n      } else {\r\n        resultItem += `<li class=\"local-search-hit-item\"><a href=\"${url.href}\"><span class=\"search-result-title\">${title}</span>`\r\n      }\r\n\r\n      slicesOfContent.forEach(slice => {\r\n        resultItem += `<p class=\"search-result\">${this.highlightKeyword(content, slice)}...</p>`\r\n      })\r\n\r\n      resultItem += '</a></li>'\r\n      resultItems.push({\r\n        item: resultItem,\r\n        id: resultItems.length,\r\n        hitCount,\r\n        includedCount\r\n      })\r\n    })\r\n    return resultItems\r\n  }\r\n\r\n  fetchData () {\r\n    const isXml = !this.path.endsWith('json')\r\n    fetch(this.path)\r\n      .then(response => response.text())\r\n      .then(res => {\r\n        // Get the contents from search data\r\n        this.isfetched = true\r\n        this.datas = isXml\r\n          ? [...new DOMParser().parseFromString(res, 'text/xml').querySelectorAll('entry')].map(element => ({\r\n              title: element.querySelector('title').textContent,\r\n              content: element.querySelector('content').textContent,\r\n              url: element.querySelector('url').textContent\r\n            }))\r\n          : JSON.parse(res)\r\n        // Only match articles with non-empty titles\r\n        this.datas = this.datas.filter(data => data.title).map(data => {\r\n          data.title = data.title.trim()\r\n          data.content = data.content ? data.content.trim().replace(/<[^>]+>/g, '') : ''\r\n          data.url = decodeURIComponent(data.url).replace(/\\/{2,}/g, '/')\r\n          return data\r\n        })\r\n        // Remove loading animation\r\n        window.dispatchEvent(new Event('search:loaded'))\r\n      })\r\n  }\r\n\r\n  // Highlight by wrapping node in mark elements with the given class name\r\n  highlightText (node, slice, className) {\r\n    const val = node.nodeValue\r\n    let index = slice.start\r\n    const children = []\r\n    for (const { position, length } of slice.hits) {\r\n      const text = document.createTextNode(val.substring(index, position))\r\n      index = position + length\r\n      const mark = document.createElement('mark')\r\n      mark.className = className\r\n      mark.appendChild(document.createTextNode(val.substr(position, length)))\r\n      children.push(text, mark)\r\n    }\r\n    node.nodeValue = val.substring(index, slice.end)\r\n    children.forEach(element => {\r\n      node.parentNode.insertBefore(element, node)\r\n    })\r\n  }\r\n\r\n  // Highlight the search words provided in the url in the text\r\n  highlightSearchWords (body) {\r\n    const params = new URL(location.href).searchParams.get('highlight')\r\n    const keywords = params ? params.split(' ') : []\r\n    if (!keywords.length || !body) return\r\n    const walk = document.createTreeWalker(body, NodeFilter.SHOW_TEXT, null)\r\n    const allNodes = []\r\n    while (walk.nextNode()) {\r\n      if (!walk.currentNode.parentNode.matches('button, select, textarea, .mermaid')) allNodes.push(walk.currentNode)\r\n    }\r\n    allNodes.forEach(node => {\r\n      const [indexOfNode] = this.getIndexByWord(keywords, node.nodeValue)\r\n      if (!indexOfNode.length) return\r\n      const slice = this.mergeIntoSlice(0, node.nodeValue.length, indexOfNode)\r\n      this.highlightText(node, slice, 'search-keyword')\r\n    })\r\n  }\r\n}\r\n\r\nwindow.addEventListener('load', () => {\r\n// Search\r\n  const { path, top_n_per_article, unescape, languages, pagination } = GLOBAL_CONFIG.localSearch\r\n  const enablePagination = pagination && pagination.enable\r\n  const localSearch = new LocalSearch({\r\n    path,\r\n    top_n_per_article,\r\n    unescape\r\n  })\r\n\r\n  const input = document.querySelector('.local-search-input input')\r\n  const statsItem = document.getElementById('local-search-stats')\r\n  const $loadingStatus = document.getElementById('loading-status')\r\n  const isXml = !path.endsWith('json')\r\n\r\n  // Pagination variables (only initialize if pagination is enabled)\r\n  let currentPage = 0\r\n  const hitsPerPage = pagination.hitsPerPage || 10\r\n\r\n  let currentResultItems = []\r\n\r\n  if (!enablePagination) {\r\n    // If pagination is disabled, we don't need these variables\r\n    currentPage = undefined\r\n    currentResultItems = undefined\r\n  }\r\n\r\n  // Cache frequently used elements\r\n  const elements = {\r\n    get pagination () { return document.getElementById('local-search-pagination') },\r\n    get paginationList () { return document.querySelector('#local-search-pagination .ais-Pagination-list') }\r\n  }\r\n\r\n  // Show/hide search results area\r\n  const toggleResultsVisibility = hasResults => {\r\n    if (enablePagination) {\r\n      elements.pagination.style.display = hasResults ? '' : 'none'\r\n    } else {\r\n      elements.pagination.style.display = 'none'\r\n    }\r\n  }\r\n\r\n  // Render search results for current page\r\n  const renderResults = (searchText, resultItems) => {\r\n    const container = document.getElementById('local-search-results')\r\n\r\n    // Determine items to display based on pagination mode\r\n    const itemsToDisplay = enablePagination\r\n      ? currentResultItems.slice(currentPage * hitsPerPage, (currentPage + 1) * hitsPerPage)\r\n      : resultItems\r\n\r\n    // Handle empty page in pagination mode\r\n    if (enablePagination && itemsToDisplay.length === 0 && currentResultItems.length > 0) {\r\n      currentPage = 0\r\n      renderResults(searchText, resultItems)\r\n      return\r\n    }\r\n\r\n    // Add numbering to items\r\n    const numberedItems = itemsToDisplay.map((result, index) => {\r\n      const itemNumber = enablePagination\r\n        ? currentPage * hitsPerPage + index + 1\r\n        : index + 1\r\n      return result.item.replace(\r\n        '<li class=\"local-search-hit-item\">',\r\n        `<li class=\"local-search-hit-item\" value=\"${itemNumber}\">`\r\n      )\r\n    })\r\n\r\n    container.innerHTML = `<ol class=\"search-result-list\">${numberedItems.join('')}</ol>`\r\n\r\n    // Update stats\r\n    const displayCount = enablePagination ? currentResultItems.length : resultItems.length\r\n    const stats = languages.hits_stats.replace(/\\$\\{hits}/, displayCount)\r\n    statsItem.innerHTML = `<hr><div class=\"search-result-stats\">${stats}</div>`\r\n\r\n    // Handle pagination\r\n    if (enablePagination) {\r\n      const nbPages = Math.ceil(currentResultItems.length / hitsPerPage)\r\n      renderPagination(currentPage, nbPages, searchText)\r\n    }\r\n\r\n    const hasResults = resultItems.length > 0\r\n    toggleResultsVisibility(hasResults)\r\n\r\n    window.pjax && window.pjax.refresh(container)\r\n  }\r\n\r\n  // Render pagination\r\n  const renderPagination = (page, nbPages, query) => {\r\n    if (nbPages <= 1) {\r\n      elements.pagination.style.display = 'none'\r\n      elements.paginationList.innerHTML = ''\r\n      return\r\n    }\r\n\r\n    elements.pagination.style.display = 'block'\r\n\r\n    const isFirstPage = page === 0\r\n    const isLastPage = page === nbPages - 1\r\n\r\n    // Responsive page display\r\n    const isMobile = window.innerWidth < 768\r\n    const maxVisiblePages = isMobile ? 3 : 5\r\n    let startPage = Math.max(0, page - Math.floor(maxVisiblePages / 2))\r\n    const endPage = Math.min(nbPages - 1, startPage + maxVisiblePages - 1)\r\n\r\n    // Adjust starting page to maintain max visible pages\r\n    if (endPage - startPage + 1 < maxVisiblePages) {\r\n      startPage = Math.max(0, endPage - maxVisiblePages + 1)\r\n    }\r\n\r\n    let pagesHTML = ''\r\n\r\n    // Only add ellipsis and first page when there are many pages\r\n    if (nbPages > maxVisiblePages && startPage > 0) {\r\n      pagesHTML += `\r\n        <li class=\"ais-Pagination-item ais-Pagination-item--page\">\r\n          <a class=\"ais-Pagination-link\" aria-label=\"Page 1\" href=\"#\" data-page=\"0\">1</a>\r\n        </li>`\r\n      if (startPage > 1) {\r\n        pagesHTML += `\r\n          <li class=\"ais-Pagination-item ais-Pagination-item--ellipsis\">\r\n            <span class=\"ais-Pagination-link\">...</span>\r\n          </li>`\r\n      }\r\n    }\r\n\r\n    // Add middle page numbers\r\n    for (let i = startPage; i <= endPage; i++) {\r\n      const isSelected = i === page\r\n      if (isSelected) {\r\n        pagesHTML += `\r\n          <li class=\"ais-Pagination-item ais-Pagination-item--page ais-Pagination-item--selected\">\r\n            <span class=\"ais-Pagination-link\" aria-label=\"Page ${i + 1}\">${i + 1}</span>\r\n          </li>`\r\n      } else {\r\n        pagesHTML += `\r\n          <li class=\"ais-Pagination-item ais-Pagination-item--page\">\r\n            <a class=\"ais-Pagination-link\" aria-label=\"Page ${i + 1}\" href=\"#\" data-page=\"${i}\">${i + 1}</a>\r\n          </li>`\r\n      }\r\n    }\r\n\r\n    // Only add ellipsis and last page when there are many pages\r\n    if (nbPages > maxVisiblePages && endPage < nbPages - 1) {\r\n      if (endPage < nbPages - 2) {\r\n        pagesHTML += `\r\n          <li class=\"ais-Pagination-item ais-Pagination-item--ellipsis\">\r\n            <span class=\"ais-Pagination-link\">...</span>\r\n          </li>`\r\n      }\r\n      pagesHTML += `\r\n        <li class=\"ais-Pagination-item ais-Pagination-item--page\">\r\n          <a class=\"ais-Pagination-link\" aria-label=\"Page ${nbPages}\" href=\"#\" data-page=\"${nbPages - 1}\">${nbPages}</a>\r\n        </li>`\r\n    }\r\n\r\n    if (nbPages > 1) {\r\n      elements.paginationList.innerHTML = `\r\n            <li class=\"ais-Pagination-item ais-Pagination-item--previousPage ${isFirstPage ? 'ais-Pagination-item--disabled' : ''}\">\r\n              ${isFirstPage\r\n                ? '<span class=\"ais-Pagination-link ais-Pagination-link--disabled\" aria-label=\"Previous Page\"><i class=\"fas fa-angle-left\"></i></span>'\r\n                : `<a class=\"ais-Pagination-link\" aria-label=\"Previous Page\" href=\"#\" data-page=\"${page - 1}\"><i class=\"fas fa-angle-left\"></i></a>`\r\n              }\r\n            </li>\r\n            ${pagesHTML}\r\n            <li class=\"ais-Pagination-item ais-Pagination-item--nextPage ${isLastPage ? 'ais-Pagination-item--disabled' : ''}\">\r\n              ${isLastPage\r\n                ? '<span class=\"ais-Pagination-link ais-Pagination-link--disabled\" aria-label=\"Next Page\"><i class=\"fas fa-angle-right\"></i></span>'\r\n                : `<a class=\"ais-Pagination-link\" aria-label=\"Next Page\" href=\"#\" data-page=\"${page + 1}\"><i class=\"fas fa-angle-right\"></i></a>`\r\n              }\r\n            </li>`\r\n    } else {\r\n      elements.pagination.style.display = 'none'\r\n    }\r\n  }\r\n\r\n  // Clear search results and stats\r\n  const clearSearchResults = () => {\r\n    const container = document.getElementById('local-search-results')\r\n    container.textContent = ''\r\n    statsItem.textContent = ''\r\n    toggleResultsVisibility(false)\r\n    if (enablePagination) {\r\n      currentResultItems = []\r\n      currentPage = 0\r\n    }\r\n  }\r\n\r\n  // Show no results message\r\n  const showNoResults = searchText => {\r\n    const container = document.getElementById('local-search-results')\r\n    container.textContent = ''\r\n    const statsDiv = document.createElement('div')\r\n    statsDiv.className = 'search-result-stats'\r\n    statsDiv.textContent = languages.hits_empty.replace(/\\$\\{query}/, searchText)\r\n    statsItem.innerHTML = statsDiv.outerHTML\r\n    toggleResultsVisibility(false)\r\n    if (enablePagination) {\r\n      currentResultItems = []\r\n      currentPage = 0\r\n    }\r\n  }\r\n\r\n  const inputEventFunction = () => {\r\n    if (!localSearch.isfetched) return\r\n    let searchText = input.value.trim().toLowerCase()\r\n    isXml && (searchText = searchText.replace(/</g, '&lt;').replace(/>/g, '&gt;'))\r\n\r\n    if (searchText !== '') $loadingStatus.hidden = false\r\n\r\n    const keywords = searchText.split(/[-\\s]+/)\r\n    let resultItems = []\r\n\r\n    if (searchText.length > 0) {\r\n      resultItems = localSearch.getResultItems(keywords)\r\n    }\r\n\r\n    if (keywords.length === 1 && keywords[0] === '') {\r\n      clearSearchResults()\r\n    } else if (resultItems.length === 0) {\r\n      showNoResults(searchText)\r\n    } else {\r\n      // Sort results by relevance\r\n      resultItems.sort((left, right) => {\r\n        if (left.includedCount !== right.includedCount) {\r\n          return right.includedCount - left.includedCount\r\n        } else if (left.hitCount !== right.hitCount) {\r\n          return right.hitCount - left.hitCount\r\n        }\r\n        return right.id - left.id\r\n      })\r\n\r\n      if (enablePagination) {\r\n        currentResultItems = resultItems\r\n        currentPage = 0\r\n      }\r\n      renderResults(searchText, resultItems)\r\n    }\r\n\r\n    $loadingStatus.hidden = true\r\n  }\r\n\r\n  let loadFlag = false\r\n  const $searchMask = document.getElementById('search-mask')\r\n  const $searchDialog = document.querySelector('#local-search .search-dialog')\r\n\r\n  // fix safari\r\n  const fixSafariHeight = () => {\r\n    if (window.innerWidth < 768) {\r\n      $searchDialog.style.setProperty('--search-height', window.innerHeight + 'px')\r\n    }\r\n  }\r\n\r\n  const openSearch = () => {\r\n    btf.overflowPaddingR.add()\r\n    btf.animateIn($searchMask, 'to_show 0.5s')\r\n    btf.animateIn($searchDialog, 'titleScale 0.5s')\r\n    setTimeout(() => { input.focus() }, 300)\r\n    if (!loadFlag) {\r\n      !localSearch.isfetched && localSearch.fetchData()\r\n      input.addEventListener('input', inputEventFunction)\r\n      loadFlag = true\r\n    }\r\n    // shortcut: ESC\r\n    document.addEventListener('keydown', function f (event) {\r\n      if (event.code === 'Escape') {\r\n        closeSearch()\r\n        document.removeEventListener('keydown', f)\r\n      }\r\n    })\r\n\r\n    fixSafariHeight()\r\n    window.addEventListener('resize', fixSafariHeight)\r\n  }\r\n\r\n  const closeSearch = () => {\r\n    btf.overflowPaddingR.remove()\r\n    btf.animateOut($searchDialog, 'search_close .5s')\r\n    btf.animateOut($searchMask, 'to_hide 0.5s')\r\n    window.removeEventListener('resize', fixSafariHeight)\r\n  }\r\n\r\n  const searchClickFn = () => {\r\n    btf.addEventListenerPjax(document.querySelector('#search-button > .search'), 'click', openSearch)\r\n  }\r\n\r\n  const searchFnOnce = () => {\r\n    document.querySelector('#local-search .search-close-button').addEventListener('click', closeSearch)\r\n    $searchMask.addEventListener('click', closeSearch)\r\n    if (GLOBAL_CONFIG.localSearch.preload) {\r\n      localSearch.fetchData()\r\n    }\r\n    localSearch.highlightSearchWords(document.getElementById('article-container'))\r\n\r\n    // Pagination event delegation - only add if pagination is enabled\r\n    if (enablePagination) {\r\n      elements.pagination.addEventListener('click', e => {\r\n        e.preventDefault()\r\n        const link = e.target.closest('a[data-page]')\r\n        if (link) {\r\n          const page = parseInt(link.dataset.page, 10)\r\n          if (!isNaN(page) && currentResultItems.length > 0) {\r\n            currentPage = page\r\n            renderResults(input.value.trim().toLowerCase(), currentResultItems)\r\n          }\r\n        }\r\n      })\r\n    }\r\n\r\n    // Initial state\r\n    toggleResultsVisibility(false)\r\n  }\r\n\r\n  window.addEventListener('search:loaded', () => {\r\n    const $loadDataItem = document.getElementById('loading-database')\r\n    $loadDataItem.nextElementSibling.style.visibility = 'visible'\r\n    $loadDataItem.remove()\r\n  })\r\n\r\n  searchClickFn()\r\n  searchFnOnce()\r\n\r\n  // pjax\r\n  window.addEventListener('pjax:complete', () => {\r\n    !btf.isHidden($searchMask) && closeSearch()\r\n    localSearch.highlightSearchWords(document.getElementById('article-container'))\r\n    searchClickFn()\r\n  })\r\n})\r\n"
  },
  {
    "path": "source/js/tw_cn.js",
    "content": "document.addEventListener('DOMContentLoaded', () => {\r\n  const { defaultEncoding, translateDelay, msgToTraditionalChinese, msgToSimplifiedChinese } = GLOBAL_CONFIG.translate\r\n  const snackbarData = GLOBAL_CONFIG.Snackbar\r\n  const targetEncodingCookie = 'translate-chn-cht'\r\n\r\n  let currentEncoding = defaultEncoding\r\n  let targetEncoding = Number(btf.saveToLocal.get(targetEncodingCookie)) || defaultEncoding\r\n  const translateButtonObject = document.getElementById('translateLink')\r\n  const isSnackbar = snackbarData !== undefined\r\n\r\n  const setLang = () => {\r\n    document.documentElement.lang = targetEncoding === 1 ? 'zh-TW' : 'zh-CN'\r\n  }\r\n\r\n  const translateText = txt => {\r\n    if (!txt) return ''\r\n    if (currentEncoding === 1 && targetEncoding === 2) return Simplized(txt)\r\n    if (currentEncoding === 2 && targetEncoding === 1) return Traditionalized(txt)\r\n    return txt\r\n  }\r\n\r\n  const translateBody = fobj => {\r\n    const nodes = typeof fobj === 'object' ? fobj.childNodes : document.body.childNodes\r\n\r\n    for (const node of nodes) {\r\n      // Skip BR, HR tags, or the translate button object\r\n      if (['BR', 'HR'].includes(node.tagName) || node === translateButtonObject) continue\r\n\r\n      if (node.nodeType === Node.ELEMENT_NODE) {\r\n        const { tagName, title, alt, placeholder, value, type } = node\r\n\r\n        // Translate title, alt, placeholder\r\n        if (title) node.title = translateText(title)\r\n        if (alt) node.alt = translateText(alt)\r\n        if (placeholder) node.placeholder = translateText(placeholder)\r\n\r\n        // Translate input value except text and hidden types\r\n        if (tagName === 'INPUT' && value && type !== 'text' && type !== 'hidden') {\r\n          node.value = translateText(value)\r\n        }\r\n\r\n        // Recursively translate child nodes\r\n        translateBody(node)\r\n      } else if (node.nodeType === Node.TEXT_NODE) {\r\n        // Translate text node data\r\n        node.data = translateText(node.data)\r\n      }\r\n    }\r\n  }\r\n\r\n  const translatePage = () => {\r\n    if (targetEncoding === 1) {\r\n      currentEncoding = 1\r\n      targetEncoding = 2\r\n      translateButtonObject.textContent = msgToTraditionalChinese\r\n      isSnackbar && btf.snackbarShow(snackbarData.cht_to_chs)\r\n    } else if (targetEncoding === 2) {\r\n      currentEncoding = 2\r\n      targetEncoding = 1\r\n      translateButtonObject.textContent = msgToSimplifiedChinese\r\n      isSnackbar && btf.snackbarShow(snackbarData.chs_to_cht)\r\n    }\r\n    btf.saveToLocal.set(targetEncodingCookie, targetEncoding, 2)\r\n    setLang()\r\n    translateBody()\r\n  }\r\n\r\n  const JTPYStr = () => '万与丑专业丛东丝丢两严丧个丬丰临为丽举么义乌乐乔习乡书买乱争于亏云亘亚产亩亲亵亸亿仅从仑仓仪们价众优伙会伛伞伟传伤伥伦伧伪伫体余佣佥侠侣侥侦侧侨侩侪侬俣俦俨俩俪俭债倾偬偻偾偿傥傧储傩儿兑兖党兰关兴兹养兽冁内冈册写军农冢冯冲决况冻净凄凉凌减凑凛几凤凫凭凯击凼凿刍划刘则刚创删别刬刭刽刿剀剂剐剑剥剧劝办务劢动励劲劳势勋勐勚匀匦匮区医华协单卖卢卤卧卫却卺厂厅历厉压厌厍厕厢厣厦厨厩厮县参叆叇双发变叙叠叶号叹叽吁后吓吕吗吣吨听启吴呒呓呕呖呗员呙呛呜咏咔咙咛咝咤咴咸哌响哑哒哓哔哕哗哙哜哝哟唛唝唠唡唢唣唤唿啧啬啭啮啰啴啸喷喽喾嗫呵嗳嘘嘤嘱噜噼嚣嚯团园囱围囵国图圆圣圹场坂坏块坚坛坜坝坞坟坠垄垅垆垒垦垧垩垫垭垯垱垲垴埘埙埚埝埯堑堕塆墙壮声壳壶壸处备复够头夸夹夺奁奂奋奖奥妆妇妈妩妪妫姗姜娄娅娆娇娈娱娲娴婳婴婵婶媪嫒嫔嫱嬷孙学孪宁宝实宠审宪宫宽宾寝对寻导寿将尔尘尧尴尸尽层屃屉届属屡屦屿岁岂岖岗岘岙岚岛岭岳岽岿峃峄峡峣峤峥峦崂崃崄崭嵘嵚嵛嵝嵴巅巩巯币帅师帏帐帘帜带帧帮帱帻帼幂幞干并广庄庆庐庑库应庙庞废庼廪开异弃张弥弪弯弹强归当录彟彦彻径徕御忆忏忧忾怀态怂怃怄怅怆怜总怼怿恋恳恶恸恹恺恻恼恽悦悫悬悭悯惊惧惨惩惫惬惭惮惯愍愠愤愦愿慑慭憷懑懒懔戆戋戏戗战戬户扎扑扦执扩扪扫扬扰抚抛抟抠抡抢护报担拟拢拣拥拦拧拨择挂挚挛挜挝挞挟挠挡挢挣挤挥挦捞损捡换捣据捻掳掴掷掸掺掼揸揽揿搀搁搂搅携摄摅摆摇摈摊撄撑撵撷撸撺擞攒敌敛数斋斓斗斩断无旧时旷旸昙昼昽显晋晒晓晔晕晖暂暧札术朴机杀杂权条来杨杩杰极构枞枢枣枥枧枨枪枫枭柜柠柽栀栅标栈栉栊栋栌栎栏树栖样栾桊桠桡桢档桤桥桦桧桨桩梦梼梾检棂椁椟椠椤椭楼榄榇榈榉槚槛槟槠横樯樱橥橱橹橼檐檩欢欤欧歼殁殇残殒殓殚殡殴毁毂毕毙毡毵氇气氢氩氲汇汉污汤汹沓沟没沣沤沥沦沧沨沩沪沵泞泪泶泷泸泺泻泼泽泾洁洒洼浃浅浆浇浈浉浊测浍济浏浐浑浒浓浔浕涂涌涛涝涞涟涠涡涢涣涤润涧涨涩淀渊渌渍渎渐渑渔渖渗温游湾湿溃溅溆溇滗滚滞滟滠满滢滤滥滦滨滩滪漤潆潇潋潍潜潴澜濑濒灏灭灯灵灾灿炀炉炖炜炝点炼炽烁烂烃烛烟烦烧烨烩烫烬热焕焖焘煅煳熘爱爷牍牦牵牺犊犟状犷犸犹狈狍狝狞独狭狮狯狰狱狲猃猎猕猡猪猫猬献獭玑玙玚玛玮环现玱玺珉珏珐珑珰珲琎琏琐琼瑶瑷璇璎瓒瓮瓯电画畅畲畴疖疗疟疠疡疬疮疯疱疴痈痉痒痖痨痪痫痴瘅瘆瘗瘘瘪瘫瘾瘿癞癣癫癯皑皱皲盏盐监盖盗盘眍眦眬着睁睐睑瞒瞩矫矶矾矿砀码砖砗砚砜砺砻砾础硁硅硕硖硗硙硚确硷碍碛碜碱碹磙礼祎祢祯祷祸禀禄禅离秃秆种积称秽秾稆税稣稳穑穷窃窍窑窜窝窥窦窭竖竞笃笋笔笕笺笼笾筑筚筛筜筝筹签简箓箦箧箨箩箪箫篑篓篮篱簖籁籴类籼粜粝粤粪粮糁糇紧絷纟纠纡红纣纤纥约级纨纩纪纫纬纭纮纯纰纱纲纳纴纵纶纷纸纹纺纻纼纽纾线绀绁绂练组绅细织终绉绊绋绌绍绎经绐绑绒结绔绕绖绗绘给绚绛络绝绞统绠绡绢绣绤绥绦继绨绩绪绫绬续绮绯绰绱绲绳维绵绶绷绸绹绺绻综绽绾绿缀缁缂缃缄缅缆缇缈缉缊缋缌缍缎缏缐缑缒缓缔缕编缗缘缙缚缛缜缝缞缟缠缡缢缣缤缥缦缧缨缩缪缫缬缭缮缯缰缱缲缳缴缵罂网罗罚罢罴羁羟羡翘翙翚耢耧耸耻聂聋职聍联聩聪肃肠肤肷肾肿胀胁胆胜胧胨胪胫胶脉脍脏脐脑脓脔脚脱脶脸腊腌腘腭腻腼腽腾膑臜舆舣舰舱舻艰艳艹艺节芈芗芜芦苁苇苈苋苌苍苎苏苘苹茎茏茑茔茕茧荆荐荙荚荛荜荞荟荠荡荣荤荥荦荧荨荩荪荫荬荭荮药莅莜莱莲莳莴莶获莸莹莺莼萚萝萤营萦萧萨葱蒇蒉蒋蒌蓝蓟蓠蓣蓥蓦蔷蔹蔺蔼蕲蕴薮藁藓虏虑虚虫虬虮虽虾虿蚀蚁蚂蚕蚝蚬蛊蛎蛏蛮蛰蛱蛲蛳蛴蜕蜗蜡蝇蝈蝉蝎蝼蝾螀螨蟏衅衔补衬衮袄袅袆袜袭袯装裆裈裢裣裤裥褛褴襁襕见观觃规觅视觇览觉觊觋觌觍觎觏觐觑觞触觯詟誉誊讠计订讣认讥讦讧讨让讪讫训议讯记讱讲讳讴讵讶讷许讹论讻讼讽设访诀证诂诃评诅识诇诈诉诊诋诌词诎诏诐译诒诓诔试诖诗诘诙诚诛诜话诞诟诠诡询诣诤该详诧诨诩诪诫诬语诮误诰诱诲诳说诵诶请诸诹诺读诼诽课诿谀谁谂调谄谅谆谇谈谊谋谌谍谎谏谐谑谒谓谔谕谖谗谘谙谚谛谜谝谞谟谠谡谢谣谤谥谦谧谨谩谪谫谬谭谮谯谰谱谲谳谴谵谶谷豮贝贞负贠贡财责贤败账货质贩贪贫贬购贮贯贰贱贲贳贴贵贶贷贸费贺贻贼贽贾贿赀赁赂赃资赅赆赇赈赉赊赋赌赍赎赏赐赑赒赓赔赕赖赗赘赙赚赛赜赝赞赟赠赡赢赣赪赵赶趋趱趸跃跄跖跞践跶跷跸跹跻踊踌踪踬踯蹑蹒蹰蹿躏躜躯车轧轨轩轪轫转轭轮软轰轱轲轳轴轵轶轷轸轹轺轻轼载轾轿辀辁辂较辄辅辆辇辈辉辊辋辌辍辎辏辐辑辒输辔辕辖辗辘辙辚辞辩辫边辽达迁过迈运还这进远违连迟迩迳迹适选逊递逦逻遗遥邓邝邬邮邹邺邻郁郄郏郐郑郓郦郧郸酝酦酱酽酾酿释里鉅鉴銮錾钆钇针钉钊钋钌钍钎钏钐钑钒钓钔钕钖钗钘钙钚钛钝钞钟钠钡钢钣钤钥钦钧钨钩钪钫钬钭钮钯钰钱钲钳钴钵钶钷钸钹钺钻钼钽钾钿铀铁铂铃铄铅铆铈铉铊铋铍铎铏铐铑铒铕铗铘铙铚铛铜铝铞铟铠铡铢铣铤铥铦铧铨铪铫铬铭铮铯铰铱铲铳铴铵银铷铸铹铺铻铼铽链铿销锁锂锃锄锅锆锇锈锉锊锋锌锍锎锏锐锑锒锓锔锕锖锗错锚锜锞锟锠锡锢锣锤锥锦锨锩锫锬锭键锯锰锱锲锳锴锵锶锷锸锹锺锻锼锽锾锿镀镁镂镃镆镇镈镉镊镌镍镎镏镐镑镒镕镖镗镙镚镛镜镝镞镟镠镡镢镣镤镥镦镧镨镩镪镫镬镭镮镯镰镱镲镳镴镶长门闩闪闫闬闭问闯闰闱闲闳间闵闶闷闸闹闺闻闼闽闾闿阀阁阂阃阄阅阆阇阈阉阊阋阌阍阎阏阐阑阒阓阔阕阖阗阘阙阚阛队阳阴阵阶际陆陇陈陉陕陧陨险随隐隶隽难雏雠雳雾霁霉霭靓静靥鞑鞒鞯鞴韦韧韨韩韪韫韬韵页顶顷顸项顺须顼顽顾顿颀颁颂颃预颅领颇颈颉颊颋颌颍颎颏颐频颒颓颔颕颖颗题颙颚颛颜额颞颟颠颡颢颣颤颥颦颧风飏飐飑飒飓飔飕飖飗飘飙飚飞飨餍饤饥饦饧饨饩饪饫饬饭饮饯饰饱饲饳饴饵饶饷饸饹饺饻饼饽饾饿馀馁馂馃馄馅馆馇馈馉馊馋馌馍馎馏馐馑馒馓馔馕马驭驮驯驰驱驲驳驴驵驶驷驸驹驺驻驼驽驾驿骀骁骂骃骄骅骆骇骈骉骊骋验骍骎骏骐骑骒骓骔骕骖骗骘骙骚骛骜骝骞骟骠骡骢骣骤骥骦骧髅髋髌鬓魇魉鱼鱽鱾鱿鲀鲁鲂鲄鲅鲆鲇鲈鲉鲊鲋鲌鲍鲎鲏鲐鲑鲒鲓鲔鲕鲖鲗鲘鲙鲚鲛鲜鲝鲞鲟鲠鲡鲢鲣鲤鲥鲦鲧鲨鲩鲪鲫鲬鲭鲮鲯鲰鲱鲲鲳鲴鲵鲶鲷鲸鲹鲺鲻鲼鲽鲾鲿鳀鳁鳂鳃鳄鳅鳆鳇鳈鳉鳊鳋鳌鳍鳎鳏鳐鳑鳒鳓鳔鳕鳖鳗鳘鳙鳛鳜鳝鳞鳟鳠鳡鳢鳣鸟鸠鸡鸢鸣鸤鸥鸦鸧鸨鸩鸪鸫鸬鸭鸮鸯鸰鸱鸲鸳鸴鸵鸶鸷鸸鸹鸺鸻鸼鸽鸾鸿鹀鹁鹂鹃鹄鹅鹆鹇鹈鹉鹊鹋鹌鹍鹎鹏鹐鹑鹒鹓鹔鹕鹖鹗鹘鹚鹛鹜鹝鹞鹟鹠鹡鹢鹣鹤鹥鹦鹧鹨鹩鹪鹫鹬鹭鹯鹰鹱鹲鹳鹴鹾麦麸黄黉黡黩黪黾龙历志制一台皋准复猛钟注范签'\r\n  const FTPYStr = () => '萬與醜專業叢東絲丟兩嚴喪個爿豐臨為麗舉麼義烏樂喬習鄉書買亂爭於虧雲亙亞產畝親褻嚲億僅從侖倉儀們價眾優夥會傴傘偉傳傷倀倫傖偽佇體餘傭僉俠侶僥偵側僑儈儕儂俁儔儼倆儷儉債傾傯僂僨償儻儐儲儺兒兌兗黨蘭關興茲養獸囅內岡冊寫軍農塚馮衝決況凍淨淒涼淩減湊凜幾鳳鳧憑凱擊氹鑿芻劃劉則剛創刪別剗剄劊劌剴劑剮劍剝劇勸辦務勱動勵勁勞勢勳猛勩勻匭匱區醫華協單賣盧鹵臥衛卻巹廠廳曆厲壓厭厙廁廂厴廈廚廄廝縣參靉靆雙發變敘疊葉號歎嘰籲後嚇呂嗎唚噸聽啟吳嘸囈嘔嚦唄員咼嗆嗚詠哢嚨嚀噝吒噅鹹呱響啞噠嘵嗶噦嘩噲嚌噥喲嘜嗊嘮啢嗩唕喚呼嘖嗇囀齧囉嘽嘯噴嘍嚳囁嗬噯噓嚶囑嚕劈囂謔團園囪圍圇國圖圓聖壙場阪壞塊堅壇壢壩塢墳墜壟壟壚壘墾坰堊墊埡墶壋塏堖塒塤堝墊垵塹墮壪牆壯聲殼壺壼處備複夠頭誇夾奪奩奐奮獎奧妝婦媽嫵嫗媯姍薑婁婭嬈嬌孌娛媧嫻嫿嬰嬋嬸媼嬡嬪嬙嬤孫學孿寧寶實寵審憲宮寬賓寢對尋導壽將爾塵堯尷屍盡層屭屜屆屬屢屨嶼歲豈嶇崗峴嶴嵐島嶺嶽崠巋嶨嶧峽嶢嶠崢巒嶗崍嶮嶄嶸嶔崳嶁脊巔鞏巰幣帥師幃帳簾幟帶幀幫幬幘幗冪襆幹並廣莊慶廬廡庫應廟龐廢廎廩開異棄張彌弳彎彈強歸當錄彠彥徹徑徠禦憶懺憂愾懷態慫憮慪悵愴憐總懟懌戀懇惡慟懨愷惻惱惲悅愨懸慳憫驚懼慘懲憊愜慚憚慣湣慍憤憒願懾憖怵懣懶懍戇戔戲戧戰戩戶紮撲扡執擴捫掃揚擾撫拋摶摳掄搶護報擔擬攏揀擁攔擰撥擇掛摯攣掗撾撻挾撓擋撟掙擠揮撏撈損撿換搗據撚擄摑擲撣摻摜摣攬撳攙擱摟攪攜攝攄擺搖擯攤攖撐攆擷擼攛擻攢敵斂數齋斕鬥斬斷無舊時曠暘曇晝曨顯晉曬曉曄暈暉暫曖劄術樸機殺雜權條來楊榪傑極構樅樞棗櫪梘棖槍楓梟櫃檸檉梔柵標棧櫛櫳棟櫨櫟欄樹棲樣欒棬椏橈楨檔榿橋樺檜槳樁夢檮棶檢欞槨櫝槧欏橢樓欖櫬櫚櫸檟檻檳櫧橫檣櫻櫫櫥櫓櫞簷檁歡歟歐殲歿殤殘殞殮殫殯毆毀轂畢斃氈毿氌氣氫氬氳彙漢汙湯洶遝溝沒灃漚瀝淪滄渢溈滬濔濘淚澩瀧瀘濼瀉潑澤涇潔灑窪浹淺漿澆湞溮濁測澮濟瀏滻渾滸濃潯濜塗湧濤澇淶漣潿渦溳渙滌潤澗漲澀澱淵淥漬瀆漸澠漁瀋滲溫遊灣濕潰濺漵漊潷滾滯灩灄滿瀅濾濫灤濱灘澦濫瀠瀟瀲濰潛瀦瀾瀨瀕灝滅燈靈災燦煬爐燉煒熗點煉熾爍爛烴燭煙煩燒燁燴燙燼熱煥燜燾煆糊溜愛爺牘犛牽犧犢強狀獷獁猶狽麅獮獰獨狹獅獪猙獄猻獫獵獼玀豬貓蝟獻獺璣璵瑒瑪瑋環現瑲璽瑉玨琺瓏璫琿璡璉瑣瓊瑤璦璿瓔瓚甕甌電畫暢佘疇癤療瘧癘瘍鬁瘡瘋皰屙癰痙癢瘂癆瘓癇癡癉瘮瘞瘺癟癱癮癭癩癬癲臒皚皺皸盞鹽監蓋盜盤瞘眥矓著睜睞瞼瞞矚矯磯礬礦碭碼磚硨硯碸礪礱礫礎硜矽碩硤磽磑礄確鹼礙磧磣堿镟滾禮禕禰禎禱禍稟祿禪離禿稈種積稱穢穠穭稅穌穩穡窮竊竅窯竄窩窺竇窶豎競篤筍筆筧箋籠籩築篳篩簹箏籌簽簡籙簀篋籜籮簞簫簣簍籃籬籪籟糴類秈糶糲粵糞糧糝餱緊縶糸糾紆紅紂纖紇約級紈纊紀紉緯紜紘純紕紗綱納紝縱綸紛紙紋紡紵紖紐紓線紺絏紱練組紳細織終縐絆紼絀紹繹經紿綁絨結絝繞絰絎繪給絢絳絡絕絞統綆綃絹繡綌綏絛繼綈績緒綾緓續綺緋綽緔緄繩維綿綬繃綢綯綹綣綜綻綰綠綴緇緙緗緘緬纜緹緲緝縕繢緦綞緞緶線緱縋緩締縷編緡緣縉縛縟縝縫縗縞纏縭縊縑繽縹縵縲纓縮繆繅纈繚繕繒韁繾繰繯繳纘罌網羅罰罷羆羈羥羨翹翽翬耮耬聳恥聶聾職聹聯聵聰肅腸膚膁腎腫脹脅膽勝朧腖臚脛膠脈膾髒臍腦膿臠腳脫腡臉臘醃膕齶膩靦膃騰臏臢輿艤艦艙艫艱豔艸藝節羋薌蕪蘆蓯葦藶莧萇蒼苧蘇檾蘋莖蘢蔦塋煢繭荊薦薘莢蕘蓽蕎薈薺蕩榮葷滎犖熒蕁藎蓀蔭蕒葒葤藥蒞蓧萊蓮蒔萵薟獲蕕瑩鶯蓴蘀蘿螢營縈蕭薩蔥蕆蕢蔣蔞藍薊蘺蕷鎣驀薔蘞藺藹蘄蘊藪槁蘚虜慮虛蟲虯蟣雖蝦蠆蝕蟻螞蠶蠔蜆蠱蠣蟶蠻蟄蛺蟯螄蠐蛻蝸蠟蠅蟈蟬蠍螻蠑螿蟎蠨釁銜補襯袞襖嫋褘襪襲襏裝襠褌褳襝褲襇褸襤繈襴見觀覎規覓視覘覽覺覬覡覿覥覦覯覲覷觴觸觶讋譽謄訁計訂訃認譏訐訌討讓訕訖訓議訊記訒講諱謳詎訝訥許訛論訩訟諷設訪訣證詁訶評詛識詗詐訴診詆謅詞詘詔詖譯詒誆誄試詿詩詰詼誠誅詵話誕詬詮詭詢詣諍該詳詫諢詡譸誡誣語誚誤誥誘誨誑說誦誒請諸諏諾讀諑誹課諉諛誰諗調諂諒諄誶談誼謀諶諜謊諫諧謔謁謂諤諭諼讒諮諳諺諦謎諞諝謨讜謖謝謠謗諡謙謐謹謾謫譾謬譚譖譙讕譜譎讞譴譫讖穀豶貝貞負貟貢財責賢敗賬貨質販貪貧貶購貯貫貳賤賁貰貼貴貺貸貿費賀貽賊贄賈賄貲賃賂贓資賅贐賕賑賚賒賦賭齎贖賞賜贔賙賡賠賧賴賵贅賻賺賽賾贗讚贇贈贍贏贛赬趙趕趨趲躉躍蹌蹠躒踐躂蹺蹕躚躋踴躊蹤躓躑躡蹣躕躥躪躦軀車軋軌軒軑軔轉軛輪軟轟軲軻轤軸軹軼軤軫轢軺輕軾載輊轎輈輇輅較輒輔輛輦輩輝輥輞輬輟輜輳輻輯轀輸轡轅轄輾轆轍轔辭辯辮邊遼達遷過邁運還這進遠違連遲邇逕跡適選遜遞邐邏遺遙鄧鄺鄔郵鄒鄴鄰鬱郤郟鄶鄭鄆酈鄖鄲醞醱醬釅釃釀釋裏钜鑒鑾鏨釓釔針釘釗釙釕釷釺釧釤鈒釩釣鍆釹鍚釵鈃鈣鈈鈦鈍鈔鍾鈉鋇鋼鈑鈐鑰欽鈞鎢鉤鈧鈁鈥鈄鈕鈀鈺錢鉦鉗鈷缽鈳鉕鈽鈸鉞鑽鉬鉭鉀鈿鈾鐵鉑鈴鑠鉛鉚鈰鉉鉈鉍鈹鐸鉶銬銠鉺銪鋏鋣鐃銍鐺銅鋁銱銦鎧鍘銖銑鋌銩銛鏵銓鉿銚鉻銘錚銫鉸銥鏟銃鐋銨銀銣鑄鐒鋪鋙錸鋱鏈鏗銷鎖鋰鋥鋤鍋鋯鋨鏽銼鋝鋒鋅鋶鐦鐧銳銻鋃鋟鋦錒錆鍺錯錨錡錁錕錩錫錮鑼錘錐錦鍁錈錇錟錠鍵鋸錳錙鍥鍈鍇鏘鍶鍔鍤鍬鍾鍛鎪鍠鍰鎄鍍鎂鏤鎡鏌鎮鎛鎘鑷鐫鎳鎿鎦鎬鎊鎰鎔鏢鏜鏍鏰鏞鏡鏑鏃鏇鏐鐔钁鐐鏷鑥鐓鑭鐠鑹鏹鐙鑊鐳鐶鐲鐮鐿鑔鑣鑞鑲長門閂閃閆閈閉問闖閏闈閑閎間閔閌悶閘鬧閨聞闥閩閭闓閥閣閡閫鬮閱閬闍閾閹閶鬩閿閽閻閼闡闌闃闠闊闋闔闐闒闕闞闤隊陽陰陣階際陸隴陳陘陝隉隕險隨隱隸雋難雛讎靂霧霽黴靄靚靜靨韃鞽韉韝韋韌韍韓韙韞韜韻頁頂頃頇項順須頊頑顧頓頎頒頌頏預顱領頗頸頡頰頲頜潁熲頦頤頻頮頹頷頴穎顆題顒顎顓顏額顳顢顛顙顥纇顫顬顰顴風颺颭颮颯颶颸颼颻飀飄飆飆飛饗饜飣饑飥餳飩餼飪飫飭飯飲餞飾飽飼飿飴餌饒餉餄餎餃餏餅餑餖餓餘餒餕餜餛餡館餷饋餶餿饞饁饃餺餾饈饉饅饊饌饢馬馭馱馴馳驅馹駁驢駔駛駟駙駒騶駐駝駑駕驛駘驍罵駰驕驊駱駭駢驫驪騁驗騂駸駿騏騎騍騅騌驌驂騙騭騤騷騖驁騮騫騸驃騾驄驏驟驥驦驤髏髖髕鬢魘魎魚魛魢魷魨魯魴魺鮁鮃鯰鱸鮋鮓鮒鮊鮑鱟鮍鮐鮭鮚鮳鮪鮞鮦鰂鮜鱠鱭鮫鮮鮺鯗鱘鯁鱺鰱鰹鯉鰣鰷鯀鯊鯇鮶鯽鯒鯖鯪鯕鯫鯡鯤鯧鯝鯢鯰鯛鯨鯵鯴鯔鱝鰈鰏鱨鯷鰮鰃鰓鱷鰍鰒鰉鰁鱂鯿鰠鼇鰭鰨鰥鰩鰟鰜鰳鰾鱈鱉鰻鰵鱅鰼鱖鱔鱗鱒鱯鱤鱧鱣鳥鳩雞鳶鳴鳲鷗鴉鶬鴇鴆鴣鶇鸕鴨鴞鴦鴒鴟鴝鴛鴬鴕鷥鷙鴯鴰鵂鴴鵃鴿鸞鴻鵐鵓鸝鵑鵠鵝鵒鷳鵜鵡鵲鶓鵪鶤鵯鵬鵮鶉鶊鵷鷫鶘鶡鶚鶻鶿鶥鶩鷊鷂鶲鶹鶺鷁鶼鶴鷖鸚鷓鷚鷯鷦鷲鷸鷺鸇鷹鸌鸏鸛鸘鹺麥麩黃黌黶黷黲黽龍歷誌製壹臺臯準復勐鐘註範籤'\r\n\r\n  const Traditionalized = cc => {\r\n    let str = ''\r\n    const ss = JTPYStr()\r\n    const tt = FTPYStr()\r\n    for (let i = 0; i < cc.length; i++) {\r\n      if (cc.charCodeAt(i) > 10000 && ss.indexOf(cc.charAt(i)) !== -1) {\r\n        str += tt.charAt(ss.indexOf(cc.charAt(i)))\r\n      } else str += cc.charAt(i)\r\n    }\r\n    return str\r\n  }\r\n\r\n  const Simplized = cc => {\r\n    let str = ''\r\n    const ss = JTPYStr()\r\n    const tt = FTPYStr()\r\n    for (let i = 0; i < cc.length; i++) {\r\n      if (cc.charCodeAt(i) > 10000 && tt.indexOf(cc.charAt(i)) !== -1) {\r\n        str += ss.charAt(tt.indexOf(cc.charAt(i)))\r\n      } else str += cc.charAt(i)\r\n    }\r\n    return str\r\n  }\r\n\r\n  const translateInitialization = () => {\r\n    if (translateButtonObject) {\r\n      if (currentEncoding !== targetEncoding) {\r\n        translateButtonObject.textContent =\r\n          targetEncoding === 1\r\n            ? msgToSimplifiedChinese\r\n            : msgToTraditionalChinese\r\n        setLang()\r\n        setTimeout(translateBody, translateDelay)\r\n      }\r\n    }\r\n  }\r\n\r\n  window.translateFn = {\r\n    translatePage,\r\n    Traditionalized,\r\n    Simplized,\r\n    translateInitialization\r\n  }\r\n\r\n  translateInitialization()\r\n  btf.addGlobalFn('pjaxComplete', translateInitialization, 'translateInitialization')\r\n})\r\n"
  },
  {
    "path": "source/js/utils.js",
    "content": "(() => {\r\n  const btfFn = {\r\n    debounce: (func, wait = 0, immediate = false) => {\r\n      let timeout\r\n      return (...args) => {\r\n        const later = () => {\r\n          timeout = null\r\n          if (!immediate) func(...args)\r\n        }\r\n        const callNow = immediate && !timeout\r\n        clearTimeout(timeout)\r\n        timeout = setTimeout(later, wait)\r\n        if (callNow) func(...args)\r\n      }\r\n    },\r\n\r\n    throttle: (func, wait, options = {}) => {\r\n      let timeout, args\r\n      let previous = 0\r\n\r\n      const later = () => {\r\n        previous = options.leading === false ? 0 : new Date().getTime()\r\n        timeout = null\r\n        func(...args)\r\n        if (!timeout) args = null\r\n      }\r\n\r\n      return (...params) => {\r\n        const now = new Date().getTime()\r\n        if (!previous && options.leading === false) previous = now\r\n        const remaining = wait - (now - previous)\r\n        args = params\r\n\r\n        if (remaining <= 0 || remaining > wait) {\r\n          if (timeout) {\r\n            clearTimeout(timeout)\r\n            timeout = null\r\n          }\r\n          previous = now\r\n          func(...args)\r\n          if (!timeout) args = null\r\n        } else if (!timeout && options.trailing !== false) {\r\n          timeout = setTimeout(later, remaining)\r\n        }\r\n      }\r\n    },\r\n\r\n    overflowPaddingR: {\r\n      add: () => {\r\n        const paddingRight = window.innerWidth - document.body.clientWidth\r\n\r\n        if (paddingRight > 0) {\r\n          document.body.style.paddingRight = `${paddingRight}px`\r\n          document.body.style.overflow = 'hidden'\r\n          const menuElement = document.querySelector('#page-header.nav-fixed #menus')\r\n          if (menuElement) {\r\n            menuElement.style.paddingRight = `${paddingRight}px`\r\n          }\r\n        }\r\n      },\r\n      remove: () => {\r\n        document.body.style.paddingRight = ''\r\n        document.body.style.overflow = ''\r\n        const menuElement = document.querySelector('#page-header.nav-fixed #menus')\r\n        if (menuElement) {\r\n          menuElement.style.paddingRight = ''\r\n        }\r\n      }\r\n    },\r\n\r\n    snackbarShow: (text, showAction = false, duration = 2000) => {\r\n      const { position, bgLight, bgDark } = GLOBAL_CONFIG.Snackbar\r\n      const bg = document.documentElement.getAttribute('data-theme') === 'light' ? bgLight : bgDark\r\n      Snackbar.show({\r\n        text,\r\n        backgroundColor: bg,\r\n        showAction,\r\n        duration,\r\n        pos: position,\r\n        customClass: 'snackbar-css'\r\n      })\r\n    },\r\n\r\n    diffDate: (inputDate, more = false) => {\r\n      const dateNow = new Date()\r\n      const datePost = new Date(inputDate)\r\n      const diffMs = dateNow - datePost\r\n      const diffSec = diffMs / 1000\r\n      const diffMin = diffSec / 60\r\n      const diffHour = diffMin / 60\r\n      const diffDay = diffHour / 24\r\n      const diffMonth = diffDay / 30\r\n      const { dateSuffix } = GLOBAL_CONFIG\r\n\r\n      if (!more) return Math.floor(diffDay)\r\n\r\n      if (diffMonth > 12) return datePost.toISOString().slice(0, 10)\r\n      if (diffMonth >= 1) return `${Math.floor(diffMonth)} ${dateSuffix.month}`\r\n      if (diffDay >= 1) return `${Math.floor(diffDay)} ${dateSuffix.day}`\r\n      if (diffHour >= 1) return `${Math.floor(diffHour)} ${dateSuffix.hour}`\r\n      if (diffMin >= 1) return `${Math.floor(diffMin)} ${dateSuffix.min}`\r\n      return dateSuffix.just\r\n    },\r\n\r\n    loadComment: (dom, callback) => {\r\n      if ('IntersectionObserver' in window) {\r\n        const observerItem = new IntersectionObserver(entries => {\r\n          if (entries[0].isIntersecting) {\r\n            callback()\r\n            observerItem.disconnect()\r\n          }\r\n        }, { threshold: [0] })\r\n        observerItem.observe(dom)\r\n      } else {\r\n        callback()\r\n      }\r\n    },\r\n\r\n    scrollToDest: (pos, time = 500) => {\r\n      const currentPos = window.scrollY\r\n      const isNavFixed = document.getElementById('page-header').classList.contains('fixed')\r\n      if (currentPos > pos || isNavFixed) pos = pos - 70\r\n\r\n      if ('scrollBehavior' in document.documentElement.style) {\r\n        window.scrollTo({\r\n          top: pos,\r\n          behavior: 'smooth'\r\n        })\r\n        return\r\n      }\r\n\r\n      const startTime = performance.now()\r\n      const animate = currentTime => {\r\n        const timeElapsed = currentTime - startTime\r\n        const progress = Math.min(timeElapsed / time, 1)\r\n        window.scrollTo(0, currentPos + (pos - currentPos) * progress)\r\n        if (progress < 1) {\r\n          requestAnimationFrame(animate)\r\n        }\r\n      }\r\n      requestAnimationFrame(animate)\r\n    },\r\n\r\n    animateIn: (ele, animation) => {\r\n      ele.style.display = 'block'\r\n      ele.style.animation = animation\r\n    },\r\n\r\n    animateOut: (ele, animation) => {\r\n      const handleAnimationEnd = () => {\r\n        ele.style.display = ''\r\n        ele.style.animation = ''\r\n        ele.removeEventListener('animationend', handleAnimationEnd)\r\n      }\r\n      ele.addEventListener('animationend', handleAnimationEnd)\r\n      ele.style.animation = animation\r\n    },\r\n\r\n    wrap: (selector, eleType, options) => {\r\n      const createEle = document.createElement(eleType)\r\n      for (const [key, value] of Object.entries(options)) {\r\n        createEle.setAttribute(key, value)\r\n      }\r\n      selector.parentNode.insertBefore(createEle, selector)\r\n      createEle.appendChild(selector)\r\n    },\r\n\r\n    isHidden: ele => ele.offsetHeight === 0 && ele.offsetWidth === 0,\r\n\r\n    getEleTop: ele => ele.getBoundingClientRect().top + window.scrollY,\r\n\r\n    loadLightbox: ele => {\r\n      const service = GLOBAL_CONFIG.lightbox\r\n\r\n      if (service === 'medium_zoom') {\r\n        mediumZoom(ele, { background: 'var(--zoom-bg)' })\r\n        return\r\n      }\r\n\r\n      if (service === 'fancybox') {\r\n        ele.forEach(i => {\r\n          if (i.parentNode.tagName !== 'A') {\r\n            const dataSrc = i.dataset.lazySrc || i.src\r\n            const dataCaption = i.title || i.alt || ''\r\n            btf.wrap(i, 'a', { href: dataSrc, 'data-fancybox': 'gallery', 'data-caption': dataCaption, 'data-thumb': dataSrc })\r\n          }\r\n        })\r\n\r\n        if (!window.fancyboxRun) {\r\n          let options = ''\r\n          if (Fancybox.version < '6') {\r\n            options = {\r\n              Hash: false,\r\n              Thumbs: {\r\n                showOnStart: false\r\n              },\r\n              Images: {\r\n                Panzoom: {\r\n                  maxScale: 4\r\n                }\r\n              },\r\n              Carousel: {\r\n                transition: 'slide'\r\n              },\r\n              Toolbar: {\r\n                display: {\r\n                  left: ['infobar'],\r\n                  middle: [\r\n                    'zoomIn',\r\n                    'zoomOut',\r\n                    'toggle1to1',\r\n                    'rotateCCW',\r\n                    'rotateCW',\r\n                    'flipX',\r\n                    'flipY'\r\n                  ],\r\n                  right: ['slideshow', 'thumbs', 'close']\r\n                }\r\n              }\r\n            }\r\n          } else {\r\n            options = {\r\n              Hash: false,\r\n              Carousel: {\r\n                transition: 'slide',\r\n                Thumbs: {\r\n                  showOnStart: false\r\n                },\r\n                Toolbar: {\r\n                  display: {\r\n                    left: ['counter'],\r\n                    middle: [\r\n                      'zoomIn',\r\n                      'zoomOut',\r\n                      'toggle1to1',\r\n                      'rotateCCW',\r\n                      'rotateCW',\r\n                      'flipX',\r\n                      'flipY',\r\n                      'reset'\r\n                    ],\r\n                    right: ['autoplay', 'thumbs', 'close']\r\n                  }\r\n                },\r\n                Zoomable: {\r\n                  Panzoom: {\r\n                    maxScale: 4\r\n                  }\r\n                }\r\n              }\r\n            }\r\n          }\r\n\r\n          Fancybox.bind('[data-fancybox]', options)\r\n          window.fancyboxRun = true\r\n        }\r\n      }\r\n    },\r\n\r\n    setLoading: {\r\n      add: ele => {\r\n        const html = `\r\n        <div class=\"loading-container\">\r\n          <div class=\"loading-item\">\r\n            <div></div><div></div><div></div><div></div><div></div>\r\n          </div>\r\n        </div>\r\n      `\r\n        ele.insertAdjacentHTML('afterend', html)\r\n      },\r\n      remove: ele => {\r\n        ele.nextElementSibling.remove()\r\n      }\r\n    },\r\n\r\n    updateAnchor: anchor => {\r\n      if (anchor !== window.location.hash) {\r\n        if (!anchor) anchor = location.pathname\r\n        const title = GLOBAL_CONFIG_SITE.title\r\n        window.history.replaceState({\r\n          url: location.href,\r\n          title\r\n        }, title, anchor)\r\n      }\r\n    },\r\n\r\n    getScrollPercent: (() => {\r\n      let docHeight, winHeight, headerHeight, contentMath\r\n\r\n      return (currentTop, ele) => {\r\n        if (!docHeight || ele.clientHeight !== docHeight) {\r\n          docHeight = ele.clientHeight\r\n          winHeight = window.innerHeight\r\n          headerHeight = ele.offsetTop\r\n          contentMath = Math.max(docHeight - winHeight, document.documentElement.scrollHeight - winHeight)\r\n        }\r\n\r\n        const scrollPercent = (currentTop - headerHeight) / contentMath\r\n        return Math.max(0, Math.min(100, Math.round(scrollPercent * 100)))\r\n      }\r\n    })(),\r\n\r\n    addEventListenerPjax: (ele, event, fn, option = false) => {\r\n      ele.addEventListener(event, fn, option)\r\n      btf.addGlobalFn('pjaxSendOnce', () => {\r\n        ele.removeEventListener(event, fn, option)\r\n      })\r\n    },\r\n\r\n    removeGlobalFnEvent: (key, parent = window) => {\r\n      const globalFn = parent.globalFn || {}\r\n      const keyObj = globalFn[key]\r\n      if (!keyObj) return\r\n\r\n      Object.keys(keyObj).forEach(i => keyObj[i]())\r\n\r\n      delete globalFn[key]\r\n    },\r\n\r\n    switchComments: (el = document, path) => {\r\n      const switchBtn = el.querySelector('#switch-btn')\r\n      if (!switchBtn) return\r\n\r\n      let switchDone = false\r\n      const postComment = el.querySelector('#post-comment')\r\n      const handleSwitchBtn = () => {\r\n        postComment.classList.toggle('move')\r\n        if (!switchDone && typeof loadOtherComment === 'function') {\r\n          switchDone = true\r\n          loadOtherComment(el, path)\r\n        }\r\n      }\r\n      btf.addEventListenerPjax(switchBtn, 'click', handleSwitchBtn)\r\n    }\r\n  }\r\n\r\n  window.btf = { ...window.btf, ...btfFn }\r\n})()\r\n"
  }
]