mirror of
https://github.com/jerryc127/hexo-theme-butterfly.git
synced 2026-04-10 21:17:07 +08:00
Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fedddbb5a | ||
|
|
439014bbb6 | ||
|
|
d369de91b8 | ||
|
|
2d4765202d | ||
|
|
5e766ac40a | ||
|
|
4c3a782610 | ||
|
|
8645a4355d | ||
|
|
88f3f2eef3 | ||
|
|
4226c95818 | ||
|
|
67c95cee0c | ||
|
|
0a72f43fae | ||
|
|
006414da98 | ||
|
|
c61f55a773 | ||
|
|
6feac51de7 | ||
|
|
fcd760011a | ||
|
|
ec1a226774 | ||
|
|
5ee24defc3 | ||
|
|
58818a0630 | ||
|
|
a551b9277f | ||
|
|
1b5bc97431 | ||
|
|
0da72787fa | ||
|
|
dcb35181fc | ||
|
|
0e9b8f5b69 | ||
|
|
89b3626c84 | ||
|
|
c5417d4532 | ||
|
|
dd8a18e8f4 | ||
|
|
736cd75cda | ||
|
|
a61e216452 | ||
|
|
b89f165be3 | ||
|
|
1de3507843 | ||
|
|
d2eacd2d8a | ||
|
|
5bfc1da03b | ||
|
|
21c238e5c1 | ||
|
|
60fa703fd3 | ||
|
|
7353de6c2e | ||
|
|
a9fe9f5332 | ||
|
|
73de62a6e1 | ||
|
|
cf059bd533 | ||
|
|
8cb726ddaa | ||
|
|
baee803689 | ||
|
|
023d82820d | ||
|
|
b7f1610859 | ||
|
|
5bac44a284 | ||
|
|
9c5294b40c | ||
|
|
a7315b6bfc | ||
|
|
cb3778c686 | ||
|
|
2ce969023c | ||
|
|
7c697c15e8 | ||
|
|
ca030589fb | ||
|
|
46cf1c4e80 | ||
|
|
a3f6b625ed | ||
|
|
4a955f0f05 | ||
|
|
525ed7ac82 | ||
|
|
b3ba4c9ac4 | ||
|
|
628d1bbe52 | ||
|
|
ede4f8bfea | ||
|
|
feb3346cf6 | ||
|
|
576fa5c80e | ||
|
|
6019cc8b7c | ||
|
|
7f9409553a | ||
|
|
2de7d34b2b | ||
|
|
4b8a610c08 |
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -10,4 +10,4 @@ liberapay: # Replace with a single Liberapay username
|
|||||||
issuehunt: # Replace with a single IssueHunt username
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
otechie: # Replace with a single Otechie username
|
otechie: # Replace with a single Otechie username
|
||||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||||
custom: ['https://buy.stripe.com/3cs6rP6YA91sbbG5kk','https://jsd.012700.xyz/gh/jerryc127/CDN/Photo/wechat.jpg'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
custom: ['https://buy.stripe.com/3cs6rP6YA91sbbG5kk'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +1,2 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
.DS_Store
|
node_modules/
|
||||||
|
|||||||
310
README.md
310
README.md
@@ -1,117 +1,193 @@
|
|||||||
<div align="right">
|
<div align="right">
|
||||||
<a title="Chinese" href="/README_CN.md">中文</a>
|
<a title="中文" href="/README_CN.md">中文</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
<img src="./source/img/butterfly-icon.png" width="150" height="150" />
|
<img src="./source/img/butterfly-icon.png" width="150" height="150" alt="Butterfly Logo" />
|
||||||
|
|
||||||
# hexo-theme-butterfly
|
# hexo-theme-butterfly
|
||||||
|
|
||||||

|
A modern, elegant and feature-rich theme for Hexo
|
||||||

|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|

|
||||||
📢 Demo: [Butterfly](https://butterfly.js.org/) / [CrazyWong](https://blog.crazywong.com/)
|

|
||||||
|

|
||||||
📖 Docs: [English](https://butterfly.js.org/en/posts/butterfly-docs-en-get-started/) / [Chinese](https://butterfly.js.org/posts/21cfbf15/)
|
|
||||||
|
📢 **Demo**: [Butterfly Official](https://butterfly.js.org/) | [CrazyWong's Blog](https://blog.crazywong.com/)
|
||||||

|
|
||||||
|
📖 **Documentation**: [English Docs](https://butterfly.js.org/en/posts/butterfly-docs-en-get-started/) | [中文文档](https://butterfly.js.org/posts/21cfbf15/)
|
||||||
</div>
|
|
||||||
|

|
||||||
---
|
|
||||||
|
</div>
|
||||||
## 💻 Installation
|
|
||||||
|
---
|
||||||
### GIT
|
|
||||||
|
## 🚀 Quick Start
|
||||||
> If you are in Mainland China, you can download in [Gitee](https://gitee.com/immyw/hexo-theme-butterfly.git)
|
|
||||||
|
### 💾 Installation
|
||||||
Stable branch [recommend]:
|
|
||||||
|
#### Method 1: Git Installation (Recommended)
|
||||||
```
|
|
||||||
git clone -b master https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly
|
> 💡 **Tip**: If GitHub access is slow in mainland China, you can use the [Gitee Mirror](https://gitee.com/immyw/hexo-theme-butterfly.git)
|
||||||
```
|
|
||||||
|
Execute in your Hexo blog root directory:
|
||||||
Dev branch:
|
|
||||||
|
```bash
|
||||||
```
|
# Install stable version (recommended)
|
||||||
git clone -b dev https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly
|
git clone -b master https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly
|
||||||
```
|
```
|
||||||
|
|
||||||
### NPM
|
```bash
|
||||||
|
# Install development version (early access to new features)
|
||||||
> It supports Hexo 5.0.0 or later
|
git clone -b dev https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly
|
||||||
|
```
|
||||||
In Hexo site root directory
|
|
||||||
|
#### Method 2: NPM Installation
|
||||||
```powershell
|
|
||||||
npm i hexo-theme-butterfly
|
> ⚠️ **Note**: NPM installation only supports Hexo 5.0.0 and above
|
||||||
```
|
|
||||||
|
```bash
|
||||||
## ⚙ Configuration
|
npm install hexo-theme-butterfly
|
||||||
|
```
|
||||||
Set theme in the hexo work folder's root config file `_config.yml`:
|
|
||||||
|
### ⚙️ Theme Configuration
|
||||||
> theme: butterfly
|
|
||||||
|
1. **Enable Theme**: Modify your Hexo configuration file `_config.yml`:
|
||||||
If you don't have pug & stylus renderer, try this:
|
|
||||||
|
```yaml
|
||||||
> npm install hexo-renderer-pug hexo-renderer-stylus
|
theme: butterfly
|
||||||
|
```
|
||||||
## 🎉 Features
|
|
||||||
|
2. **Install Dependencies**: If you haven't installed pug and stylus renderers, please run:
|
||||||
- [x] Card UI Design
|
|
||||||
- [x] Rounded Design/Squared Design
|
```bash
|
||||||
- [X] Support sub-menu
|
npm install hexo-renderer-pug hexo-renderer-stylus --save
|
||||||
- [x] Two-column layout
|
```
|
||||||
- [x] Responsive Web Design
|
|
||||||
- [x] Dark Mode
|
## ✨ Theme Features
|
||||||
- [x] Pjax
|
|
||||||
- [x] Read Mode
|
### 🎨 Design Style
|
||||||
- [x] Conversion between Traditional and Simplified Chinese
|
- [x] **Card-based Design** - Modern card-style layout
|
||||||
- [X] TOC catalog is available for both computers and mobile phones
|
- [x] **Rounded/Square Design** - Customizable border styles
|
||||||
- [X] Built-in Syntax Highlighting Themes (darker/pale night/light/ocean), also support customization
|
- [x] **Responsive Design** - Perfect adaptation to all screen sizes
|
||||||
- [X] Code Blocks (Display code language/close or expand Code Blocks/Copy Button/word wrap)
|
- [x] **Two-column Layout** - Optimized reading experience
|
||||||
- [X] Disable copy/Add a Copyright Notice to the Copied Text
|
- [x] **Dark Mode** - Eye-friendly night mode
|
||||||
- [X] Search (Algolia Search/Local Search)
|
|
||||||
- [x] Mathjax and Katex
|
### 📝 Content Features
|
||||||
- [x] Built-in 404 page
|
- [x] **Multi-level Menu** - Support for secondary navigation menus
|
||||||
- [x] WordCount
|
- [x] **Reading Mode** - Focused article reading experience
|
||||||
- [x] Related articles
|
- [x] **TOC Navigation** - Desktop and mobile TOC support
|
||||||
- [x] Displays outdated notice for a post
|
- [x] **Word Count** - Display article word count and reading time
|
||||||
- [x] Share (Sharejs/Addtoany)
|
- [x] **Related Articles** - Smart recommendation of related content
|
||||||
- [X] Comment (Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus/Remark42/artalk)
|
- [x] **Outdated Reminder** - Automatic article update status alerts
|
||||||
- [x] Multiple Comment System Support
|
- [x] **Traditional/Simplified Chinese** - Support for Traditional and Simplified Chinese switching
|
||||||
- [x] Online Chats (Chatra/Tidio/Crisp)
|
- [x] **Tag Plugins** - Rich tag plugin support
|
||||||
- [x] Web analytics
|
|
||||||
- [x] Google AdSense
|
### 🔍 Search & Navigation
|
||||||
- [x] Webmaster Verification
|
- [x] **Multiple Search Options** - Algolia Search / Local Search / Docsearch
|
||||||
- [x] Change website colour scheme
|
- [x] **Built-in 404** - Beautiful 404 error page
|
||||||
- [x] Typewriter Effect: activate_power_mode
|
- [x] **Pjax Support** - Smooth page transition experience
|
||||||
- [x] Background effects (Canvas ribbon/canvas_ribbon_piao/canvas_nest)
|
|
||||||
- [x] Mouse click effects (Fireworks/Heart/Text)
|
### 🎨 Code Display
|
||||||
- [x] Preloader/Loading Animation/pace.js
|
- [x] **Syntax Highlighting** - Built-in multiple themes (darker/pale night/light/ocean)
|
||||||
- [x] Busuanzi visitor counter
|
- [x] **Code Features** - Language display/fold expand/copy button/auto-wrap
|
||||||
- [x] Medium Zoom/Fancybox
|
- [x] **Math Formulas** - Support for Mathjax and Katex
|
||||||
- [x] Mermaid
|
|
||||||
- [x] Chart.js
|
### 💬 Social Interaction
|
||||||
- [x] Justified Gallery
|
- [x] **Multiple Comment Systems** - Disqus/Gitalk/Valine/Waline/Twikoo/Giscus/Artalk etc.
|
||||||
- [x] Lazyload images
|
- [x] **Dual Comments Support** - Enable two comment systems simultaneously
|
||||||
- [x] Instantpage/Snackbar notification toast/PWA......
|
- [x] **Share Features** - Sharejs/Addtoany sharing components
|
||||||
|
- [x] **Live Chat** - Chatra/Tidio/Crisp instant messaging
|
||||||
## ✨ Contributors
|
|
||||||
|
### 📊 Analytics & Statistics
|
||||||
<a href="https://github.com/jerryc127/hexo-theme-butterfly/graphs/contributors">
|
- [x] **Visit Statistics** - Busuanzi counter
|
||||||
<img src="https://contrib.rocks/image?repo=jerryc127/hexo-theme-butterfly" />
|
- [x] **Site Analytics** - Google Analytics/Baidu Analytics/Cloudflare Analytics/Microsoft Clarity/Umami
|
||||||
</a>
|
- [x] **Webmaster Verification** - Major search engine verification
|
||||||
|
- [x] **Ad Support** - Google AdSense/custom ad slots
|
||||||
## 📷 Screenshots
|
|
||||||
|
### 🎪 Visual Effects
|
||||||

|
- [x] **Typing Effects** - activate_power_mode animations
|
||||||

|
- [x] **Background Effects** - Static ribbons/dynamic ribbons/floating ribbons/Canvas Nest
|
||||||

|
- [x] **Mouse Effects** - Fireworks/hearts/text click effects
|
||||||

|
- [x] **Loading Animations** - Preloader and pace.js progress bars
|
||||||
|
- [x] **Image Effects** - Medium Zoom/Fancybox image lightbox
|
||||||
|
- [x] **Lazy Loading** - Image lazy loading optimization
|
||||||
|
|
||||||
|
### 🛠️ Advanced Features
|
||||||
|
- [x] **PWA Support** - Progressive Web App
|
||||||
|
- [x] **Copy Protection** - Disable text copying/copyright info append
|
||||||
|
- [x] **Theme Customization** - Custom site color schemes
|
||||||
|
- [x] **Chart Support** - Mermaid flowcharts/Chart.js data charts
|
||||||
|
- [x] **Music Notation** - ABCJS music notation support
|
||||||
|
- [x] **Music Player** - APlayer/Meting music playback
|
||||||
|
- [x] **Article Series** - Series article organization
|
||||||
|
- [x] **Instantpage** - Page preloading acceleration
|
||||||
|
- [x] **Snackbar** - Elegant notification messages
|
||||||
|
|
||||||
|
## 🤝 Contributors
|
||||||
|
|
||||||
|
Thanks to all the developers who have contributed to the Butterfly theme!
|
||||||
|
|
||||||
|
[](https://github.com/jerryc127/hexo-theme-butterfly/graphs/contributors)
|
||||||
|
|
||||||
|
## 📸 Screenshots
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
## ⭐ Star History
|
||||||
|
|
||||||
|
[](https://star-history.com/#jerryc127/hexo-theme-butterfly&Date)
|
||||||
|
|
||||||
|
## 🤝 Building a Better Theme Together
|
||||||
|
|
||||||
|
We 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.
|
||||||
|
|
||||||
|
### 💬 Get Help & Support
|
||||||
|
|
||||||
|
- 🐛 **Found a bug?** → [GitHub Issues](https://github.com/jerryc127/hexo-theme-butterfly/issues) - Let's solve it together!
|
||||||
|
- 💡 **Have ideas?** → [GitHub Discussions](https://github.com/jerryc127/hexo-theme-butterfly/discussions) - Share your creative ideas!
|
||||||
|
- 📚 **Learning to use?** → [Official Documentation](https://butterfly.js.org/) - Detailed usage guide
|
||||||
|
- 💬 **Real-time discussion?** → [Telegram Group](https://t.me/bu2fly) - Chat with community members
|
||||||
|
|
||||||
|
### 🎯 Contributing
|
||||||
|
|
||||||
|
Want to make Butterfly better? We welcome any form of contribution:
|
||||||
|
|
||||||
|
- **🔧 Code Contributions** - Fix bugs, add new features, optimize performance
|
||||||
|
- **📝 Documentation** - Improve docs, translate content, write tutorials
|
||||||
|
- **🎨 Design Suggestions** - UI/UX improvements, theme colors, icon design
|
||||||
|
- **🧪 Testing & Feedback** - Test new features, report issues, provide user experience
|
||||||
|
- **💰 Financial Support** - [Sponsor the Project](https://buy.stripe.com/3cs6rP6YA91sbbG5kk) - Support long-term development
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
This project is licensed under the [Apache 2.0](LICENSE) License.
|
||||||
|
|
||||||
|
## 🙏 Acknowledgments
|
||||||
|
|
||||||
|
This 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!
|
||||||
|
|
||||||
|
Thanks to all friends who have contributed to the development of the Butterfly theme. Your support has made this theme continuously improve and progress.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
**✨ If this theme helps you, please give us a ⭐ Star! ✨**
|
||||||
|
</div>
|
||||||
|
|||||||
222
README_CN.md
222
README_CN.md
@@ -4,114 +4,190 @@
|
|||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
<img src="./source/img/butterfly-icon.png" width="150" height="150" />
|
<img src="./source/img/butterfly-icon.png" width="150" height="150" alt="Butterfly Logo" />
|
||||||
|
|
||||||
# hexo-theme-butterfly
|
# hexo-theme-butterfly
|
||||||
|
|
||||||
|
一個適用於 Hexo 的現代化、美觀且功能豐富的主題
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|

|
||||||
|
|
||||||
📢 預覽: [Butterfly](https://butterfly.js.org/) / [CrazyWong](https://blog.crazywong.com/)
|
📢 **在線預覽**: [Butterfly 官方](https://butterfly.js.org/) | [CrazyWong 博客](https://blog.crazywong.com/)
|
||||||
|
|
||||||
📖 文檔: [中文](https://butterfly.js.org/posts/21cfbf15/) / [English](https://butterfly.js.org/en/posts/butterfly-docs-en-get-started/)
|
📖 **完整文檔**: [中文文檔](https://butterfly.js.org/posts/21cfbf15/) | [English Docs](https://butterfly.js.org/en/posts/butterfly-docs-en-get-started/)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 💻 安裝
|
## 🚀 快速開始
|
||||||
|
|
||||||
### Git 安裝
|
### 💾 安裝方式
|
||||||
|
|
||||||
> 本倉庫同時上傳到 [Gitee](https://gitee.com/immyw/hexo-theme-butterfly.git),如果你訪問 Github 緩慢,可從 Gitee 中下載。
|
#### 方式一:Git 安裝(推薦)
|
||||||
|
|
||||||
在博客根目錄裡安裝穩定版【推薦】
|
> 💡 **提示**: 如果您在中國大陸訪問 GitHub 速度較慢,可以使用 [Gitee 鏡像](https://gitee.com/immyw/hexo-theme-butterfly.git)
|
||||||
|
|
||||||
```powershell
|
在您的 Hexo 博客根目錄下執行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安裝穩定版本(推薦)
|
||||||
git clone -b master https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly
|
git clone -b master https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly
|
||||||
```
|
```
|
||||||
|
|
||||||
如果想要安裝比較新的dev分支,可以
|
```bash
|
||||||
|
# 安裝開發版本(搶先體驗新功能)
|
||||||
```powershell
|
|
||||||
git clone -b dev https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly
|
git clone -b dev https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly
|
||||||
```
|
```
|
||||||
|
|
||||||
### npm 安裝
|
#### 方式二:NPM 安裝
|
||||||
|
|
||||||
> 此方法只支持Hexo 5.0.0以上版本
|
> ⚠️ **注意**: NPM 安裝方式僅支援 Hexo 5.0.0 及以上版本
|
||||||
|
|
||||||
在博客根目錄裡
|
```bash
|
||||||
|
npm install hexo-theme-butterfly
|
||||||
```powershell
|
|
||||||
npm i hexo-theme-butterfly
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## ⚙ 應用主題
|
### ⚙️ 主題配置
|
||||||
|
|
||||||
修改hexo配置文件`_config.yml`,把主題改為`Butterfly`
|
1. **啟用主題**: 修改您的 Hexo 配置檔案 `_config.yml`:
|
||||||
|
|
||||||
```
|
```yaml
|
||||||
theme: butterfly
|
theme: butterfly
|
||||||
```
|
```
|
||||||
|
|
||||||
>如果你沒有pug以及stylus的渲染器,請下載安裝: npm install hexo-renderer-pug hexo-renderer-stylus --save
|
2. **安裝依賴**: 如果您尚未安裝 pug 和 stylus 渲染器,請執行:
|
||||||
|
|
||||||
## 🎉 特色
|
```bash
|
||||||
|
npm install hexo-renderer-pug hexo-renderer-stylus --save
|
||||||
|
```
|
||||||
|
|
||||||
- [x] 卡片化設計
|
## ✨ 主題特色
|
||||||
- [x] 圓角化設計/直角化設計
|
|
||||||
- [X] 支持二級目錄
|
|
||||||
- [x] 雙欄設計
|
|
||||||
- [x] 響應式主題
|
|
||||||
- [x] 夜間模式
|
|
||||||
- [x] Pjax
|
|
||||||
- [x] 文章閲讀模式
|
|
||||||
- [x] 簡體和繁體轉換
|
|
||||||
- [X] 電腦和手機都可查看TOC目錄
|
|
||||||
- [X] 內置多種代碼配色(darker/pale night/light/ocean),可自定義代碼配色
|
|
||||||
- [X] 代碼塊顯示代碼語言/關閉或展開代碼塊/代碼複製/代碼自動換行
|
|
||||||
- [X] 可關閉文字複製/可開啟內容複製增加版權信息)
|
|
||||||
- [X] 兩種搜索( Algolia 搜索和本地搜索)
|
|
||||||
- [x] Mathjax 和 Katex
|
|
||||||
- [x] 內置404頁面
|
|
||||||
- [x] 顯示字數統計
|
|
||||||
- [x] 顯示相關文章
|
|
||||||
- [x] 過期文章提醒
|
|
||||||
- [x] 多種分享系統(Sharejs/Addtoany)
|
|
||||||
- [X] 多種評論系統(Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus/Remark42/artalk)
|
|
||||||
- [x] 支持雙評論部署
|
|
||||||
- [x] 多種在線聊天(Chatra/Tidio/Crisp)
|
|
||||||
- [x] 多種分析系統
|
|
||||||
- [x] 谷歌廣告/手動廣告位置
|
|
||||||
- [x] 各種站長驗證
|
|
||||||
- [x] 修改網站配色
|
|
||||||
- [x] 打字特效 activate_power_mode
|
|
||||||
- [x] 多種背景特效(靜止彩帶/動態彩帶/Canvas Nest)
|
|
||||||
- [x] 多種鼠標點擊特效(煙花/文字/愛心)
|
|
||||||
- [x] 內置一種 Preloader 加載動畫和 pace.js 加載動畫條
|
|
||||||
- [x] 不蒜子訪問統計
|
|
||||||
- [x] 兩種大圖模式(Medium Zoom/Fancybox)
|
|
||||||
- [x] Mermaid 圖表顯示
|
|
||||||
- [x] Chart.js 圖表顯示
|
|
||||||
- [x] 照片牆
|
|
||||||
- [x] 圖片懶加載
|
|
||||||
- [x] Instantpage/Snackbar彈窗/PWA......
|
|
||||||
|
|
||||||
## ✨ 貢獻者
|
### 🎨 設計風格
|
||||||
|
- [x] **卡片化設計** - 現代化的卡片式佈局
|
||||||
|
- [x] **圓角/直角設計** - 支援自訂邊框樣式
|
||||||
|
- [x] **響應式設計** - 完美適配各種螢幕尺寸
|
||||||
|
- [x] **雙欄佈局** - 優化的閱讀體驗
|
||||||
|
- [x] **深色模式** - 護眼的夜間模式
|
||||||
|
|
||||||
<a href="https://github.com/jerryc127/hexo-theme-butterfly/graphs/contributors">
|
### 📝 內容功能
|
||||||
<img src="https://contrib.rocks/image?repo=jerryc127/hexo-theme-butterfly" />
|
- [x] **多級選單** - 支援二級導航選單
|
||||||
</a>
|
- [x] **閱讀模式** - 專注的文章閱讀體驗
|
||||||
|
- [x] **目錄導航** - 電腦和手機雙端支援 TOC
|
||||||
|
- [x] **字數統計** - 顯示文章字數和閱讀時間
|
||||||
|
- [x] **相關文章** - 智能推薦相關內容
|
||||||
|
- [x] **過期提醒** - 自動提示文章更新狀態
|
||||||
|
- [x] **簡繁轉換** - 支援繁體中文和簡體中文切換
|
||||||
|
- [x] **標籤外掛** - 豐富的標籤外掛支持
|
||||||
|
|
||||||
## 📷 截圖
|
### 🔍 搜尋與導航
|
||||||
|
- [x] **多種搜尋** - Algolia 搜尋 / 本地搜尋 / Docsearch
|
||||||
|
- [x] **內建 404** - 美觀的 404 錯誤頁面
|
||||||
|
- [x] **Pjax 支援** - 流暢的頁面切換體驗
|
||||||
|
|
||||||

|
### 🎨 程式碼展示
|
||||||

|
- [x] **語法高亮** - 內建多種主題(darker/pale night/light/ocean)
|
||||||

|
- [x] **程式碼功能** - 語言顯示/摺疊展開/複製按鈕/自動換行
|
||||||

|
- [x] **數學公式** - 支援 Mathjax 和 Katex
|
||||||
|
|
||||||
|
### 💬 社交互動
|
||||||
|
- [x] **多元評論系統** - Disqus/Gitalk/Valine/Waline/Twikoo/Giscus/Artalk 等
|
||||||
|
- [x] **雙評論支援** - 可同時啟用兩套評論系統
|
||||||
|
- [x] **分享功能** - Sharejs/Addtoany 分享套件
|
||||||
|
- [x] **線上客服** - Chatra/Tidio/Crisp 即時聊天
|
||||||
|
|
||||||
|
### 📊 數據分析
|
||||||
|
- [x] **訪問統計** - 不蒜子計數器
|
||||||
|
- [x] **網站分析** - Google Analytics/百度統計/Cloudflare Analytics/Microsoft Clarity/Umami
|
||||||
|
- [x] **站長驗證** - 各大搜尋引擎驗證
|
||||||
|
- [x] **廣告支援** - Google AdSense/自訂廣告位
|
||||||
|
|
||||||
|
### 🎪 視覺效果
|
||||||
|
- [x] **打字特效** - activate_power_mode 動畫
|
||||||
|
- [x] **背景特效** - 靜態彩帶/動態彩帶/飄帶效果/Canvas Nest
|
||||||
|
- [x] **滑鼠特效** - 煙花/愛心/文字點擊效果
|
||||||
|
- [x] **載入動畫** - Preloader 和 pace.js 進度條
|
||||||
|
- [x] **圖片效果** - Medium Zoom/Fancybox 圖片燈箱
|
||||||
|
- [x] **懶載入** - 圖片延遲載入優化
|
||||||
|
|
||||||
|
### 🛠️ 進階功能
|
||||||
|
- [x] **PWA 支援** - 漸進式網頁應用
|
||||||
|
- [x] **複製保護** - 可關閉文字複製/版權資訊追加
|
||||||
|
- [x] **主題定製** - 自訂網站配色方案
|
||||||
|
- [x] **圖表支援** - Mermaid 流程圖/Chart.js 數據圖表
|
||||||
|
- [x] **音樂符號** - ABCJS 音樂記譜法支援
|
||||||
|
- [x] **音樂播放器** - APlayer/Meting 音樂播放功能
|
||||||
|
- [x] **系列文章** - 系列文章組織功能
|
||||||
|
- [x] **Instantpage** - 頁面預載入加速
|
||||||
|
- [x] **Snackbar** - 優雅的提示訊息
|
||||||
|
|
||||||
|
## 🤝 貢獻者
|
||||||
|
|
||||||
|
感謝所有為 Butterfly 主題做出貢獻的開發者們!
|
||||||
|
|
||||||
|
[](https://github.com/jerryc127/hexo-theme-butterfly/graphs/contributors)
|
||||||
|
|
||||||
|
## 📸 主題截圖
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
## ⭐ Star 趨勢
|
||||||
|
|
||||||
|
[](https://star-history.com/#jerryc127/hexo-theme-butterfly&Date)
|
||||||
|
|
||||||
|
## 🤝 一起構建更美好的主題
|
||||||
|
|
||||||
|
我們相信,**開源的力量來自於每一個人的參與**!無論您是開發者、設計師還是用戶,都可以為 Butterfly 主題的發展貢獻力量。
|
||||||
|
|
||||||
|
### 💬 獲取幫助與支援
|
||||||
|
|
||||||
|
- 🐛 **發現問題?** → [GitHub Issues](https://github.com/jerryc127/hexo-theme-butterfly/issues) - 讓我們一起解決!
|
||||||
|
- 💡 **有好想法?** → [GitHub Discussions](https://github.com/jerryc127/hexo-theme-butterfly/discussions) - 分享您的創意想法!
|
||||||
|
- 📚 **學習使用?** → [官方文檔](https://butterfly.js.org/) - 詳細的使用指南
|
||||||
|
- 💬 **即時討論?** → [Telegram 群組](https://t.me/bu2fly) - 與社群成員實時交流
|
||||||
|
|
||||||
|
### 🎯 參與貢獻
|
||||||
|
|
||||||
|
想要讓 Butterfly 變得更好嗎?我們歡迎您的任何形式的貢獻:
|
||||||
|
|
||||||
|
- **🔧 代碼貢獻** - 修復 Bug、添加新功能、優化性能
|
||||||
|
- **📝 文檔完善** - 改進文檔、翻譯內容、撰寫教程
|
||||||
|
- **🎨 設計建議** - UI/UX 改進、主題配色、圖示設計
|
||||||
|
- **🧪 測試反饋** - 測試新功能、回報問題、提供使用體驗
|
||||||
|
- **💰 資金支援** - [贊助項目](https://buy.stripe.com/3cs6rP6YA91sbbG5kk) - 支持長期發展
|
||||||
|
|
||||||
|
## 📄 授權條款
|
||||||
|
|
||||||
|
本專案採用 [Apache 2.0](LICENSE) 授權條款。
|
||||||
|
|
||||||
|
## 🙏 致敬與感謝
|
||||||
|
|
||||||
|
本主題基於 [hexo-theme-melody](https://github.com/Molunerfinn/hexo-theme-melody) 進行開發,感謝原作者的精彩創作為我們提供了靈感與基礎!
|
||||||
|
|
||||||
|
感謝所有為 Butterfly 主題發展做出貢獻的朋友們,是你們的支持讓這個主題能夠不斷完善與進步。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
**✨ 如果這個主題對您有幫助,請給我們一個 ⭐ Star!✨**
|
||||||
|
</div>
|
||||||
|
|||||||
42
_config.yml
42
_config.yml
@@ -13,6 +13,7 @@ nav:
|
|||||||
# Navigation bar logo image
|
# Navigation bar logo image
|
||||||
logo:
|
logo:
|
||||||
display_title: true
|
display_title: true
|
||||||
|
display_post_title: true
|
||||||
# Whether to fix navigation bar
|
# Whether to fix navigation bar
|
||||||
fixed: false
|
fixed: false
|
||||||
|
|
||||||
@@ -158,7 +159,7 @@ subtitle:
|
|||||||
# Choose: false/1/2/3
|
# Choose: false/1/2/3
|
||||||
# false - disable the function
|
# false - disable the function
|
||||||
# 1 - hitokoto.cn
|
# 1 - hitokoto.cn
|
||||||
# 2 - yijuzhan.com
|
# 2 - https://api.aa1.cn/doc/yiyan.html
|
||||||
# 3 - jinrishici.com
|
# 3 - jinrishici.com
|
||||||
source: false
|
source: false
|
||||||
# If you close the typewriter effect, the subtitle will only show the first line of sub
|
# If you close the typewriter effect, the subtitle will only show the first line of sub
|
||||||
@@ -254,12 +255,15 @@ noticeOutdate:
|
|||||||
# Footer Settings
|
# Footer Settings
|
||||||
# --------------------------------------
|
# --------------------------------------
|
||||||
footer:
|
footer:
|
||||||
|
nav:
|
||||||
owner:
|
owner:
|
||||||
enable: true
|
enable: true
|
||||||
since: 2019
|
since: 2025
|
||||||
custom_text:
|
|
||||||
# Copyright of theme and framework
|
# Copyright of theme and framework
|
||||||
copyright: true
|
copyright:
|
||||||
|
enable: true
|
||||||
|
version: true
|
||||||
|
custom_text:
|
||||||
|
|
||||||
# --------------------------------------
|
# --------------------------------------
|
||||||
# Aside Settings
|
# Aside Settings
|
||||||
@@ -399,6 +403,9 @@ rightside_item_order:
|
|||||||
# Default: toc,chat,comment
|
# Default: toc,chat,comment
|
||||||
show:
|
show:
|
||||||
|
|
||||||
|
# Animation for the bottom right config button
|
||||||
|
rightside_config_animation: true
|
||||||
|
|
||||||
# --------------------------------------
|
# --------------------------------------
|
||||||
# Global Settings
|
# Global Settings
|
||||||
# --------------------------------------
|
# --------------------------------------
|
||||||
@@ -481,6 +488,11 @@ search:
|
|||||||
top_n_per_article: 1
|
top_n_per_article: 1
|
||||||
# Unescape html strings to the readable one.
|
# Unescape html strings to the readable one.
|
||||||
unescape: false
|
unescape: false
|
||||||
|
# Enable pagination for search results
|
||||||
|
pagination:
|
||||||
|
enable: false
|
||||||
|
# Number of search results per page
|
||||||
|
hitsPerPage: 8
|
||||||
CDN:
|
CDN:
|
||||||
|
|
||||||
# Docsearch
|
# Docsearch
|
||||||
@@ -503,12 +515,12 @@ share:
|
|||||||
# Share.js
|
# Share.js
|
||||||
# https://github.com/overtrue/share.js
|
# https://github.com/overtrue/share.js
|
||||||
sharejs:
|
sharejs:
|
||||||
sites: facebook,twitter,wechat,weibo,qq
|
sites: facebook,x,wechat,weibo,qq
|
||||||
|
|
||||||
# AddToAny
|
# AddToAny
|
||||||
# https://www.addtoany.com/
|
# https://www.addtoany.com/
|
||||||
addtoany:
|
addtoany:
|
||||||
item: facebook,twitter,wechat,sina_weibo,facebook_messenger,email,copy_link
|
item: facebook,x,wechat,sina_weibo,facebook_messenger,email,copy_link
|
||||||
|
|
||||||
# --------------------------------------
|
# --------------------------------------
|
||||||
# Comments System
|
# Comments System
|
||||||
@@ -687,6 +699,7 @@ umami_analytics:
|
|||||||
enable: false
|
enable: false
|
||||||
# For self-hosted setups, configure the hostname of the Umami instance
|
# For self-hosted setups, configure the hostname of the Umami instance
|
||||||
serverURL:
|
serverURL:
|
||||||
|
script_name: script.js
|
||||||
website_id:
|
website_id:
|
||||||
option:
|
option:
|
||||||
UV_PV:
|
UV_PV:
|
||||||
@@ -696,6 +709,12 @@ umami_analytics:
|
|||||||
# Umami Cloud (API key) / self-hosted Umami (token)
|
# Umami Cloud (API key) / self-hosted Umami (token)
|
||||||
token:
|
token:
|
||||||
|
|
||||||
|
# https://www.googletagmanager.com/
|
||||||
|
google_tag_manager:
|
||||||
|
tag_id:
|
||||||
|
# optional
|
||||||
|
domain:
|
||||||
|
|
||||||
# --------------------------------------
|
# --------------------------------------
|
||||||
# Advertisement
|
# Advertisement
|
||||||
# --------------------------------------
|
# --------------------------------------
|
||||||
@@ -987,6 +1006,8 @@ instantpage: false
|
|||||||
# https://github.com/verlok/vanilla-lazyload
|
# https://github.com/verlok/vanilla-lazyload
|
||||||
lazyload:
|
lazyload:
|
||||||
enable: false
|
enable: false
|
||||||
|
# Use browser's native lazyload instead of vanilla-lazyload
|
||||||
|
native: false
|
||||||
# Specify the field to use lazyload (site or post)
|
# Specify the field to use lazyload (site or post)
|
||||||
field: site
|
field: site
|
||||||
placeholder:
|
placeholder:
|
||||||
@@ -1018,7 +1039,11 @@ Open_Graph_meta:
|
|||||||
|
|
||||||
# Structured Data
|
# Structured Data
|
||||||
# https://developers.google.com/search/docs/guides/intro-structured-data
|
# https://developers.google.com/search/docs/guides/intro-structured-data
|
||||||
structured_data: true
|
structured_data:
|
||||||
|
enable: false
|
||||||
|
# Alternate name for the site, used in structured data
|
||||||
|
# Format: ['name1', 'name2']
|
||||||
|
alternate_name:
|
||||||
|
|
||||||
# Add the vendor prefixes to ensure compatibility
|
# Add the vendor prefixes to ensure compatibility
|
||||||
css_prefix: true
|
css_prefix: true
|
||||||
@@ -1042,7 +1067,7 @@ CDN:
|
|||||||
third_party_provider: jsdelivr
|
third_party_provider: jsdelivr
|
||||||
|
|
||||||
# Add version number to url, true or false
|
# Add version number to url, true or false
|
||||||
version: false
|
version: true
|
||||||
|
|
||||||
# Custom format
|
# Custom format
|
||||||
# For example: https://cdn.staticfile.org/${cdnjs_name}/${version}/${min_cdnjs_file}
|
# For example: https://cdn.staticfile.org/${cdnjs_name}/${version}/${min_cdnjs_file}
|
||||||
@@ -1078,7 +1103,6 @@ CDN:
|
|||||||
# gitalk_css:
|
# gitalk_css:
|
||||||
# giscus:
|
# giscus:
|
||||||
# instantpage:
|
# instantpage:
|
||||||
# instantsearch:
|
|
||||||
# katex:
|
# katex:
|
||||||
# katex_copytex:
|
# katex_copytex:
|
||||||
# lazyload:
|
# lazyload:
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ post:
|
|||||||
copyright_content: 'All articles on this blog are licensed under <a href="%s">%s</a> unless otherwise stated.'
|
copyright_content: 'All articles on this blog are licensed under <a href="%s">%s</a> unless otherwise stated.'
|
||||||
recommend: Related Articles
|
recommend: Related Articles
|
||||||
edit: Edit
|
edit: Edit
|
||||||
|
back_to_home: Back to Home
|
||||||
|
|
||||||
search:
|
search:
|
||||||
title: Search
|
title: Search
|
||||||
@@ -47,6 +48,7 @@ search:
|
|||||||
pagination:
|
pagination:
|
||||||
prev: Previous
|
prev: Previous
|
||||||
next: Next
|
next: Next
|
||||||
|
page_info: 'Page ${current} of ${total}'
|
||||||
|
|
||||||
comment: Comments
|
comment: Comments
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ post:
|
|||||||
copyright_content: 'All articles on this blog are licensed under <a href="%s">%s</a> unless otherwise stated.'
|
copyright_content: 'All articles on this blog are licensed under <a href="%s">%s</a> unless otherwise stated.'
|
||||||
recommend: Related Articles
|
recommend: Related Articles
|
||||||
edit: Edit
|
edit: Edit
|
||||||
|
back_to_home: Back to Home
|
||||||
|
|
||||||
search:
|
search:
|
||||||
title: Search
|
title: Search
|
||||||
@@ -47,6 +48,7 @@ search:
|
|||||||
pagination:
|
pagination:
|
||||||
prev: Previous
|
prev: Previous
|
||||||
next: Next
|
next: Next
|
||||||
|
page_info: 'Page ${current} of ${total}'
|
||||||
|
|
||||||
comment: Comments
|
comment: Comments
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ post:
|
|||||||
copyright_content: 'このブログのすべての記事は、<a href="%s">%s</a> ライセンスの下で提供されており、特に明記されていない限り、すべての権利を留保します。転載時には出典を明記してください: <a href="%s">%s</a>。'
|
copyright_content: 'このブログのすべての記事は、<a href="%s">%s</a> ライセンスの下で提供されており、特に明記されていない限り、すべての権利を留保します。転載時には出典を明記してください: <a href="%s">%s</a>。'
|
||||||
recommend: 関連記事
|
recommend: 関連記事
|
||||||
edit: 編集
|
edit: 編集
|
||||||
|
back_to_home: ホームに戻る
|
||||||
|
|
||||||
search:
|
search:
|
||||||
title: 検索
|
title: 検索
|
||||||
@@ -47,6 +48,7 @@ search:
|
|||||||
pagination:
|
pagination:
|
||||||
prev: 前へ
|
prev: 前へ
|
||||||
next: 次へ
|
next: 次へ
|
||||||
|
page_info: '${current} ページ / 合計 ${total} ページ'
|
||||||
|
|
||||||
comment: コメント
|
comment: コメント
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ post:
|
|||||||
copyright_content: '이 블로그의 모든 글은 <a href="%s">%s</a> 라이선스를 따르며, 별도로 명시되지 않는 한 모든 권리를 보유합니다. 재배포 시 출처를 명시해 주세요: <a href="%s">%s</a>.'
|
copyright_content: '이 블로그의 모든 글은 <a href="%s">%s</a> 라이선스를 따르며, 별도로 명시되지 않는 한 모든 권리를 보유합니다. 재배포 시 출처를 명시해 주세요: <a href="%s">%s</a>.'
|
||||||
recommend: 관련 글
|
recommend: 관련 글
|
||||||
edit: 편집
|
edit: 편집
|
||||||
|
back_to_home: 홈으로 돌아가기
|
||||||
|
|
||||||
search:
|
search:
|
||||||
title: 검색
|
title: 검색
|
||||||
@@ -47,6 +48,7 @@ search:
|
|||||||
pagination:
|
pagination:
|
||||||
prev: 이전
|
prev: 이전
|
||||||
next: 다음
|
next: 다음
|
||||||
|
page_info: '${current} 페이지 / 총 ${total} 페이지'
|
||||||
|
|
||||||
comment: 댓글
|
comment: 댓글
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ post:
|
|||||||
<a href="%s" target="_blank">%s</a> 许可协议。转载请注明来源 <a href="%s" target="_blank">%s</a>!'
|
<a href="%s" target="_blank">%s</a> 许可协议。转载请注明来源 <a href="%s" target="_blank">%s</a>!'
|
||||||
recommend: 相关推荐
|
recommend: 相关推荐
|
||||||
edit: 编辑
|
edit: 编辑
|
||||||
|
back_to_home: 返回首页
|
||||||
|
|
||||||
search:
|
search:
|
||||||
title: 搜索
|
title: 搜索
|
||||||
@@ -48,6 +49,7 @@ search:
|
|||||||
pagination:
|
pagination:
|
||||||
prev: 上一篇
|
prev: 上一篇
|
||||||
next: 下一篇
|
next: 下一篇
|
||||||
|
page_info: '第 ${current} 页 / 共 ${total} 页'
|
||||||
|
|
||||||
comment: 评论
|
comment: 评论
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ post:
|
|||||||
copyright_content: '除特別聲明外,本博客所有文章均採用<a href="%s">%s</a> 授權協議。轉載請註明出處:<a href="%s">%s</a>。'
|
copyright_content: '除特別聲明外,本博客所有文章均採用<a href="%s">%s</a> 授權協議。轉載請註明出處:<a href="%s">%s</a>。'
|
||||||
recommend: 相關文章
|
recommend: 相關文章
|
||||||
edit: 編輯
|
edit: 編輯
|
||||||
|
back_to_home: 返回首頁
|
||||||
|
|
||||||
search:
|
search:
|
||||||
title: 搜尋
|
title: 搜尋
|
||||||
@@ -47,6 +48,7 @@ search:
|
|||||||
pagination:
|
pagination:
|
||||||
prev: 上一頁
|
prev: 上一頁
|
||||||
next: 下一頁
|
next: 下一頁
|
||||||
|
page_info: '第 ${current} 頁 / 共 ${total} 頁'
|
||||||
|
|
||||||
comment: 評論
|
comment: 評論
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ post:
|
|||||||
copyright_content: '本部落格所有文章除特別聲明外,均採用<a href="%s" target="_blank">%s</a> 授權協議。轉載請註明來源 <a href="%s" target="_blank">%s</a>!'
|
copyright_content: '本部落格所有文章除特別聲明外,均採用<a href="%s" target="_blank">%s</a> 授權協議。轉載請註明來源 <a href="%s" target="_blank">%s</a>!'
|
||||||
recommend: 相關推薦
|
recommend: 相關推薦
|
||||||
edit: 編輯
|
edit: 編輯
|
||||||
|
back_to_home: 返回首頁
|
||||||
|
|
||||||
search:
|
search:
|
||||||
title: 搜尋
|
title: 搜尋
|
||||||
@@ -47,6 +48,7 @@ search:
|
|||||||
pagination:
|
pagination:
|
||||||
prev: 上一篇
|
prev: 上一篇
|
||||||
next: 下一篇
|
next: 下一篇
|
||||||
|
page_info: '第 ${current} 頁 / 共 ${total} 頁'
|
||||||
|
|
||||||
comment: 評論
|
comment: 評論
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ div
|
|||||||
!= partial("includes/third-party/umami_analytics", {}, { cache: true })
|
!= partial("includes/third-party/umami_analytics", {}, { cache: true })
|
||||||
|
|
||||||
if theme.busuanzi.site_uv || theme.busuanzi.site_pv || theme.busuanzi.page_pv
|
if theme.busuanzi.site_uv || theme.busuanzi.site_pv || theme.busuanzi.page_pv
|
||||||
script(async data-pjax src= theme.asset.busuanzi || '//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js')
|
script(async data-pjax src=url_for(theme.asset.busuanzi) || '//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js')
|
||||||
|
|
||||||
!= partial('includes/third-party/search/index', {}, { cache: true })
|
!= partial('includes/third-party/search/index', {}, { cache: true })
|
||||||
|
|
||||||
|
if theme.google_tag_manager && theme.google_tag_manager.tag_id
|
||||||
|
noscript
|
||||||
|
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")
|
||||||
@@ -1,19 +1,39 @@
|
|||||||
#footer-wrap
|
- const { nav, owner, copyright, custom_text } = theme.footer
|
||||||
if theme.footer.owner.enable
|
|
||||||
- const currentYear = new Date().getFullYear()
|
if nav
|
||||||
- const sinceYear = theme.footer.owner.since
|
.footer-flex
|
||||||
.copyright
|
for block in nav
|
||||||
if sinceYear && sinceYear != currentYear
|
.footer-flex-items(style=`${ block.width ? 'flex-grow:' + block.width : '' }`)
|
||||||
!= `©${sinceYear} - ${currentYear} By ${config.author}`
|
for blockItem in block.content
|
||||||
else
|
.footer-flex-item
|
||||||
!= `©${currentYear} By ${config.author}`
|
.footer-flex-title= blockItem.title
|
||||||
if theme.footer.copyright
|
.footer-flex-content
|
||||||
- const v = getVersion()
|
for subitem in blockItem.item
|
||||||
.framework-info
|
if subitem.html
|
||||||
span= _p('footer.framework') + ' '
|
div!= subitem.html
|
||||||
a(href='https://hexo.io')= `Hexo ${v.hexo}`
|
else if subitem.url
|
||||||
span.footer-separator |
|
a(href=url_for(subitem.url), target='_blank' title=subitem.title)= subitem.title
|
||||||
span= _p('footer.theme') + ' '
|
else if subitem.title
|
||||||
a(href='https://github.com/jerryc127/hexo-theme-butterfly')= `Butterfly ${v.theme}`
|
div!= subitem.title
|
||||||
|
.footer-other
|
||||||
|
.footer-copyright
|
||||||
|
if owner.enable
|
||||||
|
- const currentYear = new Date().getFullYear()
|
||||||
|
- const sinceYear = owner.since
|
||||||
|
span.copyright
|
||||||
|
if sinceYear && sinceYear != currentYear
|
||||||
|
!= `© ${sinceYear} - ${currentYear} By ${config.author}`
|
||||||
|
else
|
||||||
|
!= `© ${currentYear} By ${config.author}`
|
||||||
|
if copyright.enable
|
||||||
|
- const v = copyright.version ? getVersion() : false
|
||||||
|
span.framework-info
|
||||||
|
if owner.enable && nav
|
||||||
|
span.footer-separator |
|
||||||
|
span= _p('footer.framework') + ' '
|
||||||
|
a(href='https://hexo.io')= `Hexo${ v ? ' ' + v.hexo : '' }`
|
||||||
|
span.footer-separator |
|
||||||
|
span= _p('footer.theme') + ' '
|
||||||
|
a(href='https://github.com/jerryc127/hexo-theme-butterfly')= `Butterfly${ v ? ' ' + v.theme : '' }`
|
||||||
if theme.footer.custom_text
|
if theme.footer.custom_text
|
||||||
.footer_custom_text!= theme.footer.custom_text
|
.footer_custom_text!= theme.footer.custom_text
|
||||||
|
|||||||
@@ -31,4 +31,15 @@ if theme.microsoft_clarity
|
|||||||
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
|
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
|
||||||
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
|
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
|
||||||
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
|
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
|
||||||
})(window, document, "clarity", "script", "!{theme.microsoft_clarity}");
|
})(window, document, "clarity", "script", "!{theme.microsoft_clarity}");
|
||||||
|
|
||||||
|
if (theme.google_tag_manager && theme.google_tag_manager.tag_id)
|
||||||
|
script.
|
||||||
|
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||||
|
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||||
|
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||||
|
"!{theme.google_tag_manager.domain ? theme.google_tag_manager.domain : 'https://www.googletagmanager.com'}/gtm.js?id="+i+dl;f.parentNode.insertBefore(j,f);
|
||||||
|
})(window,document,'script','dataLayer','!{theme.google_tag_manager.tag_id}');
|
||||||
|
btf.addGlobalFn('pjaxComplete', () => {
|
||||||
|
dataLayer.push({'event': 'pjaxComplete', 'page_title': document.title, 'page_location': location.href, 'page_path': window.location.pathname})
|
||||||
|
}, 'google_tag_manager')
|
||||||
@@ -10,7 +10,6 @@
|
|||||||
hitsPerPage: theme.search.algolia_search.hitsPerPage,
|
hitsPerPage: theme.search.algolia_search.hitsPerPage,
|
||||||
// search languages
|
// search languages
|
||||||
languages: {
|
languages: {
|
||||||
input_placeholder: theme.search.placeholder || _p("search.input_placeholder"),
|
|
||||||
hits_empty: _p("search.algolia_search.hits_empty"),
|
hits_empty: _p("search.algolia_search.hits_empty"),
|
||||||
hits_stats: _p("search.algolia_search.hits_stats"),
|
hits_stats: _p("search.algolia_search.hits_stats"),
|
||||||
}
|
}
|
||||||
@@ -19,12 +18,16 @@
|
|||||||
|
|
||||||
let localSearch = 'undefined'
|
let localSearch = 'undefined'
|
||||||
if (theme.search.use === 'local_search') {
|
if (theme.search.use === 'local_search') {
|
||||||
const { CDN, preload, top_n_per_article, unescape } = theme.search.local_search
|
const { CDN, preload, top_n_per_article, pagination, unescape } = theme.search.local_search
|
||||||
localSearch = JSON.stringify({
|
localSearch = JSON.stringify({
|
||||||
path: CDN || config.root + config.search.path,
|
path: CDN || config.root + config.search.path,
|
||||||
preload,
|
preload,
|
||||||
top_n_per_article,
|
top_n_per_article,
|
||||||
unescape,
|
unescape,
|
||||||
|
pagination: {
|
||||||
|
enable: pagination.enable,
|
||||||
|
hitsPerPage: pagination.hitsPerPage
|
||||||
|
},
|
||||||
languages: {
|
languages: {
|
||||||
// search languages
|
// search languages
|
||||||
hits_empty: _p("search.local_search.hits_empty"),
|
hits_empty: _p("search.local_search.hits_empty"),
|
||||||
|
|||||||
@@ -1,34 +1,67 @@
|
|||||||
if theme.structured_data && page.layout === 'post'
|
if theme.structured_data
|
||||||
-
|
if page.layout === 'post'
|
||||||
// use json-ld to add structured data
|
-
|
||||||
|
// https://developers.google.com/search/docs/appearance/structured-data/article
|
||||||
|
|
||||||
const title = page.title
|
const title = page.title
|
||||||
const url = page.permalink
|
const url = page.permalink
|
||||||
const imageVal = page.cover_type === 'img' ? page.cover : theme.avatar.img
|
const imageVal = page.cover_type === 'img' ? page.cover : theme.avatar.img
|
||||||
const image = imageVal ? full_url_for(imageVal) : ''
|
const image = imageVal ? full_url_for(imageVal) : ''
|
||||||
const datePublished = page.date.toISOString()
|
const datePublished = page.date.toISOString()
|
||||||
const dateModified = (page.updated || page.date).toISOString()
|
const dateModified = (page.updated || page.date).toISOString()
|
||||||
const author = page.copyright_author || config.author
|
const author = page.copyright_author || config.author
|
||||||
const authorHrefVal = page.copyright_author_href || theme.post_copyright.author_href || site.url;
|
const authorHrefVal = page.copyright_author_href || theme.post_copyright.author_href || config.url
|
||||||
const authorHref = full_url_for(authorHrefVal);
|
const authorHref = full_url_for(authorHrefVal)
|
||||||
|
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
"@context": "https://schema.org",
|
"@context": "https://schema.org",
|
||||||
"@type": "BlogPosting",
|
"@type": "BlogPosting",
|
||||||
"headline": title,
|
"headline": title,
|
||||||
"url": url,
|
"url": url,
|
||||||
"image": image,
|
"image": image,
|
||||||
"datePublished": datePublished,
|
"datePublished": datePublished,
|
||||||
"dateModified": dateModified,
|
"dateModified": dateModified,
|
||||||
"author": [{
|
"author": [{
|
||||||
"@type": "Person",
|
"@type": "Person",
|
||||||
"name": author,
|
"name": author,
|
||||||
"url": authorHref
|
"url": authorHref
|
||||||
}]
|
}]
|
||||||
};
|
}
|
||||||
|
|
||||||
jsonLdScript = JSON.stringify(jsonLd, null, 2);
|
jsonLdScript = JSON.stringify(jsonLd, null, 2)
|
||||||
-
|
-
|
||||||
|
|
||||||
|
else if is_home() && (!page.current || page.current === 1)
|
||||||
|
-
|
||||||
|
// https://developers.google.com/search/docs/appearance/site-names#website
|
||||||
|
|
||||||
|
const baseUrl = config.url;
|
||||||
|
const currentPath = url_for('/');
|
||||||
|
const isRootOrSubdomain = currentPath.split('/').filter(Boolean).length === 0;
|
||||||
|
|
||||||
|
if (isRootOrSubdomain) {
|
||||||
|
const domain = new URL(config.url).hostname;
|
||||||
|
const alternateNames = theme.structured_data.alternate_name || [];
|
||||||
|
|
||||||
|
if (config.subtitle) {
|
||||||
|
alternateNames.push(config.subtitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (domain) {
|
||||||
|
alternateNames.push(domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
const jsonLd = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "WebSite",
|
||||||
|
"name": config.title,
|
||||||
|
"alternateName": alternateNames,
|
||||||
|
"url": full_url_for('/'),
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonLdScript = JSON.stringify(jsonLd, null, 2)
|
||||||
|
}
|
||||||
|
-
|
||||||
|
|
||||||
script(type="application/ld+json").
|
script(type="application/ld+json").
|
||||||
!{jsonLdScript}
|
!{jsonLdScript}
|
||||||
|
|||||||
@@ -5,9 +5,13 @@ nav#nav
|
|||||||
img.site-icon(src=url_for(theme.nav.logo) alt='Logo')
|
img.site-icon(src=url_for(theme.nav.logo) alt='Logo')
|
||||||
if theme.nav.display_title
|
if theme.nav.display_title
|
||||||
span.site-name=config.title
|
span.site-name=config.title
|
||||||
if globalPageType === 'post'
|
if globalPageType === 'post' && theme.nav.display_post_title
|
||||||
a.nav-page-title(href=url_for('/'))
|
a.nav-page-title(href=url_for('/'))
|
||||||
span.site-name=(page.title || config.title)
|
span.site-name=(page.title || config.title)
|
||||||
|
span.site-name
|
||||||
|
i.fa-solid.fa-circle-arrow-left
|
||||||
|
span= ' ' + _p('post.back_to_home')
|
||||||
|
|
||||||
#menus
|
#menus
|
||||||
if theme.search.use
|
if theme.search.use
|
||||||
#search-button
|
#search-button
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ script.
|
|||||||
const $body = document.body
|
const $body = document.body
|
||||||
const preloader = {
|
const preloader = {
|
||||||
endLoading: () => {
|
endLoading: () => {
|
||||||
|
if ($loadingBox.classList.contains('loaded')) return
|
||||||
$body.style.overflow = ''
|
$body.style.overflow = ''
|
||||||
$loadingBox.classList.add('loaded')
|
$loadingBox.classList.add('loaded')
|
||||||
},
|
},
|
||||||
@@ -24,7 +25,15 @@ script.
|
|||||||
}
|
}
|
||||||
|
|
||||||
preloader.initLoading()
|
preloader.initLoading()
|
||||||
window.addEventListener('load', preloader.endLoading)
|
|
||||||
|
if (document.readyState === 'complete') {
|
||||||
|
preloader.endLoading()
|
||||||
|
} else {
|
||||||
|
window.addEventListener('load', preloader.endLoading)
|
||||||
|
document.addEventListener('DOMContentLoaded', preloader.endLoading)
|
||||||
|
// Add timeout protection: force end after 7 seconds
|
||||||
|
setTimeout(preloader.endLoading, 7000)
|
||||||
|
}
|
||||||
|
|
||||||
if (!{theme.pjax && theme.pjax.enable}) {
|
if (!{theme.pjax && theme.pjax.enable}) {
|
||||||
btf.addGlobalFn('pjaxSend', preloader.initLoading, 'preloader_init')
|
btf.addGlobalFn('pjaxSend', preloader.initLoading, 'preloader_init')
|
||||||
|
|||||||
@@ -9,40 +9,47 @@
|
|||||||
- page.toc = false
|
- page.toc = false
|
||||||
|
|
||||||
#article-container
|
#article-container
|
||||||
if page.comments !== false && theme.comments.use
|
if page.shuoshuo_url || (site.data.shuoshuo && site.data.shuoshuo.length)
|
||||||
- commentsJsLoad = true
|
if page.comments !== false && theme.comments.use
|
||||||
|
- commentsJsLoad = true
|
||||||
|
|
||||||
script.
|
script.
|
||||||
(() => {
|
(() => {
|
||||||
const commentDiv = `!{partial('includes/third-party/comments/index', {}, {cache: true})}`
|
const commentDiv = `!{partial('includes/third-party/comments/index', {}, {cache: true})}`
|
||||||
|
|
||||||
const runDestroy = (shuoshuoComment) => {
|
const runDestroy = (shuoshuoComment) => {
|
||||||
if (!shuoshuoComment) return
|
if (!shuoshuoComment) return
|
||||||
|
|
||||||
for (const [key, fn] of Object.entries(shuoshuoComment)) {
|
for (const [key, fn] of Object.entries(shuoshuoComment)) {
|
||||||
if (key.startsWith('destroy')) fn()
|
if (key.startsWith('destroy')) fn()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
window.addCommentToShuoshuo = e => {
|
window.addCommentToShuoshuo = e => {
|
||||||
const btn = e.target.closest('.shuoshuo-comment-btn')
|
const btn = e.target.closest('.shuoshuo-comment-btn')
|
||||||
if (!btn) return
|
if (!btn) return
|
||||||
|
|
||||||
const ele = btn.closest('.container').nextElementSibling
|
const ele = btn.closest('.container').nextElementSibling
|
||||||
const { shuoshuoComment } = window
|
const { shuoshuoComment } = window
|
||||||
const isInclude = ele.classList.contains('no-comment')
|
const isInclude = ele.classList.contains('no-comment')
|
||||||
runDestroy(shuoshuoComment)
|
runDestroy(shuoshuoComment)
|
||||||
if (isInclude) {
|
if (isInclude) {
|
||||||
ele.classList.remove('no-comment')
|
ele.classList.remove('no-comment')
|
||||||
ele.innerHTML = commentDiv
|
ele.innerHTML = commentDiv
|
||||||
const key = `${location.pathname.replace(/\/$/, '')}?key=${ele.getAttribute('data-key')}`
|
const key = `${location.pathname.replace(/\/$/, '')}?key=${ele.getAttribute('data-key')}`
|
||||||
btf.switchComments(ele, key)
|
btf.switchComments(ele, key)
|
||||||
shuoshuoComment.loadComment && shuoshuoComment.loadComment(ele, key)
|
shuoshuoComment.loadComment && shuoshuoComment.loadComment(ele, key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})()
|
||||||
})()
|
|
||||||
|
|
||||||
if page.shuoshuo_url
|
|
||||||
|
- const localDate = page.shuoshuo_url ? [] : shuoshuoFN(site.data.shuoshuo, page)
|
||||||
|
|
||||||
|
if !page.shuoshuo_url
|
||||||
|
script(type='application/json' id='shuoshuo-data')!= safeJSON(localDate)
|
||||||
|
|
||||||
|
- const { enable, native, placeholder, field } = theme.lazyload
|
||||||
script.
|
script.
|
||||||
(() => {
|
(() => {
|
||||||
const limitConfig = !{ JSON.stringify(page.limit || {}) }
|
const limitConfig = !{ JSON.stringify(page.limit || {}) }
|
||||||
@@ -78,111 +85,248 @@
|
|||||||
return `${year}-${month}-${day} ${hour}:${minute}:${second}`
|
return `${year}-${month}-${day} ${hour}:${minute}:${second}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const addLazyload = str => {
|
||||||
|
const config = {
|
||||||
|
enable: !{Boolean(enable)},
|
||||||
|
native: !{Boolean(native)},
|
||||||
|
field: '!{field}',
|
||||||
|
placeholder: '!{url_for(placeholder)}',
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.enable || config.field !== 'site') return str
|
||||||
|
const parser = new DOMParser()
|
||||||
|
const doc = parser.parseFromString(str, 'text/html')
|
||||||
|
const images = doc.querySelectorAll('img')
|
||||||
|
|
||||||
|
images.forEach(img => {
|
||||||
|
if (config.native) {
|
||||||
|
img.setAttribute('loading', 'lazy')
|
||||||
|
} else {
|
||||||
|
const src = img.getAttribute('src')
|
||||||
|
img.setAttribute('data-lazy-src', src)
|
||||||
|
|
||||||
|
if (config.placeholder) {
|
||||||
|
img.setAttribute('src', config.placeholder)
|
||||||
|
} else {
|
||||||
|
img.removeAttribute('src')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return doc.body.innerHTML
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentPage = 1
|
||||||
|
const itemsPerPage = 8
|
||||||
|
let totalPages = 0
|
||||||
|
let data = []
|
||||||
|
let inputEventsAttached = false // Flag to mark if input event listeners have been added
|
||||||
|
|
||||||
|
const renderData = (dataSlice) => {
|
||||||
|
const content = dataSlice.map(item => {
|
||||||
|
const formattedDate = formatToTimeZone(item.date)
|
||||||
|
const tags = item.tags && item.tags.map(tag => `<span class="shuoshuo-tag">${tag}</span>`).join('') || ''
|
||||||
|
const commentButton = item.key && !{commentsJsLoad}
|
||||||
|
? `<div class="shuoshuo-comment-btn" onclick="addCommentToShuoshuo(event)">
|
||||||
|
<i class="fa-solid fa-comments"></i>
|
||||||
|
</div>`
|
||||||
|
: ''
|
||||||
|
const commentContainer = item.key
|
||||||
|
? `<div class="shuoshuo-comment no-comment" data-key="${item.key}"></div>`
|
||||||
|
: ''
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="shuoshuo-item">
|
||||||
|
<div class="container">
|
||||||
|
<div class="shuoshuo-item-header">
|
||||||
|
<div class="shuoshuo-avatar">
|
||||||
|
<img class="no-lightbox" src="${item.avatar || '!{url_for(theme.avatar.img)}'}">
|
||||||
|
</div>
|
||||||
|
<div class="shuoshuo-info">
|
||||||
|
<div class="shuoshuo-author">${item.author || '!{config.author}'}</div>
|
||||||
|
<time class="shuoshuo-date" title="${formattedDate}">
|
||||||
|
${btf.diffDate(formattedDate, true)}
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="shuoshuo-content">${addLazyload(item.content)}</div>
|
||||||
|
<div class="shuoshuo-footer ${tags ? 'flex-between' : 'flex-end'}">
|
||||||
|
${tags ? `<div class="shuoshuo-tags">${tags}</div>` : ''}
|
||||||
|
${commentButton}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
${commentContainer}
|
||||||
|
</div>`
|
||||||
|
}).join('')
|
||||||
|
|
||||||
|
const container = document.getElementById('article-container')
|
||||||
|
container.innerHTML = content
|
||||||
|
|
||||||
|
window.lazyLoadInstance && window.lazyLoadInstance.update()
|
||||||
|
btf.loadLightbox(document.querySelectorAll('#article-container img:not(.no-lightbox)'))
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderNavigation = () => {
|
||||||
|
const container = document.getElementById('article-container')
|
||||||
|
const existingNav = container.nextElementSibling
|
||||||
|
if (existingNav && existingNav.classList.contains('shuoshuo-navigation')) {
|
||||||
|
existingNav.remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
const pageInfoTemplate = '#{__('pagination.page_info')}'
|
||||||
|
const pageInfoText = pageInfoTemplate
|
||||||
|
.replace(/\$\{current}/g, currentPage)
|
||||||
|
.replace(/\$\{total}/g, totalPages)
|
||||||
|
|
||||||
|
const navHtml = `
|
||||||
|
<div class="shuoshuo-navigation">
|
||||||
|
<button onclick="window.shuoshuoPrevPage()" ${currentPage === 1 ? 'disabled' : ''}><i class="fa-solid fa-chevron-left"></i></button>
|
||||||
|
<span class="shuoshuo-page-info">${pageInfoText}</span>
|
||||||
|
<input type="number" class="shuoshuo-page-input" min="1" max="${totalPages}" placeholder="${currentPage}" onkeydown="window.shuoshuoHandleKeyDown(event)">
|
||||||
|
<button onclick="window.shuoshuoNextPage()" ${currentPage === totalPages ? 'disabled' : ''}><i class="fa-solid fa-chevron-right"></i></button>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
container.insertAdjacentHTML('afterend', navHtml)
|
||||||
|
|
||||||
|
// Add input validation event listeners (only once)
|
||||||
|
if (!inputEventsAttached) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const input = document.querySelector('.shuoshuo-page-input')
|
||||||
|
if (input) {
|
||||||
|
// Clear placeholder when clicking the input box
|
||||||
|
input.addEventListener('focus', (event) => {
|
||||||
|
event.target.placeholder = ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// Restore placeholder if no content when losing focus
|
||||||
|
input.addEventListener('blur', (event) => {
|
||||||
|
if (!event.target.value.trim()) {
|
||||||
|
event.target.placeholder = currentPage
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
input.addEventListener('input', (event) => {
|
||||||
|
const value = parseInt(event.target.value) || 0
|
||||||
|
let wasInvalid = false
|
||||||
|
|
||||||
|
if (value > totalPages) {
|
||||||
|
event.target.value = totalPages
|
||||||
|
wasInvalid = true
|
||||||
|
} else if (value < 1 && event.target.value !== '') {
|
||||||
|
event.target.value = 1
|
||||||
|
wasInvalid = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// If value is corrected, show red and shake effect
|
||||||
|
if (wasInvalid) {
|
||||||
|
event.target.classList.add('invalid')
|
||||||
|
setTimeout(() => {
|
||||||
|
event.target.classList.remove('invalid')
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
inputEventsAttached = true // Mark that event listeners have been added
|
||||||
|
}
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderPage = (page) => {
|
||||||
|
const start = (page - 1) * itemsPerPage
|
||||||
|
const end = start + itemsPerPage
|
||||||
|
const pageData = data.slice(start, end)
|
||||||
|
renderData(pageData)
|
||||||
|
renderNavigation()
|
||||||
|
}
|
||||||
|
|
||||||
|
window.shuoshuoPrevPage = () => {
|
||||||
|
if (currentPage > 1) {
|
||||||
|
currentPage--
|
||||||
|
renderPage(currentPage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.shuoshuoNextPage = () => {
|
||||||
|
if (currentPage < totalPages) {
|
||||||
|
currentPage++
|
||||||
|
renderPage(currentPage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.shuoshuoGoToPage = (page) => {
|
||||||
|
if (typeof page === 'number') {
|
||||||
|
// Directly jump to the specified page
|
||||||
|
if (page >= 1 && page <= totalPages && page !== currentPage) {
|
||||||
|
currentPage = page
|
||||||
|
renderPage(currentPage)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Get page from input box
|
||||||
|
const input = document.querySelector('.shuoshuo-page-input')
|
||||||
|
const inputValue = input.value.trim()
|
||||||
|
const inputPage = inputValue === '' ? currentPage : parseInt(inputValue)
|
||||||
|
if (inputPage >= 1 && inputPage <= totalPages && inputPage !== currentPage) {
|
||||||
|
currentPage = inputPage
|
||||||
|
renderPage(currentPage)
|
||||||
|
} else if (inputValue === '') {
|
||||||
|
// If input box is empty, re-render current page (update placeholder)
|
||||||
|
renderPage(currentPage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.shuoshuoHandleKeyDown = (event) => {
|
||||||
|
const input = event.target
|
||||||
|
const value = input.value + event.key
|
||||||
|
|
||||||
|
// Allow delete, arrow keys, backspace, etc.
|
||||||
|
if (event.key === 'Enter' || event.key === 'Backspace' || event.key === 'Delete' ||
|
||||||
|
event.key === 'ArrowLeft' || event.key === 'ArrowRight' ||
|
||||||
|
event.key === 'Tab' || event.ctrlKey || event.metaKey) {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
window.shuoshuoGoToPage()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only allow numbers
|
||||||
|
if (!/^\d$/.test(event.key)) {
|
||||||
|
event.preventDefault()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the value after input exceeds the range
|
||||||
|
const newValue = parseInt(value) || 0
|
||||||
|
if (newValue > totalPages || (value.length > 1 && newValue === 0)) {
|
||||||
|
event.preventDefault()
|
||||||
|
// Add red and shake effect
|
||||||
|
input.classList.add('invalid')
|
||||||
|
setTimeout(() => {
|
||||||
|
input.classList.remove('invalid')
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const loadShuoshuo = async () => {
|
const loadShuoshuo = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('!{url_for(page.shuoshuo_url)}')
|
let originData = []
|
||||||
let data = await response.json()
|
if (!{Boolean(page.shuoshuo_url)}) {
|
||||||
|
const response = await fetch('!{url_for(page.shuoshuo_url)}')
|
||||||
data = filterDataByLimit(sortDataByDate(data), limitConfig)
|
originData = await response.json()
|
||||||
|
} else {
|
||||||
const container = document.getElementById('article-container')
|
const dataElement = document.getElementById('shuoshuo-data')
|
||||||
let start = 0
|
originData = dataElement ? JSON.parse(dataElement.textContent) : []
|
||||||
|
|
||||||
const renderData = (dataSlice) => {
|
|
||||||
const content = dataSlice.map(item => {
|
|
||||||
const formattedDate = formatToTimeZone(item.date)
|
|
||||||
const tags = item.tags && item.tags.map(tag => `<span class="shuoshuo-tag">${tag}</span>`).join('') || ''
|
|
||||||
const commentButton = item.key && !{commentsJsLoad}
|
|
||||||
? `<div class="shuoshuo-comment-btn" onclick="addCommentToShuoshuo(event)">
|
|
||||||
<i class="fa-solid fa-comments"></i>
|
|
||||||
</div>`
|
|
||||||
: ''
|
|
||||||
const commentContainer = item.key
|
|
||||||
? `<div class="shuoshuo-comment no-comment" data-key="${item.key}"></div>`
|
|
||||||
: ''
|
|
||||||
|
|
||||||
return `
|
|
||||||
<div class="shuoshuo-item">
|
|
||||||
<div class="container">
|
|
||||||
<div class="shuoshuo-item-header">
|
|
||||||
<div class="shuoshuo-avatar">
|
|
||||||
<img class="no-lightbox" src="${item.avatar || '!{url_for(theme.avatar.img)}'}">
|
|
||||||
</div>
|
|
||||||
<div class="shuoshuo-info">
|
|
||||||
<div class="shuoshuo-author">${item.author || '!{config.author}'}</div>
|
|
||||||
<time class="shuoshuo-date" title="${formattedDate}">
|
|
||||||
${btf.diffDate(formattedDate, true)}
|
|
||||||
</time>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="shuoshuo-content">${item.content}</div>
|
|
||||||
<div class="shuoshuo-footer ${tags ? 'flex-between' : 'flex-end'}">
|
|
||||||
${tags ? `<div class="shuoshuo-tags">${tags}</div>` : ''}
|
|
||||||
${commentButton}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
${commentContainer}
|
|
||||||
</div>`
|
|
||||||
}).join('')
|
|
||||||
|
|
||||||
container.insertAdjacentHTML('beforeend', content)
|
|
||||||
|
|
||||||
window.lazyLoadInstance.update()
|
|
||||||
btf.loadLightbox(document.querySelectorAll('#article-container img:not(.no-lightbox)'))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleIntersection = (entries) => {
|
data = filterDataByLimit(sortDataByDate(originData), limitConfig)
|
||||||
if (!entries[0].isIntersecting) return
|
|
||||||
observer.unobserve(entries[0].target)
|
|
||||||
|
|
||||||
const slice = data.slice(start, start + 10)
|
totalPages = Math.ceil(data.length / itemsPerPage)
|
||||||
renderData(slice)
|
|
||||||
start += 10
|
|
||||||
|
|
||||||
if (start < data.length) {
|
renderPage(currentPage)
|
||||||
setTimeout(() => observer.observe(container.lastElementChild), 100)
|
|
||||||
} else {
|
|
||||||
observer.disconnect()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const observer = new IntersectionObserver(handleIntersection, {
|
|
||||||
root: null,
|
|
||||||
rootMargin: '0px',
|
|
||||||
threshold: 1.0
|
|
||||||
})
|
|
||||||
|
|
||||||
renderData(data.slice(start, 10))
|
|
||||||
start += 10
|
|
||||||
|
|
||||||
if (container.lastElementChild) observer.observe(container.lastElementChild)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.pjax ? loadShuoshuo() : window.addEventListener('load', loadShuoshuo)
|
window.pjax ? loadShuoshuo() : window.addEventListener('load', loadShuoshuo)
|
||||||
})()
|
})()
|
||||||
else
|
|
||||||
if site.data.shuoshuo
|
|
||||||
each i in shuoshuoFN(site.data.shuoshuo, page)
|
|
||||||
.shuoshuo-item
|
|
||||||
.container
|
|
||||||
.shuoshuo-item-header
|
|
||||||
.shuoshuo-avatar
|
|
||||||
img.no-lightbox(src=i.avatar || url_for(theme.avatar.img))
|
|
||||||
.shuoshuo-info
|
|
||||||
.shuoshuo-author=i.author || config.author
|
|
||||||
time.shuoshuo-date(title=i.date)=i.date
|
|
||||||
.shuoshuo-content
|
|
||||||
!=markdown(i.content)
|
|
||||||
.shuoshuo-footer(class=i.tags && i.tags.length ? 'flex-between' : 'flex-end')
|
|
||||||
if i.tags
|
|
||||||
.shuoshuo-tags
|
|
||||||
each tag in i.tags
|
|
||||||
span.shuoshuo-tag=tag
|
|
||||||
if i.key && commentsJsLoad
|
|
||||||
.shuoshuo-comment-btn(onclick='addCommentToShuoshuo(event)')
|
|
||||||
i.fa-solid.fa-comments
|
|
||||||
if i.key && commentsJsLoad
|
|
||||||
.shuoshuo-comment.no-comment(data-key=i.key)
|
|
||||||
@@ -1,37 +1,38 @@
|
|||||||
-
|
if page.total !== 1
|
||||||
var options = {
|
-
|
||||||
prev_text: '<i class="fas fa-chevron-left fa-fw"></i>',
|
var options = {
|
||||||
next_text: '<i class="fas fa-chevron-right fa-fw"></i>',
|
prev_text: '<i class="fas fa-chevron-left fa-fw"></i>',
|
||||||
mid_size: 1,
|
next_text: '<i class="fas fa-chevron-right fa-fw"></i>',
|
||||||
escape: false
|
mid_size: 1,
|
||||||
}
|
escape: false
|
||||||
|
}
|
||||||
|
|
||||||
if globalPageType === 'post'
|
if globalPageType === 'post'
|
||||||
- let paginationOrder = theme.post_pagination === 1 ? { prev: page.prev, next: page.next } : { prev: page.next, next: page.prev }
|
- let paginationOrder = theme.post_pagination === 2 ? { prev: page.prev, next: page.next } : { prev: page.next, next: page.prev }
|
||||||
|
|
||||||
nav#pagination.pagination-post
|
nav#pagination.pagination-post
|
||||||
each direction, key in paginationOrder
|
each direction, key in paginationOrder
|
||||||
if direction
|
if direction
|
||||||
- const getPostDesc = direction.postDesc || postDesc(direction)
|
- const getPostDesc = direction.postDesc || postDesc(direction)
|
||||||
- let className = key === 'prev' ? (paginationOrder.next ? '' : 'full-width') : (paginationOrder.prev ? '' : 'full-width')
|
- let className = key === 'prev' ? (paginationOrder.next ? '' : 'full-width') : (paginationOrder.prev ? '' : 'full-width')
|
||||||
- className = getPostDesc ? className : className + ' no-desc'
|
- className = getPostDesc ? className : className + ' no-desc'
|
||||||
|
|
||||||
a.pagination-related(class=className href=url_for(direction.path) title=direction.title)
|
a.pagination-related(class=className href=url_for(direction.path) title=direction.title)
|
||||||
if direction.cover_type === 'img'
|
if direction.cover_type === 'img'
|
||||||
img.cover(src=url_for(direction.cover) onerror=`onerror=null;src='${url_for(theme.error_img.post_page)}'` alt=`cover of ${key === 'prev' ? 'previous' : 'next'} post`)
|
img.cover(src=url_for(direction.cover) onerror=`onerror=null;src='${url_for(theme.error_img.post_page)}'` alt=`cover of ${key === 'prev' ? 'previous' : 'next'} post`)
|
||||||
else
|
else
|
||||||
.cover(style=`background: ${direction.cover || 'var(--default-bg-color)'}`)
|
.cover(style=`background: ${direction.cover || 'var(--default-bg-color)'}`)
|
||||||
|
|
||||||
.info(class=key === 'prev' ? '' : 'text-right')
|
.info(class=key === 'prev' ? '' : 'text-right')
|
||||||
.info-1
|
.info-1
|
||||||
.info-item-1=_p(`pagination.${key}`)
|
.info-item-1=_p(`pagination.${key}`)
|
||||||
.info-item-2!=direction.title
|
.info-item-2!=direction.title
|
||||||
if getPostDesc
|
if getPostDesc
|
||||||
.info-2
|
.info-2
|
||||||
.info-item-1!=getPostDesc
|
.info-item-1!=getPostDesc
|
||||||
else
|
else
|
||||||
nav#pagination
|
nav#pagination
|
||||||
.pagination
|
.pagination
|
||||||
if globalPageType === 'home'
|
if globalPageType === 'home'
|
||||||
- options.format = 'page/%d/#content-inner'
|
- options.format = 'page/%d/#content-inner'
|
||||||
!=paginator(options)
|
!=paginator(options)
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
- const { readmode, translate, darkmode, aside, chat } = theme
|
- const { readmode, translate, darkmode, aside, chat } = theme
|
||||||
|
|
||||||
mixin rightsideItem(array)
|
mixin rightsideItem(array)
|
||||||
each item in array
|
each item in array
|
||||||
case item
|
case item
|
||||||
@@ -30,30 +31,22 @@ mixin rightsideItem(array)
|
|||||||
a#to_comment(href="#post-comment" title=_p("rightside.scroll_to_comment"))
|
a#to_comment(href="#post-comment" title=_p("rightside.scroll_to_comment"))
|
||||||
i.fas.fa-comments
|
i.fas.fa-comments
|
||||||
|
|
||||||
|
- const { enable, hide, show } = theme.rightside_item_order
|
||||||
|
- const hideArray = enable && hide ? hide.split(',') : ['readmode','translate','darkmode','hideAside']
|
||||||
|
- const showArray = enable && show ? show.split(',') : ['toc','chat','comment']
|
||||||
|
- const needCogBtn = (enable && hide) || (!enable && ((globalPageType === 'post' && (readmode || translate.enable || (darkmode.enable && darkmode.button))) || (translate.enable || (darkmode.enable && darkmode.button))))
|
||||||
|
|
||||||
#rightside
|
#rightside
|
||||||
- const { enable, hide, show } = theme.rightside_item_order
|
|
||||||
- const hideArray = enable ? hide && hide.split(',') : ['readmode','translate','darkmode','hideAside']
|
|
||||||
- const showArray = enable ? show && show.split(',') : ['toc','chat','comment']
|
|
||||||
|
|
||||||
|
|
||||||
#rightside-config-hide
|
#rightside-config-hide
|
||||||
if hideArray
|
if hideArray.length
|
||||||
+rightsideItem(hideArray)
|
+rightsideItem(hideArray)
|
||||||
#rightside-config-show
|
|
||||||
if enable
|
|
||||||
if hide
|
|
||||||
button#rightside-config(type="button" title=_p("rightside.setting"))
|
|
||||||
i.fas.fa-cog.fa-spin
|
|
||||||
else
|
|
||||||
if globalPageType === 'post'
|
|
||||||
if (readmode || translate.enable || (darkmode.enable && darkmode.button))
|
|
||||||
button#rightside-config(type="button" title=_p("rightside.setting"))
|
|
||||||
i.fas.fa-cog.fa-spin
|
|
||||||
else if translate.enable || (darkmode.enable && darkmode.button)
|
|
||||||
button#rightside-config(type="button" title=_p("rightside.setting"))
|
|
||||||
i.fas.fa-cog.fa-spin
|
|
||||||
|
|
||||||
if showArray
|
#rightside-config-show
|
||||||
|
if needCogBtn
|
||||||
|
button#rightside-config(type="button" title=_p("rightside.setting"))
|
||||||
|
i.fas.fa-cog(class=theme.rightside_config_animation ? 'fa-spin' : '')
|
||||||
|
|
||||||
|
if showArray.length
|
||||||
+rightsideItem(showArray)
|
+rightsideItem(showArray)
|
||||||
|
|
||||||
button#go-up(type="button" title=_p("rightside.back_to_top"))
|
button#go-up(type="button" title=_p("rightside.back_to_top"))
|
||||||
|
|||||||
53
layout/includes/third-party/abcjs/abcjs.pug
vendored
53
layout/includes/third-party/abcjs/abcjs.pug
vendored
@@ -1,17 +1,46 @@
|
|||||||
script.
|
script.
|
||||||
(() => {
|
(() => {
|
||||||
const abcjsInit = () => {
|
const abcjsInit = () => {
|
||||||
const abcjsFn = () => setTimeout(() => {
|
const abcjsFn = () => {
|
||||||
document.querySelectorAll(".abc-music-sheet").forEach(ele => {
|
setTimeout(() => {
|
||||||
if (ele.children.length > 0) return
|
const sheets = document.querySelectorAll(".abc-music-sheet")
|
||||||
ABCJS.renderAbc(ele, ele.innerHTML, {responsive: 'resize'})
|
for (let i = 0; i < sheets.length; i++) {
|
||||||
})
|
const ele = sheets[i]
|
||||||
}, 100)
|
if (ele.children.length > 0) continue
|
||||||
|
|
||||||
typeof ABCJS === 'object' ? abcjsFn()
|
// Parse parameters from data-params attribute
|
||||||
: btf.getScript('!{url_for(theme.asset.abcjs_basic_js)}').then(abcjsFn)
|
let params = {}
|
||||||
|
const dp = ele.getAttribute("data-params")
|
||||||
|
if (dp) {
|
||||||
|
try {
|
||||||
|
params = JSON.parse(dp)
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to parse data-params:", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge parsed parameters with the responsive option
|
||||||
|
// Ensures params content appears before responsive
|
||||||
|
const options = { ...params, responsive: "resize" }
|
||||||
|
|
||||||
|
// Render the music score using ABCJS.renderAbc
|
||||||
|
ABCJS.renderAbc(ele, ele.innerHTML, options)
|
||||||
|
}
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof ABCJS === "object") {
|
||||||
|
abcjsFn()
|
||||||
|
} else {
|
||||||
|
btf.getScript("!{url_for(theme.asset.abcjs_basic_js)}").then(abcjsFn)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.pjax ? abcjsInit() : window.addEventListener('load', abcjsInit)
|
if (window.pjax) {
|
||||||
btf.addGlobalFn('encrypt', abcjsInit, 'abcjs')
|
abcjsInit()
|
||||||
})()
|
} else {
|
||||||
|
window.addEventListener("load", abcjsInit)
|
||||||
|
}
|
||||||
|
|
||||||
|
btf.addGlobalFn("encrypt", abcjsInit, "abcjs")
|
||||||
|
})()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
script.
|
script.
|
||||||
(() => {
|
(() => {
|
||||||
const getCommentUrl = () => {
|
const getCommentUrl = () => {
|
||||||
const eleGroup = document.querySelectorAll('#recent-posts .article-title')
|
const eleGroup = document.querySelectorAll('#recent-posts .article-title')
|
||||||
let urlArray = []
|
let urlArray = []
|
||||||
@@ -18,7 +18,9 @@ script.
|
|||||||
includeReply: false
|
includeReply: false
|
||||||
}).then(function (res) {
|
}).then(function (res) {
|
||||||
document.querySelectorAll('#recent-posts .twikoo-count').forEach((item,index) => {
|
document.querySelectorAll('#recent-posts .twikoo-count').forEach((item,index) => {
|
||||||
item.textContent = res[index].count
|
if (res[index]) {
|
||||||
|
item.textContent = res[index].count
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ script.
|
|||||||
const loadArtalk = async (el, pageKey) => {
|
const loadArtalk = async (el, pageKey) => {
|
||||||
if (typeof Artalk === 'object') initArtalk(el, pageKey)
|
if (typeof Artalk === 'object') initArtalk(el, pageKey)
|
||||||
else {
|
else {
|
||||||
await btf.getCSS('!{theme.asset.artalk_css}')
|
await btf.getCSS('!{url_for(theme.asset.artalk_css)}')
|
||||||
await btf.getScript('!{theme.asset.artalk_js}')
|
await btf.getScript('!{url_for(theme.asset.artalk_js)}')
|
||||||
initArtalk(el, pageKey)
|
initArtalk(el, pageKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ hr.custom-hr
|
|||||||
.comment-headline
|
.comment-headline
|
||||||
i.fas.fa-comments.fa-fw
|
i.fas.fa-comments.fa-fw
|
||||||
span= ' ' + _p('comment')
|
span= ' ' + _p('comment')
|
||||||
|
|
||||||
if theme.comments.use.length > 1
|
if theme.comments.use.length > 1
|
||||||
.comment-switch
|
.comment-switch
|
||||||
span.first-comment=defaultComment
|
span.first-comment=defaultComment
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ script.
|
|||||||
: window.loadOtherComment = loadLivere
|
: window.loadOtherComment = loadLivere
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('!{use[0]}' === 'Livere' || !!{lazyload}) {
|
if ('!{use[0]}' === 'Livere' || !!{lazyload}) {
|
||||||
if (!{lazyload}) btf.loadComment(document.getElementById('lv-container'), loadLivere)
|
if (!{lazyload}) btf.loadComment(document.getElementById('lv-container'), loadLivere)
|
||||||
else loadLivere()
|
else loadLivere()
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
script.
|
script.
|
||||||
(() => {
|
(() => {
|
||||||
const isShuoshuo = GLOBAL_CONFIG_SITE.pageType === 'shuoshuo'
|
const isShuoshuo = GLOBAL_CONFIG_SITE.pageType === 'shuoshuo'
|
||||||
const option = !{JSON.stringify(option)}
|
const options = !{JSON.stringify(option)}
|
||||||
|
|
||||||
const loadScript = src => {
|
const loadScript = src => {
|
||||||
const script = document.createElement('script')
|
const script = document.createElement('script')
|
||||||
@@ -42,8 +42,8 @@ script.
|
|||||||
host: '!{host}',
|
host: '!{host}',
|
||||||
site_id: '!{siteId}',
|
site_id: '!{siteId}',
|
||||||
theme: document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light',
|
theme: document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light',
|
||||||
...option,
|
...options,
|
||||||
url: isShuoshuo ? window.location.origin + path : (option && option.url) || window.location.origin + window.location.pathname
|
url: isShuoshuo ? window.location.origin + path : (options && options.url) || window.location.origin + window.location.pathname
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.REMARK42) {
|
if (window.REMARK42) {
|
||||||
@@ -68,7 +68,7 @@ script.
|
|||||||
: window.loadOtherComment = loadRemark42
|
: window.loadOtherComment = loadRemark42
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('!{theme.comments.use[0]}' === 'Remark42' || !!{theme.comments.lazyload}) {
|
if ('!{theme.comments.use[0]}' === 'Remark42' || !!{theme.comments.lazyload}) {
|
||||||
if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('remark42'), loadRemark42)
|
if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('remark42'), loadRemark42)
|
||||||
else loadRemark42()
|
else loadRemark42()
|
||||||
|
|||||||
33
layout/includes/third-party/math/mathjax.pug
vendored
33
layout/includes/third-party/math/mathjax.pug
vendored
@@ -1,19 +1,50 @@
|
|||||||
//- Mathjax 3
|
//- Mathjax 4/5
|
||||||
- const { tags, enableMenu } = theme.math.mathjax
|
- const { tags, enableMenu } = theme.math.mathjax
|
||||||
script.
|
script.
|
||||||
(() => {
|
(() => {
|
||||||
const loadMathjax = () => {
|
const loadMathjax = () => {
|
||||||
if (!window.MathJax) {
|
if (!window.MathJax) {
|
||||||
window.MathJax = {
|
window.MathJax = {
|
||||||
|
loader: {
|
||||||
|
load: [
|
||||||
|
// Four font extension packages (optional)
|
||||||
|
//- '[tex]/bbm',
|
||||||
|
//- '[tex]/bboldx',
|
||||||
|
//- '[tex]/dsfont',
|
||||||
|
'[tex]/mhchem'
|
||||||
|
],
|
||||||
|
paths: {
|
||||||
|
'mathjax-newcm': '[mathjax]/../@mathjax/mathjax-newcm-font',
|
||||||
|
|
||||||
|
//- // Four font extension packages (optional)
|
||||||
|
//- 'mathjax-bbm-extension': '[mathjax]/../@mathjax/mathjax-bbm-font-extension',
|
||||||
|
//- 'mathjax-bboldx-extension': '[mathjax]/../@mathjax/mathjax-bboldx-font-extension',
|
||||||
|
//- 'mathjax-dsfont-extension': '[mathjax]/../@mathjax/mathjax-dsfont-font-extension',
|
||||||
|
'mathjax-mhchem-extension': '[mathjax]/../@mathjax/mathjax-mhchem-font-extension'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
font: 'mathjax-newcm',
|
||||||
|
},
|
||||||
tex: {
|
tex: {
|
||||||
inlineMath: [['$', '$'], ['\\(', '\\)']],
|
inlineMath: [['$', '$'], ['\\(', '\\)']],
|
||||||
tags: '!{tags}',
|
tags: '!{tags}',
|
||||||
|
packages: {
|
||||||
|
'[+]': [
|
||||||
|
'mhchem'
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
chtml: {
|
chtml: {
|
||||||
scale: 1.1
|
scale: 1.1
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
enableMenu: !{enableMenu},
|
enableMenu: !{enableMenu},
|
||||||
|
menuOptions: {
|
||||||
|
settings: {
|
||||||
|
enrich: false // Turn off Braille and voice narration text automatic generation
|
||||||
|
}
|
||||||
|
},
|
||||||
renderActions: {
|
renderActions: {
|
||||||
findScript: [10, doc => {
|
findScript: [10, doc => {
|
||||||
for (const node of document.querySelectorAll('script[type^="math/tex"]')) {
|
for (const node of document.querySelectorAll('script[type^="math/tex"]')) {
|
||||||
|
|||||||
6
layout/includes/third-party/math/mermaid.pug
vendored
6
layout/includes/third-party/math/mermaid.pug
vendored
@@ -6,7 +6,11 @@ script.
|
|||||||
|
|
||||||
ele.forEach((item, index) => {
|
ele.forEach((item, index) => {
|
||||||
const mermaidSrc = item.firstElementChild
|
const mermaidSrc = item.firstElementChild
|
||||||
const mermaidThemeConfig = `%%{init:{ 'theme':'${theme}'}}%%\n`
|
const config = mermaidSrc.dataset.config ? JSON.parse(mermaidSrc.dataset.config) : {}
|
||||||
|
if (!config.theme) {
|
||||||
|
config.theme = theme
|
||||||
|
}
|
||||||
|
const mermaidThemeConfig = `%%{init: ${JSON.stringify(config)}}%%\n`
|
||||||
const mermaidID = `mermaid-${index}`
|
const mermaidID = `mermaid-${index}`
|
||||||
const mermaidDefinition = mermaidThemeConfig + mermaidSrc.textContent
|
const mermaidDefinition = mermaidThemeConfig + mermaidSrc.textContent
|
||||||
|
|
||||||
|
|||||||
13
layout/includes/third-party/pjax.pug
vendored
13
layout/includes/third-party/pjax.pug
vendored
@@ -29,7 +29,13 @@ script.
|
|||||||
|
|
||||||
const triggerPjaxFn = (val) => {
|
const triggerPjaxFn = (val) => {
|
||||||
if (!val) return
|
if (!val) return
|
||||||
Object.values(val).forEach(fn => fn())
|
Object.values(val).forEach(fn => {
|
||||||
|
try {
|
||||||
|
fn()
|
||||||
|
} catch (err) {
|
||||||
|
console.debug('Pjax callback failed:', err)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('pjax:send', () => {
|
document.addEventListener('pjax:send', () => {
|
||||||
@@ -59,7 +65,10 @@ script.
|
|||||||
|
|
||||||
document.addEventListener('pjax:error', e => {
|
document.addEventListener('pjax:error', e => {
|
||||||
if (e.request.status === 404) {
|
if (e.request.status === 404) {
|
||||||
window.location.href = e.request.responseURL
|
const usePjax = !{theme.pjax && theme.pjax.enable}
|
||||||
|
!{theme.error_404 && theme.error_404.enable}
|
||||||
|
? (usePjax ? pjax.loadUrl('!{url_for("/404.html")}') : window.location.href = '!{url_for("/404.html")}')
|
||||||
|
: window.location.href = e.request.responseURL
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})()
|
})()
|
||||||
24
layout/includes/third-party/search/algolia.pug
vendored
24
layout/includes/third-party/search/algolia.pug
vendored
@@ -2,21 +2,33 @@
|
|||||||
.search-dialog
|
.search-dialog
|
||||||
nav.search-nav
|
nav.search-nav
|
||||||
span.search-dialog-title= _p('search.title')
|
span.search-dialog-title= _p('search.title')
|
||||||
|
i.fas.fa-spinner.fa-pulse#loading-status(hidden)
|
||||||
button.search-close-button
|
button.search-close-button
|
||||||
i.fas.fa-times
|
i.fas.fa-times
|
||||||
|
|
||||||
.search-wrap
|
#algolia-search-input
|
||||||
#algolia-search-input
|
.ais-SearchBox
|
||||||
|
form.ais-SearchBox-form(action="" role="search" novalidate="")
|
||||||
|
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")
|
||||||
|
button.ais-SearchBox-submit(type="submit" title="Submit the search query" style="display:none;")
|
||||||
|
svg.ais-SearchBox-submitIcon(width="10" height="10" viewBox="0 0 40 40" aria-hidden="true")
|
||||||
|
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")
|
||||||
hr
|
hr
|
||||||
#algolia-search-results
|
#algolia-search-results
|
||||||
#algolia-hits
|
#algolia-hits
|
||||||
#algolia-pagination
|
#algolia-hits-empty(style="display:none;")
|
||||||
|
.ais-Hits(style="display:none;")
|
||||||
|
ol.ais-Hits-list
|
||||||
|
#algolia-pagination.ais-Pagination(style="display:none;")
|
||||||
|
ul.ais-Pagination-list
|
||||||
#algolia-info
|
#algolia-info
|
||||||
.algolia-stats
|
span.ais-Stats-text
|
||||||
.algolia-poweredBy
|
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")
|
||||||
|
svg.ais-PoweredBy-logo(height="1.2em" viewBox="0 0 572 64" style="width: auto;")
|
||||||
|
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")
|
||||||
|
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")
|
||||||
|
|
||||||
#search-mask
|
#search-mask
|
||||||
|
|
||||||
script(src=url_for(theme.asset.algolia_search))
|
script(src=url_for(theme.asset.algolia_search))
|
||||||
script(src=url_for(theme.asset.instantsearch))
|
|
||||||
script(src=url_for(theme.asset.algolia_js))
|
script(src=url_for(theme.asset.algolia_js))
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
.search-dialog
|
.search-dialog
|
||||||
nav.search-nav
|
nav.search-nav
|
||||||
span.search-dialog-title= _p('search.title')
|
span.search-dialog-title= _p('search.title')
|
||||||
span#loading-status
|
i.fas.fa-spinner.fa-pulse#loading-status(hidden)
|
||||||
button.search-close-button
|
button.search-close-button
|
||||||
i.fas.fa-times
|
i.fas.fa-times
|
||||||
|
|
||||||
@@ -10,13 +10,15 @@
|
|||||||
i.fas.fa-spinner.fa-pulse
|
i.fas.fa-spinner.fa-pulse
|
||||||
span= ' ' + _p("search.load_data")
|
span= ' ' + _p("search.load_data")
|
||||||
|
|
||||||
.search-wrap
|
.local-search-input
|
||||||
#local-search-input
|
input(placeholder=theme.search.placeholder || _p("search.input_placeholder") type="text")
|
||||||
.local-search-box
|
|
||||||
input(placeholder=theme.search.placeholder || _p("search.input_placeholder") type="text").local-search-box--input
|
|
||||||
hr
|
|
||||||
#local-search-results
|
|
||||||
#local-search-stats-wrap
|
|
||||||
#search-mask
|
|
||||||
|
|
||||||
|
hr
|
||||||
|
|
||||||
|
#local-search-results
|
||||||
|
#local-search-pagination.ais-Pagination(style="display:none;")
|
||||||
|
ul.ais-Pagination-list
|
||||||
|
#local-search-stats
|
||||||
|
|
||||||
|
#search-mask
|
||||||
script(src=url_for(theme.asset.local_search))
|
script(src=url_for(theme.asset.local_search))
|
||||||
100
layout/includes/third-party/subtitle.pug
vendored
100
layout/includes/third-party/subtitle.pug
vendored
@@ -1,4 +1,4 @@
|
|||||||
- const { effect,source,sub,typed_option } = theme.subtitle
|
- const { effect, source, sub, typed_option } = theme.subtitle
|
||||||
- let subContent = sub || new Array()
|
- let subContent = sub || new Array()
|
||||||
|
|
||||||
script.
|
script.
|
||||||
@@ -22,6 +22,26 @@ script.
|
|||||||
} else {
|
} else {
|
||||||
subtitleType()
|
subtitleType()
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
processSubtitle: (content, extraContents = []) => {
|
||||||
|
if (!{effect}) {
|
||||||
|
const sub = !{JSON.stringify(subContent)}.slice()
|
||||||
|
|
||||||
|
if (extraContents.length > 0) {
|
||||||
|
sub.unshift(...extraContents)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof content === 'string') {
|
||||||
|
sub.unshift(content)
|
||||||
|
} else if (Array.isArray(content)) {
|
||||||
|
sub.unshift(...content)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub.length > 0 && typedJSFn.init(sub)
|
||||||
|
} else {
|
||||||
|
document.getElementById('subtitle').textContent = typeof content === 'string' ? content :
|
||||||
|
(Array.isArray(content) && content.length > 0 ? content[0] : '')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
btf.addGlobalFn('pjaxSendOnce', () => { typed.destroy() }, 'typedDestroy')
|
btf.addGlobalFn('pjaxSendOnce', () => { typed.destroy() }, 'typedDestroy')
|
||||||
@@ -33,14 +53,12 @@ case source
|
|||||||
fetch('https://v1.hitokoto.cn')
|
fetch('https://v1.hitokoto.cn')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (!{effect}) {
|
const from = '出自 ' + data.from
|
||||||
const from = '出自 ' + data.from
|
typedJSFn.processSubtitle(data.hitokoto, [from])
|
||||||
const sub = !{JSON.stringify(subContent)}
|
})
|
||||||
sub.unshift(data.hitokoto, from)
|
.catch(err => {
|
||||||
typedJSFn.init(sub)
|
console.error('Failed to get the Hitokoto API:', err)
|
||||||
} else {
|
typedJSFn.processSubtitle(!{JSON.stringify(subContent)})
|
||||||
document.getElementById('subtitle').textContent = data.hitokoto
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
typedJSFn.run(subtitleType)
|
typedJSFn.run(subtitleType)
|
||||||
@@ -48,46 +66,48 @@ case source
|
|||||||
when 2
|
when 2
|
||||||
script.
|
script.
|
||||||
function subtitleType () {
|
function subtitleType () {
|
||||||
btf.getScript('https://yijuzhan.com/api/word.php?m=js').then(() => {
|
fetch('https://v.api.aa1.cn/api/yiyan/index.php')
|
||||||
const con = str[0]
|
.then(response => response.text())
|
||||||
if (!{effect}) {
|
.then(data => {
|
||||||
const from = '出自 ' + str[1]
|
const reg = /<p>(.*?)<\/p>/g
|
||||||
const sub = !{JSON.stringify(subContent)}
|
const result = reg.exec(data)
|
||||||
sub.unshift(con, from)
|
if (result && result[1]) {
|
||||||
typedJSFn.init(sub)
|
typedJSFn.processSubtitle(result[1])
|
||||||
} else {
|
} else {
|
||||||
document.getElementById('subtitle').textContent = con
|
throw new Error('Failed to parse the return value of the Yiyan API')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('Failed to get the Yiyan API:', err)
|
||||||
|
typedJSFn.processSubtitle(!{JSON.stringify(subContent.length)})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
typedJSFn.run(subtitleType)
|
typedJSFn.run(subtitleType)
|
||||||
|
|
||||||
when 3
|
when 3
|
||||||
script.
|
script.
|
||||||
function subtitleType () {
|
function subtitleType () {
|
||||||
btf.getScript('https://sdk.jinrishici.com/v2/browser/jinrishici.js').then(() => {
|
btf.getScript('https://sdk.jinrishici.com/v2/browser/jinrishici.js')
|
||||||
jinrishici.load(result =>{
|
.then(() => {
|
||||||
if (!{effect}) {
|
jinrishici.load(result => {
|
||||||
const sub = !{JSON.stringify(subContent)}
|
if (result && result.data && result.data.content) {
|
||||||
const content = result.data.content
|
typedJSFn.processSubtitle(result.data.content)
|
||||||
sub.unshift(content)
|
} else {
|
||||||
typedJSFn.init(sub)
|
throw new Error('Failed to parse the return value of Jinrishici API')
|
||||||
} else {
|
}
|
||||||
document.getElementById('subtitle').textContent = result.data.content
|
})
|
||||||
}
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('Failed to get the Jinrishici API:', err)
|
||||||
|
typedJSFn.processSubtitle(!{JSON.stringify(subContent.length)})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
typedJSFn.run(subtitleType)
|
typedJSFn.run(subtitleType)
|
||||||
|
|
||||||
default
|
default
|
||||||
- subContent = subContent.length ? subContent : new Array(config.subtitle)
|
if subContent.length > 0
|
||||||
script.
|
script.
|
||||||
function subtitleType () {
|
function subtitleType () {
|
||||||
if (!{effect}) {
|
typedJSFn.processSubtitle(!{JSON.stringify(subContent)})
|
||||||
typedJSFn.init(!{JSON.stringify(subContent)})
|
|
||||||
} else {
|
|
||||||
document.getElementById("subtitle").textContent = !{JSON.stringify(subContent[0])}
|
|
||||||
}
|
}
|
||||||
}
|
typedJSFn.run(subtitleType)
|
||||||
typedJSFn.run(subtitleType)
|
|
||||||
101
layout/includes/third-party/umami_analytics.pug
vendored
101
layout/includes/third-party/umami_analytics.pug
vendored
@@ -1,4 +1,4 @@
|
|||||||
- let { serverURL, website_id, option, UV_PV } = theme.umami_analytics
|
- let { serverURL, script_name, website_id, option, UV_PV } = theme.umami_analytics
|
||||||
- const isServerURL = !!serverURL
|
- const isServerURL = !!serverURL
|
||||||
- const baseURL = serverURL ? serverURL.replace(/\/$/, '') : 'https://cloud.umami.is'
|
- const baseURL = serverURL ? serverURL.replace(/\/$/, '') : 'https://cloud.umami.is'
|
||||||
- const apiUrl = serverURL ? serverURL.replace(/\/$/, '') + '/api' : 'https://api.umami.is/v1'
|
- const apiUrl = serverURL ? serverURL.replace(/\/$/, '') + '/api' : 'https://api.umami.is/v1'
|
||||||
@@ -9,28 +9,51 @@ script.
|
|||||||
const config = !{JSON.stringify(UV_PV)}
|
const config = !{JSON.stringify(UV_PV)}
|
||||||
|
|
||||||
const runTrack = () => {
|
const runTrack = () => {
|
||||||
umami.track(props => ({ ...props, url: window.location.pathname, title: GLOBAL_CONFIG_SITE.title }))
|
if (typeof umami !== 'undefined' && typeof umami.track === 'function') {
|
||||||
|
umami.track(props => ({ ...props, url: window.location.pathname, title: GLOBAL_CONFIG_SITE.title }))
|
||||||
|
} else {
|
||||||
|
console.warn('Umami Analytics: umami.track is not available')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadUmamiJS = () => {
|
const loadUmamiJS = () => {
|
||||||
btf.getScript('!{baseURL}/script.js', {
|
btf.getScript('!{baseURL}/!{script_name}', {
|
||||||
'data-website-id': '!{website_id}',
|
'data-website-id': '!{website_id}',
|
||||||
'data-auto-track': 'false',
|
'data-auto-track': 'false',
|
||||||
...option
|
...option
|
||||||
}).then(runTrack)
|
}).then(() => {
|
||||||
|
runTrack()
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('Umami Analytics: Error loading script', error)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const getData = async (isPost) => {
|
const getData = async (isPost) => {
|
||||||
const now = Date.now()
|
try {
|
||||||
const keyUrl = isPost ? `&url=${window.location.pathname}` : ''
|
const now = Date.now()
|
||||||
const headerList = { 'Accept': 'application/json' }
|
const keyUrl = isPost ? `&url=${window.location.pathname}` : ''
|
||||||
if (!{isServerURL}) headerList['Authorization'] = `Bearer ${config.token}`
|
const headerList = { 'Accept': 'application/json' }
|
||||||
else headerList['x-umami-api-key'] = config.token
|
|
||||||
const res = await fetch(`!{apiUrl}/websites/!{website_id}/stats?startAt=0000000000&endAt=${now}${keyUrl}`, {
|
if (!{isServerURL}) {
|
||||||
method: "GET",
|
headerList['Authorization'] = `Bearer ${config.token}`
|
||||||
headers: headerList
|
} else {
|
||||||
})
|
headerList['x-umami-api-key'] = config.token
|
||||||
return await res.json()
|
}
|
||||||
|
|
||||||
|
const res = await fetch(`!{apiUrl}/websites/!{website_id}/stats?startAt=0000000000&endAt=${now}${keyUrl}`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: headerList
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${res.status}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return await res.json()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Umami Analytics: Failed to fetch data', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const insertData = async () => {
|
const insertData = async () => {
|
||||||
@@ -39,27 +62,49 @@ script.
|
|||||||
const pagePV = document.getElementById('umamiPV')
|
const pagePV = document.getElementById('umamiPV')
|
||||||
if (pagePV) {
|
if (pagePV) {
|
||||||
const data = await getData(true)
|
const data = await getData(true)
|
||||||
pagePV.textContent = data.pageviews.value
|
if (data && data.pageviews && typeof data.pageviews.value !== 'undefined') {
|
||||||
}
|
pagePV.textContent = data.pageviews.value
|
||||||
} else {
|
} else {
|
||||||
const data = (config.site_uv || config.site_pv) && await getData()
|
console.warn('Umami Analytics: Invalid page view data received')
|
||||||
if (config.site_uv) {
|
}
|
||||||
const siteUV = document.getElementById('umami-site-uv')
|
|
||||||
if (siteUV) siteUV.textContent = data.visitors.value
|
|
||||||
}
|
|
||||||
if (config.site_pv) {
|
|
||||||
const sitePV = document.getElementById('umami-site-pv')
|
|
||||||
if (sitePV) sitePV.textContent = data.pageviews.value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to load Umami Analytics:', e)
|
if (config.site_uv || config.site_pv) {
|
||||||
|
const data = await getData(false)
|
||||||
|
|
||||||
|
if (config.site_uv) {
|
||||||
|
const siteUV = document.getElementById('umami-site-uv')
|
||||||
|
if (siteUV && data && data.visitors && typeof data.visitors.value !== 'undefined') {
|
||||||
|
siteUV.textContent = data.visitors.value
|
||||||
|
} else if (siteUV) {
|
||||||
|
console.warn('Umami Analytics: Invalid site UV data received')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.site_pv) {
|
||||||
|
const sitePV = document.getElementById('umami-site-pv')
|
||||||
|
if (sitePV && data && data.pageviews && typeof data.pageviews.value !== 'undefined') {
|
||||||
|
sitePV.textContent = data.pageviews.value
|
||||||
|
} else if (sitePV) {
|
||||||
|
console.warn('Umami Analytics: Invalid site PV data received')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Umami Analytics: Failed to insert data', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
btf.addGlobalFn('pjaxComplete', runTrack, 'umami_analytics_run_track')
|
btf.addGlobalFn('pjaxComplete', runTrack, 'umami_analytics_run_track')
|
||||||
btf.addGlobalFn('pjaxComplete', insertData, 'umami_analytics_insert')
|
btf.addGlobalFn('pjaxComplete', insertData, 'umami_analytics_insert')
|
||||||
|
|
||||||
|
|
||||||
loadUmamiJS()
|
loadUmamiJS()
|
||||||
insertData()
|
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', insertData)
|
||||||
|
} else {
|
||||||
|
setTimeout(insertData, 100)
|
||||||
|
}
|
||||||
})()
|
})()
|
||||||
@@ -9,6 +9,6 @@ if theme.aside.card_tags.enable
|
|||||||
- limit = limit === 0 ? 0 : limit || 40
|
- limit = limit === 0 ? 0 : limit || 40
|
||||||
|
|
||||||
if theme.aside.card_tags.color
|
if theme.aside.card_tags.color
|
||||||
.card-tag-cloud!= cloudTags({source: site.tags, orderby: orderby, order: order, minfontsize: 1.15, maxfontsize: 1.45, limit: limit, unit: 'em'})
|
.card-tag-cloud!= cloudTags({source: site.tags, orderby: orderby, order: order, minfontsize: 1.15, maxfontsize: 1.45, limit: limit, unit: 'em', page: 'index'})
|
||||||
else
|
else
|
||||||
.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'})
|
.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'})
|
||||||
|
|||||||
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hexo-theme-butterfly",
|
"name": "hexo-theme-butterfly",
|
||||||
"version": "5.3.3",
|
"version": "5.5.2",
|
||||||
"description": "A Simple and Card UI Design theme for Hexo",
|
"description": "A Simple and Card UI Design theme for Hexo",
|
||||||
"main": "package.json",
|
"main": "package.json",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -15,16 +15,18 @@
|
|||||||
"hexo-theme-butterfly"
|
"hexo-theme-butterfly"
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type" : "git",
|
"type": "git",
|
||||||
"url" : "https://github.com/jerryc127/hexo-theme-butterfly.git"
|
"url": "https://github.com/jerryc127/hexo-theme-butterfly.git"
|
||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/jerryc127/hexo-theme-butterfly/issues",
|
"url": "https://github.com/jerryc127/hexo-theme-butterfly/issues",
|
||||||
"email": "my@crazywong.com"
|
"email": "my@crazywong.com"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"hexo-renderer-pug": "^3.0.0",
|
||||||
"hexo-renderer-stylus": "^3.0.1",
|
"hexo-renderer-stylus": "^3.0.1",
|
||||||
"hexo-renderer-pug": "^3.0.0"
|
"hexo-util": "^4.0.0",
|
||||||
|
"moment-timezone": "^0.6.0"
|
||||||
},
|
},
|
||||||
"homepage": "https://butterfly.js.org/",
|
"homepage": "https://butterfly.js.org/",
|
||||||
"author": "Jerry <my@crazywong.com>",
|
"author": "Jerry <my@crazywong.com>",
|
||||||
|
|||||||
66
plugins.yml
66
plugins.yml
@@ -1,15 +1,15 @@
|
|||||||
abcjs_basic_js:
|
abcjs_basic_js:
|
||||||
name: abcjs
|
name: abcjs
|
||||||
file: dist/abcjs-basic-min.js
|
file: dist/abcjs-basic-min.js
|
||||||
version: 6.4.4
|
version: 6.5.2
|
||||||
activate_power_mode:
|
activate_power_mode:
|
||||||
name: butterfly-extsrc
|
name: butterfly-extsrc
|
||||||
file: dist/activate-power-mode.min.js
|
file: dist/activate-power-mode.min.js
|
||||||
version: 1.1.4
|
version: 1.1.6
|
||||||
algolia_search:
|
algolia_search:
|
||||||
name: algoliasearch
|
name: algoliasearch
|
||||||
file: dist/lite/builds/browser.umd.js
|
file: dist/lite/builds/browser.umd.js
|
||||||
version: 5.20.2
|
version: 5.43.0
|
||||||
aplayer_css:
|
aplayer_css:
|
||||||
name: aplayer
|
name: aplayer
|
||||||
file: dist/APlayer.min.css
|
file: dist/APlayer.min.css
|
||||||
@@ -33,45 +33,45 @@ blueimp_md5:
|
|||||||
canvas_fluttering_ribbon:
|
canvas_fluttering_ribbon:
|
||||||
name: butterfly-extsrc
|
name: butterfly-extsrc
|
||||||
file: dist/canvas-fluttering-ribbon.min.js
|
file: dist/canvas-fluttering-ribbon.min.js
|
||||||
version: 1.1.4
|
version: 1.1.6
|
||||||
canvas_nest:
|
canvas_nest:
|
||||||
name: butterfly-extsrc
|
name: butterfly-extsrc
|
||||||
file: dist/canvas-nest.min.js
|
file: dist/canvas-nest.min.js
|
||||||
version: 1.1.4
|
version: 1.1.6
|
||||||
canvas_ribbon:
|
canvas_ribbon:
|
||||||
name: butterfly-extsrc
|
name: butterfly-extsrc
|
||||||
file: dist/canvas-ribbon.min.js
|
file: dist/canvas-ribbon.min.js
|
||||||
version: 1.1.4
|
version: 1.1.6
|
||||||
chartjs:
|
chartjs:
|
||||||
name: chart.js
|
name: chart.js
|
||||||
file: dist/chart.umd.js
|
file: dist/chart.umd.js
|
||||||
version: 4.4.7
|
version: 4.5.1
|
||||||
clickShowText:
|
clickShowText:
|
||||||
name: butterfly-extsrc
|
name: butterfly-extsrc
|
||||||
file: dist/click-show-text.min.js
|
file: dist/click-show-text.min.js
|
||||||
version: 1.1.4
|
version: 1.1.6
|
||||||
click_heart:
|
click_heart:
|
||||||
name: butterfly-extsrc
|
name: butterfly-extsrc
|
||||||
file: dist/click-heart.min.js
|
file: dist/click-heart.min.js
|
||||||
version: 1.1.4
|
version: 1.1.6
|
||||||
disqusjs:
|
disqusjs:
|
||||||
name: disqusjs
|
name: disqusjs
|
||||||
file: dist/browser/disqusjs.es2015.umd.min.js
|
file: dist/browser/disqusjs.es2015.umd.min.js
|
||||||
version: 3.0.2
|
version: 3.2.1
|
||||||
disqusjs_css:
|
disqusjs_css:
|
||||||
name: disqusjs
|
name: disqusjs
|
||||||
file: dist/browser/styles/disqusjs.css
|
file: dist/browser/styles/disqusjs.css
|
||||||
version: 3.0.2
|
version: 3.2.1
|
||||||
docsearch_css:
|
docsearch_css:
|
||||||
name: '@docsearch/css'
|
name: '@docsearch/css'
|
||||||
other_name: docsearch-css
|
other_name: docsearch-css
|
||||||
file: dist/style.css
|
file: dist/style.css
|
||||||
version: 3.8.3
|
version: 4.3.1
|
||||||
docsearch_js:
|
docsearch_js:
|
||||||
name: '@docsearch/js'
|
name: '@docsearch/js'
|
||||||
other_name: docsearch-js
|
other_name: docsearch-js
|
||||||
file: dist/umd/index.js
|
file: dist/umd/index.js
|
||||||
version: 3.8.3
|
version: 4.3.1
|
||||||
egjs_infinitegrid:
|
egjs_infinitegrid:
|
||||||
name: '@egjs/infinitegrid'
|
name: '@egjs/infinitegrid'
|
||||||
other_name: egjs-infinitegrid
|
other_name: egjs-infinitegrid
|
||||||
@@ -80,22 +80,22 @@ egjs_infinitegrid:
|
|||||||
fancybox:
|
fancybox:
|
||||||
name: '@fancyapps/ui'
|
name: '@fancyapps/ui'
|
||||||
file: dist/fancybox/fancybox.umd.js
|
file: dist/fancybox/fancybox.umd.js
|
||||||
version: 5.0.36
|
version: 6.1.4
|
||||||
other_name: fancyapps-ui
|
other_name: fancyapps-ui
|
||||||
fancybox_css:
|
fancybox_css:
|
||||||
name: '@fancyapps/ui'
|
name: '@fancyapps/ui'
|
||||||
file: dist/fancybox/fancybox.css
|
file: dist/fancybox/fancybox.css
|
||||||
version: 5.0.36
|
version: 6.1.4
|
||||||
other_name: fancyapps-ui
|
other_name: fancyapps-ui
|
||||||
fireworks:
|
fireworks:
|
||||||
name: butterfly-extsrc
|
name: butterfly-extsrc
|
||||||
file: dist/fireworks.min.js
|
file: dist/fireworks.min.js
|
||||||
version: 1.1.4
|
version: 1.1.6
|
||||||
fontawesome:
|
fontawesome:
|
||||||
name: '@fortawesome/fontawesome-free'
|
name: '@fortawesome/fontawesome-free'
|
||||||
file: css/all.min.css
|
file: css/all.min.css
|
||||||
other_name: font-awesome
|
other_name: font-awesome
|
||||||
version: 6.7.2
|
version: 7.1.0
|
||||||
gitalk:
|
gitalk:
|
||||||
name: gitalk
|
name: gitalk
|
||||||
file: dist/gitalk.min.js
|
file: dist/gitalk.min.js
|
||||||
@@ -108,28 +108,24 @@ instantpage:
|
|||||||
name: instant.page
|
name: instant.page
|
||||||
file: instantpage.js
|
file: instantpage.js
|
||||||
version: 5.2.0
|
version: 5.2.0
|
||||||
instantsearch:
|
|
||||||
name: instantsearch.js
|
|
||||||
file: dist/instantsearch.production.min.js
|
|
||||||
version: 4.77.3
|
|
||||||
katex:
|
katex:
|
||||||
name: katex
|
name: katex
|
||||||
file: dist/katex.min.css
|
file: dist/katex.min.css
|
||||||
other_name: KaTeX
|
other_name: KaTeX
|
||||||
version: 0.16.21
|
version: 0.16.25
|
||||||
katex_copytex:
|
katex_copytex:
|
||||||
name: katex
|
name: katex
|
||||||
file: dist/contrib/copy-tex.min.js
|
file: dist/contrib/copy-tex.min.js
|
||||||
other_name: KaTeX
|
other_name: KaTeX
|
||||||
version: 0.16.21
|
version: 0.16.25
|
||||||
lazyload:
|
lazyload:
|
||||||
name: vanilla-lazyload
|
name: vanilla-lazyload
|
||||||
file: dist/lazyload.iife.min.js
|
file: dist/lazyload.iife.min.js
|
||||||
version: 19.1.3
|
version: 19.1.3
|
||||||
mathjax:
|
mathjax:
|
||||||
name: mathjax
|
name: mathjax
|
||||||
file: es5/tex-mml-chtml.js
|
file: tex-mml-chtml.js
|
||||||
version: 3.2.2
|
version: 4.0.0
|
||||||
medium_zoom:
|
medium_zoom:
|
||||||
name: medium-zoom
|
name: medium-zoom
|
||||||
file: dist/medium-zoom.min.js
|
file: dist/medium-zoom.min.js
|
||||||
@@ -137,11 +133,11 @@ medium_zoom:
|
|||||||
mermaid:
|
mermaid:
|
||||||
name: mermaid
|
name: mermaid
|
||||||
file: dist/mermaid.min.js
|
file: dist/mermaid.min.js
|
||||||
version: 11.4.1
|
version: 11.12.1
|
||||||
meting_js:
|
meting_js:
|
||||||
name: butterfly-extsrc
|
name: butterfly-extsrc
|
||||||
file: metingjs/dist/Meting.min.js
|
file: metingjs/dist/Meting.min.js
|
||||||
version: 1.1.4
|
version: 1.1.6
|
||||||
pace_default_css:
|
pace_default_css:
|
||||||
name: pace-js
|
name: pace-js
|
||||||
other_name: pace
|
other_name: pace
|
||||||
@@ -160,25 +156,25 @@ prismjs_autoloader:
|
|||||||
name: prismjs
|
name: prismjs
|
||||||
file: plugins/autoloader/prism-autoloader.min.js
|
file: plugins/autoloader/prism-autoloader.min.js
|
||||||
other_name: prism
|
other_name: prism
|
||||||
version: 1.29.0
|
version: 1.30.0
|
||||||
prismjs_js:
|
prismjs_js:
|
||||||
name: prismjs
|
name: prismjs
|
||||||
file: prism.js
|
file: prism.js
|
||||||
other_name: prism
|
other_name: prism
|
||||||
version: 1.29.0
|
version: 1.30.0
|
||||||
prismjs_lineNumber_js:
|
prismjs_lineNumber_js:
|
||||||
name: prismjs
|
name: prismjs
|
||||||
file: plugins/line-numbers/prism-line-numbers.min.js
|
file: plugins/line-numbers/prism-line-numbers.min.js
|
||||||
other_name: prism
|
other_name: prism
|
||||||
version: 1.29.0
|
version: 1.30.0
|
||||||
sharejs:
|
sharejs:
|
||||||
name: butterfly-extsrc
|
name: butterfly-extsrc
|
||||||
file: sharejs/dist/js/social-share.min.js
|
file: sharejs/dist/js/social-share.min.js
|
||||||
version: 1.1.4
|
version: 1.1.6
|
||||||
sharejs_css:
|
sharejs_css:
|
||||||
name: butterfly-extsrc
|
name: butterfly-extsrc
|
||||||
file: sharejs/dist/css/share.min.css
|
file: sharejs/dist/css/share.min.css
|
||||||
version: 1.1.4
|
version: 1.1.6
|
||||||
snackbar:
|
snackbar:
|
||||||
name: node-snackbar
|
name: node-snackbar
|
||||||
file: dist/snackbar.min.js
|
file: dist/snackbar.min.js
|
||||||
@@ -190,7 +186,7 @@ snackbar_css:
|
|||||||
twikoo:
|
twikoo:
|
||||||
name: twikoo
|
name: twikoo
|
||||||
file: dist/twikoo.all.min.js
|
file: dist/twikoo.all.min.js
|
||||||
version: 1.6.41
|
version: 1.6.44
|
||||||
typed:
|
typed:
|
||||||
name: typed.js
|
name: typed.js
|
||||||
file: dist/typed.umd.js
|
file: dist/typed.umd.js
|
||||||
@@ -203,9 +199,9 @@ waline_css:
|
|||||||
name: '@waline/client'
|
name: '@waline/client'
|
||||||
file: dist/waline.css
|
file: dist/waline.css
|
||||||
other_name: waline
|
other_name: waline
|
||||||
version: 3.5.2
|
version: 3.7.1
|
||||||
waline_js:
|
waline_js:
|
||||||
name: '@waline/client'
|
name: '@waline/client'
|
||||||
file: dist/waline.js
|
file: dist/waline.js
|
||||||
other_name: waline
|
other_name: waline
|
||||||
version: 3.5.2
|
version: 3.7.1
|
||||||
|
|||||||
598
scripts/common/default_config.js
Normal file
598
scripts/common/default_config.js
Normal file
@@ -0,0 +1,598 @@
|
|||||||
|
// Butterfly 主題默認配置
|
||||||
|
// Default configuration for Butterfly theme
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
nav: {
|
||||||
|
logo: null,
|
||||||
|
display_title: true,
|
||||||
|
display_post_title: true,
|
||||||
|
fixed: false
|
||||||
|
},
|
||||||
|
menu: null,
|
||||||
|
code_blocks: {
|
||||||
|
theme: 'light',
|
||||||
|
macStyle: false,
|
||||||
|
height_limit: false,
|
||||||
|
word_wrap: false,
|
||||||
|
copy: true,
|
||||||
|
language: true,
|
||||||
|
shrink: false,
|
||||||
|
fullpage: false
|
||||||
|
},
|
||||||
|
social: null,
|
||||||
|
favicon: '/img/favicon.png',
|
||||||
|
avatar: {
|
||||||
|
img: '/img/butterfly-icon.png',
|
||||||
|
effect: false
|
||||||
|
},
|
||||||
|
disable_top_img: false,
|
||||||
|
default_top_img: null,
|
||||||
|
index_img: null,
|
||||||
|
archive_img: null,
|
||||||
|
tag_img: null,
|
||||||
|
tag_per_img: null,
|
||||||
|
category_img: null,
|
||||||
|
category_per_img: null,
|
||||||
|
footer_img: false,
|
||||||
|
background: null,
|
||||||
|
cover: {
|
||||||
|
index_enable: true,
|
||||||
|
aside_enable: true,
|
||||||
|
archives_enable: true,
|
||||||
|
default_cover: null
|
||||||
|
},
|
||||||
|
error_img: {
|
||||||
|
flink: '/img/friend_404.gif',
|
||||||
|
post_page: '/img/404.jpg'
|
||||||
|
},
|
||||||
|
error_404: {
|
||||||
|
enable: false,
|
||||||
|
subtitle: 'Page Not Found',
|
||||||
|
background: '/img/error-page.png'
|
||||||
|
},
|
||||||
|
post_meta: {
|
||||||
|
page: {
|
||||||
|
date_type: 'created',
|
||||||
|
date_format: 'date',
|
||||||
|
categories: true,
|
||||||
|
tags: false,
|
||||||
|
label: true
|
||||||
|
},
|
||||||
|
post: {
|
||||||
|
position: 'left',
|
||||||
|
date_type: 'both',
|
||||||
|
date_format: 'date',
|
||||||
|
categories: true,
|
||||||
|
tags: true,
|
||||||
|
label: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
index_site_info_top: null,
|
||||||
|
index_top_img_height: null,
|
||||||
|
subtitle: {
|
||||||
|
enable: false,
|
||||||
|
effect: true,
|
||||||
|
typed_option: null,
|
||||||
|
source: false,
|
||||||
|
sub: null
|
||||||
|
},
|
||||||
|
index_layout: 3,
|
||||||
|
index_post_content: {
|
||||||
|
method: 3,
|
||||||
|
length: 500
|
||||||
|
},
|
||||||
|
toc: {
|
||||||
|
post: true,
|
||||||
|
page: false,
|
||||||
|
number: true,
|
||||||
|
expand: false,
|
||||||
|
style_simple: false,
|
||||||
|
scroll_percent: true
|
||||||
|
},
|
||||||
|
post_copyright: {
|
||||||
|
enable: true,
|
||||||
|
decode: false,
|
||||||
|
author_href: null,
|
||||||
|
license: 'CC BY-NC-SA 4.0',
|
||||||
|
license_url: 'https://creativecommons.org/licenses/by-nc-sa/4.0/'
|
||||||
|
},
|
||||||
|
reward: {
|
||||||
|
enable: false,
|
||||||
|
text: null,
|
||||||
|
QR_code: null
|
||||||
|
},
|
||||||
|
post_edit: {
|
||||||
|
enable: false,
|
||||||
|
url: null
|
||||||
|
},
|
||||||
|
related_post: {
|
||||||
|
enable: true,
|
||||||
|
limit: 6,
|
||||||
|
date_type: 'created'
|
||||||
|
},
|
||||||
|
post_pagination: 1,
|
||||||
|
noticeOutdate: {
|
||||||
|
enable: false,
|
||||||
|
style: 'flat',
|
||||||
|
limit_day: 365,
|
||||||
|
position: 'top',
|
||||||
|
message_prev: 'It has been',
|
||||||
|
message_next: 'days since the last update, the content of the article may be outdated.'
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
nav: null,
|
||||||
|
owner: {
|
||||||
|
enable: true,
|
||||||
|
since: 2025
|
||||||
|
},
|
||||||
|
copyright: {
|
||||||
|
enable: true,
|
||||||
|
version: true
|
||||||
|
},
|
||||||
|
custom_text: null
|
||||||
|
},
|
||||||
|
aside: {
|
||||||
|
enable: true,
|
||||||
|
hide: false,
|
||||||
|
button: true,
|
||||||
|
mobile: true,
|
||||||
|
position: 'right',
|
||||||
|
display: {
|
||||||
|
archive: true,
|
||||||
|
tag: true,
|
||||||
|
category: true
|
||||||
|
},
|
||||||
|
card_author: {
|
||||||
|
enable: true,
|
||||||
|
description: null,
|
||||||
|
button: {
|
||||||
|
enable: true,
|
||||||
|
icon: 'fab fa-github',
|
||||||
|
text: 'Follow Me',
|
||||||
|
link: 'https://github.com/xxxxxx'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
card_announcement: {
|
||||||
|
enable: true,
|
||||||
|
content: 'This is my Blog'
|
||||||
|
},
|
||||||
|
card_recent_post: {
|
||||||
|
enable: true,
|
||||||
|
limit: 5,
|
||||||
|
sort: 'date',
|
||||||
|
sort_order: null
|
||||||
|
},
|
||||||
|
card_newest_comments: {
|
||||||
|
enable: false,
|
||||||
|
sort_order: null,
|
||||||
|
limit: 6,
|
||||||
|
storage: 10,
|
||||||
|
avatar: true
|
||||||
|
},
|
||||||
|
card_categories: {
|
||||||
|
enable: true,
|
||||||
|
limit: 8,
|
||||||
|
expand: 'none',
|
||||||
|
sort_order: null
|
||||||
|
},
|
||||||
|
card_tags: {
|
||||||
|
enable: true,
|
||||||
|
limit: 40,
|
||||||
|
color: false,
|
||||||
|
orderby: 'random',
|
||||||
|
order: 1,
|
||||||
|
sort_order: null
|
||||||
|
},
|
||||||
|
card_archives: {
|
||||||
|
enable: true,
|
||||||
|
type: 'monthly',
|
||||||
|
format: 'MMMM YYYY',
|
||||||
|
order: -1,
|
||||||
|
limit: 8,
|
||||||
|
sort_order: null
|
||||||
|
},
|
||||||
|
card_post_series: {
|
||||||
|
enable: true,
|
||||||
|
series_title: false,
|
||||||
|
orderBy: 'date',
|
||||||
|
order: -1
|
||||||
|
},
|
||||||
|
card_webinfo: {
|
||||||
|
enable: true,
|
||||||
|
post_count: true,
|
||||||
|
last_push_date: true,
|
||||||
|
sort_order: null,
|
||||||
|
runtime_date: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rightside_bottom: null,
|
||||||
|
translate: {
|
||||||
|
enable: false,
|
||||||
|
default: '繁',
|
||||||
|
defaultEncoding: 2,
|
||||||
|
translateDelay: 0,
|
||||||
|
msgToTraditionalChinese: '繁',
|
||||||
|
msgToSimplifiedChinese: '簡'
|
||||||
|
},
|
||||||
|
readmode: true,
|
||||||
|
darkmode: {
|
||||||
|
enable: true,
|
||||||
|
button: true,
|
||||||
|
autoChangeMode: false,
|
||||||
|
start: null,
|
||||||
|
end: null
|
||||||
|
},
|
||||||
|
rightside_scroll_percent: false,
|
||||||
|
rightside_item_order: {
|
||||||
|
enable: false,
|
||||||
|
hide: null,
|
||||||
|
show: null
|
||||||
|
},
|
||||||
|
rightside_config_animation: true,
|
||||||
|
anchor: {
|
||||||
|
auto_update: false,
|
||||||
|
click_to_scroll: false
|
||||||
|
},
|
||||||
|
photofigcaption: false,
|
||||||
|
copy: {
|
||||||
|
enable: true,
|
||||||
|
copyright: {
|
||||||
|
enable: false,
|
||||||
|
limit_count: 150
|
||||||
|
}
|
||||||
|
},
|
||||||
|
wordcount: {
|
||||||
|
enable: false,
|
||||||
|
post_wordcount: true,
|
||||||
|
min2read: true,
|
||||||
|
total_wordcount: true
|
||||||
|
},
|
||||||
|
busuanzi: {
|
||||||
|
site_uv: true,
|
||||||
|
site_pv: true,
|
||||||
|
page_pv: true
|
||||||
|
},
|
||||||
|
math: {
|
||||||
|
use: null,
|
||||||
|
per_page: true,
|
||||||
|
hide_scrollbar: false,
|
||||||
|
mathjax: {
|
||||||
|
enableMenu: true,
|
||||||
|
tags: 'none'
|
||||||
|
},
|
||||||
|
katex: {
|
||||||
|
copy_tex: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
use: null,
|
||||||
|
placeholder: null,
|
||||||
|
algolia_search: {
|
||||||
|
hitsPerPage: 6
|
||||||
|
},
|
||||||
|
local_search: {
|
||||||
|
preload: false,
|
||||||
|
top_n_per_article: 1,
|
||||||
|
unescape: false,
|
||||||
|
pagination: {
|
||||||
|
enable: false,
|
||||||
|
hitsPerPage: 8
|
||||||
|
},
|
||||||
|
CDN: null
|
||||||
|
},
|
||||||
|
docsearch: {
|
||||||
|
appId: null,
|
||||||
|
apiKey: null,
|
||||||
|
indexName: null,
|
||||||
|
option: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
share: {
|
||||||
|
use: 'sharejs',
|
||||||
|
sharejs: {
|
||||||
|
sites: 'facebook,x,wechat,weibo,qq'
|
||||||
|
},
|
||||||
|
addtoany: {
|
||||||
|
item: 'facebook,x,wechat,sina_weibo,facebook_messenger,email,copy_link'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
comments: {
|
||||||
|
use: null,
|
||||||
|
text: true,
|
||||||
|
lazyload: false,
|
||||||
|
count: false,
|
||||||
|
card_post_count: false
|
||||||
|
},
|
||||||
|
disqus: {
|
||||||
|
shortname: null,
|
||||||
|
apikey: null
|
||||||
|
},
|
||||||
|
disqusjs: {
|
||||||
|
shortname: null,
|
||||||
|
apikey: null,
|
||||||
|
option: null
|
||||||
|
},
|
||||||
|
livere: {
|
||||||
|
uid: null
|
||||||
|
},
|
||||||
|
gitalk: {
|
||||||
|
client_id: null,
|
||||||
|
client_secret: null,
|
||||||
|
repo: null,
|
||||||
|
owner: null,
|
||||||
|
admin: null,
|
||||||
|
option: null
|
||||||
|
},
|
||||||
|
valine: {
|
||||||
|
appId: null,
|
||||||
|
appKey: null,
|
||||||
|
avatar: 'monsterid',
|
||||||
|
serverURLs: null,
|
||||||
|
bg: null,
|
||||||
|
visitor: false,
|
||||||
|
option: null
|
||||||
|
},
|
||||||
|
waline: {
|
||||||
|
serverURL: null,
|
||||||
|
bg: null,
|
||||||
|
pageview: false,
|
||||||
|
option: null
|
||||||
|
},
|
||||||
|
utterances: {
|
||||||
|
repo: null,
|
||||||
|
issue_term: 'pathname',
|
||||||
|
light_theme: 'github-light',
|
||||||
|
dark_theme: 'photon-dark',
|
||||||
|
js: null,
|
||||||
|
option: null
|
||||||
|
},
|
||||||
|
facebook_comments: {
|
||||||
|
app_id: null,
|
||||||
|
user_id: null,
|
||||||
|
pageSize: 10,
|
||||||
|
order_by: 'social',
|
||||||
|
lang: 'en_US'
|
||||||
|
},
|
||||||
|
twikoo: {
|
||||||
|
envId: null,
|
||||||
|
region: null,
|
||||||
|
visitor: false,
|
||||||
|
option: null
|
||||||
|
},
|
||||||
|
giscus: {
|
||||||
|
repo: null,
|
||||||
|
repo_id: null,
|
||||||
|
category_id: null,
|
||||||
|
light_theme: 'light',
|
||||||
|
dark_theme: 'dark',
|
||||||
|
js: null,
|
||||||
|
option: null
|
||||||
|
},
|
||||||
|
remark42: {
|
||||||
|
host: null,
|
||||||
|
siteId: null,
|
||||||
|
option: null
|
||||||
|
},
|
||||||
|
artalk: {
|
||||||
|
server: null,
|
||||||
|
site: null,
|
||||||
|
visitor: false,
|
||||||
|
option: null
|
||||||
|
},
|
||||||
|
chat: {
|
||||||
|
use: null,
|
||||||
|
rightside_button: false,
|
||||||
|
button_hide_show: false
|
||||||
|
},
|
||||||
|
chatra: {
|
||||||
|
id: null
|
||||||
|
},
|
||||||
|
tidio: {
|
||||||
|
public_key: null
|
||||||
|
},
|
||||||
|
crisp: {
|
||||||
|
website_id: null
|
||||||
|
},
|
||||||
|
google_tag_manager: {
|
||||||
|
tag_id: null,
|
||||||
|
domain: 'https://www.googletagmanager.com'
|
||||||
|
},
|
||||||
|
baidu_analytics: null,
|
||||||
|
google_analytics: null,
|
||||||
|
cloudflare_analytics: null,
|
||||||
|
microsoft_clarity: null,
|
||||||
|
umami_analytics: {
|
||||||
|
enable: false,
|
||||||
|
serverURL: null,
|
||||||
|
script_name: 'script.js',
|
||||||
|
website_id: null,
|
||||||
|
option: null,
|
||||||
|
UV_PV: {
|
||||||
|
site_uv: false,
|
||||||
|
site_pv: false,
|
||||||
|
page_pv: false,
|
||||||
|
token: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
google_adsense: {
|
||||||
|
enable: false,
|
||||||
|
auto_ads: true,
|
||||||
|
js: 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js',
|
||||||
|
client: null,
|
||||||
|
enable_page_level_ads: true
|
||||||
|
},
|
||||||
|
ad: {
|
||||||
|
index: null,
|
||||||
|
aside: null,
|
||||||
|
post: null
|
||||||
|
},
|
||||||
|
site_verification: null,
|
||||||
|
category_ui: null,
|
||||||
|
tag_ui: null,
|
||||||
|
rounded_corners_ui: true,
|
||||||
|
text_align_justify: false,
|
||||||
|
mask: {
|
||||||
|
header: true,
|
||||||
|
footer: true
|
||||||
|
},
|
||||||
|
preloader: {
|
||||||
|
enable: false,
|
||||||
|
source: 1,
|
||||||
|
pace_css_url: null
|
||||||
|
},
|
||||||
|
enter_transitions: true,
|
||||||
|
display_mode: 'light',
|
||||||
|
beautify: {
|
||||||
|
enable: false,
|
||||||
|
field: 'post',
|
||||||
|
title_prefix_icon: null,
|
||||||
|
title_prefix_icon_color: null
|
||||||
|
},
|
||||||
|
font: {
|
||||||
|
global_font_size: null,
|
||||||
|
code_font_size: null,
|
||||||
|
font_family: null,
|
||||||
|
code_font_family: null
|
||||||
|
},
|
||||||
|
blog_title_font: {
|
||||||
|
font_link: null,
|
||||||
|
font_family: null
|
||||||
|
},
|
||||||
|
hr_icon: {
|
||||||
|
enable: true,
|
||||||
|
icon: null,
|
||||||
|
icon_top: null
|
||||||
|
},
|
||||||
|
activate_power_mode: {
|
||||||
|
enable: false,
|
||||||
|
colorful: true,
|
||||||
|
shake: true,
|
||||||
|
mobile: false
|
||||||
|
},
|
||||||
|
canvas_ribbon: {
|
||||||
|
enable: false,
|
||||||
|
size: 150,
|
||||||
|
alpha: 0.6,
|
||||||
|
zIndex: -1,
|
||||||
|
click_to_change: false,
|
||||||
|
mobile: false
|
||||||
|
},
|
||||||
|
canvas_fluttering_ribbon: {
|
||||||
|
enable: false,
|
||||||
|
mobile: false
|
||||||
|
},
|
||||||
|
canvas_nest: {
|
||||||
|
enable: false,
|
||||||
|
color: '0,0,255',
|
||||||
|
opacity: 0.7,
|
||||||
|
zIndex: -1,
|
||||||
|
count: 99,
|
||||||
|
mobile: false
|
||||||
|
},
|
||||||
|
fireworks: {
|
||||||
|
enable: false,
|
||||||
|
zIndex: 9999,
|
||||||
|
mobile: false
|
||||||
|
},
|
||||||
|
click_heart: {
|
||||||
|
enable: false,
|
||||||
|
mobile: false
|
||||||
|
},
|
||||||
|
clickShowText: {
|
||||||
|
enable: false,
|
||||||
|
text: null,
|
||||||
|
fontSize: '15px',
|
||||||
|
random: false,
|
||||||
|
mobile: false
|
||||||
|
},
|
||||||
|
lightbox: null,
|
||||||
|
series: {
|
||||||
|
enable: false,
|
||||||
|
orderBy: 'title',
|
||||||
|
order: 1,
|
||||||
|
number: true
|
||||||
|
},
|
||||||
|
abcjs: {
|
||||||
|
enable: false,
|
||||||
|
per_page: true
|
||||||
|
},
|
||||||
|
mermaid: {
|
||||||
|
enable: false,
|
||||||
|
code_write: false,
|
||||||
|
theme: {
|
||||||
|
light: 'default',
|
||||||
|
dark: 'dark'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
chartjs: {
|
||||||
|
enable: false,
|
||||||
|
fontColor: {
|
||||||
|
light: 'rgba(0, 0, 0, 0.8)',
|
||||||
|
dark: 'rgba(255, 255, 255, 0.8)'
|
||||||
|
},
|
||||||
|
borderColor: {
|
||||||
|
light: 'rgba(0, 0, 0, 0.1)',
|
||||||
|
dark: 'rgba(255, 255, 255, 0.2)'
|
||||||
|
},
|
||||||
|
scale_ticks_backdropColor: {
|
||||||
|
light: 'transparent',
|
||||||
|
dark: 'transparent'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
style: 'flat',
|
||||||
|
icons: true,
|
||||||
|
border_radius: 3,
|
||||||
|
light_bg_offset: 0
|
||||||
|
},
|
||||||
|
pjax: {
|
||||||
|
enable: false,
|
||||||
|
exclude: null
|
||||||
|
},
|
||||||
|
aplayerInject: {
|
||||||
|
enable: false,
|
||||||
|
per_page: true
|
||||||
|
},
|
||||||
|
snackbar: {
|
||||||
|
enable: false,
|
||||||
|
position: 'bottom-left',
|
||||||
|
bg_light: '#49b1f5',
|
||||||
|
bg_dark: '#1f1f1f'
|
||||||
|
},
|
||||||
|
instantpage: false,
|
||||||
|
lazyload: {
|
||||||
|
enable: false,
|
||||||
|
native: false,
|
||||||
|
field: 'site',
|
||||||
|
placeholder: null,
|
||||||
|
blur: false
|
||||||
|
},
|
||||||
|
pwa: {
|
||||||
|
enable: false,
|
||||||
|
manifest: null,
|
||||||
|
apple_touch_icon: null,
|
||||||
|
favicon_32_32: null,
|
||||||
|
favicon_16_16: null,
|
||||||
|
mask_icon: null
|
||||||
|
},
|
||||||
|
Open_Graph_meta: {
|
||||||
|
enable: true,
|
||||||
|
option: null
|
||||||
|
},
|
||||||
|
structured_data: {
|
||||||
|
enable: false,
|
||||||
|
alternate_name: null
|
||||||
|
},
|
||||||
|
css_prefix: true,
|
||||||
|
inject: {
|
||||||
|
head: null,
|
||||||
|
bottom: null
|
||||||
|
},
|
||||||
|
CDN: {
|
||||||
|
internal_provider: 'local',
|
||||||
|
third_party_provider: 'jsdelivr',
|
||||||
|
version: true,
|
||||||
|
custom_format: null,
|
||||||
|
option: null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,13 +3,14 @@
|
|||||||
const { stripHTML, truncate } = require('hexo-util')
|
const { stripHTML, truncate } = require('hexo-util')
|
||||||
|
|
||||||
// Truncates the given content to a specified length, removing HTML tags and replacing newlines with spaces.
|
// Truncates the given content to a specified length, removing HTML tags and replacing newlines with spaces.
|
||||||
const truncateContent = (content, length) => {
|
const truncateContent = (content, length, encrypt = false) => {
|
||||||
return truncate(stripHTML(content), { length, separator: ' ' }).replace(/\n/g, ' ')
|
if (!content || encrypt) return ''
|
||||||
|
return truncate(stripHTML(content).replace(/\n/g, ' '), { length })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates a post description based on the provided data and theme configuration.
|
// Generates a post description based on the provided data and theme configuration.
|
||||||
const postDesc = (data, hexo) => {
|
const postDesc = (data, hexo) => {
|
||||||
const { description, content, postDesc } = data
|
const { description, content, postDesc, encrypt } = data
|
||||||
|
|
||||||
if (postDesc) return postDesc
|
if (postDesc) return postDesc
|
||||||
|
|
||||||
@@ -23,10 +24,10 @@ const postDesc = (data, hexo) => {
|
|||||||
result = description
|
result = description
|
||||||
break
|
break
|
||||||
case 2:
|
case 2:
|
||||||
result = description || truncateContent(content, length)
|
result = description || truncateContent(content, length, encrypt)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
result = truncateContent(content, length)
|
result = truncateContent(content, length, encrypt)
|
||||||
}
|
}
|
||||||
|
|
||||||
data.postDesc = result
|
data.postDesc = result
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* Capitalize the first letter of comment name
|
|
||||||
*/
|
|
||||||
|
|
||||||
hexo.extend.filter.register('before_generate', () => {
|
|
||||||
const themeConfig = hexo.theme.config
|
|
||||||
let { use } = themeConfig.comments
|
|
||||||
if (!use) return
|
|
||||||
|
|
||||||
// Make sure use is an array
|
|
||||||
use = Array.isArray(use) ? use : use.split(',')
|
|
||||||
|
|
||||||
// Capitalize the first letter of each comment name
|
|
||||||
use = use.map(item =>
|
|
||||||
item.trim().toLowerCase().replace(/\b[a-z]/g, s => s.toUpperCase())
|
|
||||||
)
|
|
||||||
|
|
||||||
// Disqus and Disqusjs conflict, only keep the first one
|
|
||||||
if (use.includes('Disqus') && use.includes('Disqusjs')) {
|
|
||||||
use = [use[0]]
|
|
||||||
}
|
|
||||||
|
|
||||||
themeConfig.comments.use = use
|
|
||||||
})
|
|
||||||
@@ -1,20 +1,86 @@
|
|||||||
hexo.extend.filter.register('before_generate', () => {
|
const { deepMerge } = require('hexo-util')
|
||||||
// Get first two digits of the Hexo version number
|
const path = require('path')
|
||||||
const { version, log, locals } = hexo
|
|
||||||
const hexoVer = version.replace(/(^.*\..*)\..*/, '$1')
|
// Cache default config to avoid repeated file reads
|
||||||
|
let cachedDefaultConfig = null
|
||||||
if (hexoVer < 5.3) {
|
|
||||||
log.error('Please update Hexo to V5.3.0 or higher!')
|
/**
|
||||||
log.error('請把 Hexo 升級到 V5.3.0 或更高的版本!')
|
* Check Hexo version and configuration
|
||||||
process.exit(-1)
|
*/
|
||||||
}
|
function checkHexoEnvironment (hexo) {
|
||||||
|
const { version, log, locals } = hexo
|
||||||
if (locals.get) {
|
|
||||||
const data = locals.get('data')
|
const [major, minor] = version.split('.').map(Number)
|
||||||
if (data && data.butterfly) {
|
const requiredMajor = 5
|
||||||
log.error("'butterfly.yml' is deprecated. Please use '_config.butterfly.yml'")
|
const requiredMinor = 3
|
||||||
log.error("'butterfly.yml' 已經棄用,請使用 '_config.butterfly.yml'")
|
|
||||||
process.exit(-1)
|
if (major < requiredMajor || (major === requiredMajor && minor < requiredMinor)) {
|
||||||
}
|
log.error('Please update Hexo to V5.3.0 or higher!')
|
||||||
}
|
log.error('請把 Hexo 升級到 V5.3.0 或更高的版本!')
|
||||||
})
|
process.exit(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for deprecated configuration file
|
||||||
|
if (locals.get) {
|
||||||
|
const data = locals.get('data')
|
||||||
|
if (data && data.butterfly) {
|
||||||
|
log.error("'butterfly.yml' is deprecated. Please use '_config.butterfly.yml'")
|
||||||
|
log.error("'butterfly.yml' 已經棄用,請使用 '_config.butterfly.yml'")
|
||||||
|
process.exit(-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load default configuration
|
||||||
|
*/
|
||||||
|
function loadDefaultConfig () {
|
||||||
|
if (cachedDefaultConfig) {
|
||||||
|
return cachedDefaultConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
const configPath = path.join(__dirname, '../common/default_config.js')
|
||||||
|
cachedDefaultConfig = require(configPath)
|
||||||
|
return cachedDefaultConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process comment system configuration
|
||||||
|
*/
|
||||||
|
function processCommentConfig (themeConfig) {
|
||||||
|
const { comments } = themeConfig
|
||||||
|
if (!comments || !comments.use) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let { use } = comments
|
||||||
|
|
||||||
|
if (!Array.isArray(use)) {
|
||||||
|
use = typeof use === 'string' ? use.split(',') : [use]
|
||||||
|
}
|
||||||
|
|
||||||
|
use = use
|
||||||
|
.map(item => {
|
||||||
|
if (typeof item !== 'string') return item
|
||||||
|
return item.trim().toLowerCase().replace(/\b[a-z]/g, s => s.toUpperCase())
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
|
||||||
|
// Handle Disqus and Disqusjs conflict
|
||||||
|
if (use.includes('Disqus') && use.includes('Disqusjs')) {
|
||||||
|
hexo.log.warn('Disqus and Disqusjs conflict detected, keeping only the first one')
|
||||||
|
hexo.log.warn('檢測到 Disqus 和 Disqusjs 衝突,只保留第一個')
|
||||||
|
use = [use[0]]
|
||||||
|
}
|
||||||
|
|
||||||
|
themeConfig.comments.use = use
|
||||||
|
}
|
||||||
|
|
||||||
|
hexo.extend.filter.register('before_generate', () => {
|
||||||
|
checkHexoEnvironment(hexo)
|
||||||
|
|
||||||
|
const defaultConfig = loadDefaultConfig()
|
||||||
|
hexo.theme.config = deepMerge(defaultConfig, hexo.theme.config)
|
||||||
|
|
||||||
|
processCommentConfig(hexo.theme.config)
|
||||||
|
}, 1)
|
||||||
|
|||||||
@@ -1,583 +0,0 @@
|
|||||||
const { deepMerge } = require('hexo-util')
|
|
||||||
|
|
||||||
hexo.extend.filter.register('before_generate', () => {
|
|
||||||
const defaultConfig = {
|
|
||||||
nav: {
|
|
||||||
logo: null,
|
|
||||||
display_title: true,
|
|
||||||
fixed: false
|
|
||||||
},
|
|
||||||
menu: null,
|
|
||||||
code_blocks: {
|
|
||||||
theme: 'light',
|
|
||||||
macStyle: false,
|
|
||||||
height_limit: false,
|
|
||||||
word_wrap: false,
|
|
||||||
copy: true,
|
|
||||||
language: true,
|
|
||||||
shrink: false,
|
|
||||||
fullpage: false
|
|
||||||
},
|
|
||||||
social: null,
|
|
||||||
favicon: '/img/favicon.png',
|
|
||||||
avatar: {
|
|
||||||
img: '/img/butterfly-icon.png',
|
|
||||||
effect: false
|
|
||||||
},
|
|
||||||
disable_top_img: false,
|
|
||||||
default_top_img: null,
|
|
||||||
index_img: null,
|
|
||||||
archive_img: null,
|
|
||||||
tag_img: null,
|
|
||||||
tag_per_img: null,
|
|
||||||
category_img: null,
|
|
||||||
category_per_img: null,
|
|
||||||
footer_img: false,
|
|
||||||
background: null,
|
|
||||||
cover: {
|
|
||||||
index_enable: true,
|
|
||||||
aside_enable: true,
|
|
||||||
archives_enable: true,
|
|
||||||
default_cover: null
|
|
||||||
},
|
|
||||||
error_img: {
|
|
||||||
flink: '/img/friend_404.gif',
|
|
||||||
post_page: '/img/404.jpg'
|
|
||||||
},
|
|
||||||
error_404: {
|
|
||||||
enable: false,
|
|
||||||
subtitle: 'Page Not Found',
|
|
||||||
background: '/img/error-page.png'
|
|
||||||
},
|
|
||||||
post_meta: {
|
|
||||||
page: {
|
|
||||||
date_type: 'created',
|
|
||||||
date_format: 'date',
|
|
||||||
categories: true,
|
|
||||||
tags: false,
|
|
||||||
label: true
|
|
||||||
},
|
|
||||||
post: {
|
|
||||||
position: 'left',
|
|
||||||
date_type: 'both',
|
|
||||||
date_format: 'date',
|
|
||||||
categories: true,
|
|
||||||
tags: true,
|
|
||||||
label: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
index_site_info_top: null,
|
|
||||||
index_top_img_height: null,
|
|
||||||
subtitle: {
|
|
||||||
enable: false,
|
|
||||||
effect: true,
|
|
||||||
typed_option: null,
|
|
||||||
source: false,
|
|
||||||
sub: null
|
|
||||||
},
|
|
||||||
index_layout: 3,
|
|
||||||
index_post_content: {
|
|
||||||
method: 3,
|
|
||||||
length: 500
|
|
||||||
},
|
|
||||||
toc: {
|
|
||||||
post: true,
|
|
||||||
page: false,
|
|
||||||
number: true,
|
|
||||||
expand: false,
|
|
||||||
style_simple: false,
|
|
||||||
scroll_percent: true
|
|
||||||
},
|
|
||||||
post_copyright: {
|
|
||||||
enable: true,
|
|
||||||
decode: false,
|
|
||||||
author_href: null,
|
|
||||||
license: 'CC BY-NC-SA 4.0',
|
|
||||||
license_url: 'https://creativecommons.org/licenses/by-nc-sa/4.0/'
|
|
||||||
},
|
|
||||||
reward: {
|
|
||||||
enable: false,
|
|
||||||
text: null,
|
|
||||||
QR_code: null
|
|
||||||
},
|
|
||||||
post_edit: {
|
|
||||||
enable: false,
|
|
||||||
url: null
|
|
||||||
},
|
|
||||||
related_post: {
|
|
||||||
enable: true,
|
|
||||||
limit: 6,
|
|
||||||
date_type: 'created'
|
|
||||||
},
|
|
||||||
post_pagination: 1,
|
|
||||||
noticeOutdate: {
|
|
||||||
enable: false,
|
|
||||||
style: 'flat',
|
|
||||||
limit_day: 365,
|
|
||||||
position: 'top',
|
|
||||||
message_prev: 'It has been',
|
|
||||||
message_next: 'days since the last update, the content of the article may be outdated.'
|
|
||||||
},
|
|
||||||
footer: {
|
|
||||||
owner: {
|
|
||||||
enable: true,
|
|
||||||
since: 2019
|
|
||||||
},
|
|
||||||
custom_text: null,
|
|
||||||
copyright: true
|
|
||||||
},
|
|
||||||
aside: {
|
|
||||||
enable: true,
|
|
||||||
hide: false,
|
|
||||||
button: true,
|
|
||||||
mobile: true,
|
|
||||||
position: 'right',
|
|
||||||
display: {
|
|
||||||
archive: true,
|
|
||||||
tag: true,
|
|
||||||
category: true
|
|
||||||
},
|
|
||||||
card_author: {
|
|
||||||
enable: true,
|
|
||||||
description: null,
|
|
||||||
button: {
|
|
||||||
enable: true,
|
|
||||||
icon: 'fab fa-github',
|
|
||||||
text: 'Follow Me',
|
|
||||||
link: 'https://github.com/xxxxxx'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
card_announcement: {
|
|
||||||
enable: true,
|
|
||||||
content: 'This is my Blog'
|
|
||||||
},
|
|
||||||
card_recent_post: {
|
|
||||||
enable: true,
|
|
||||||
limit: 5,
|
|
||||||
sort: 'date',
|
|
||||||
sort_order: null
|
|
||||||
},
|
|
||||||
card_newest_comments: {
|
|
||||||
enable: false,
|
|
||||||
sort_order: null,
|
|
||||||
limit: 6,
|
|
||||||
storage: 10,
|
|
||||||
avatar: true
|
|
||||||
},
|
|
||||||
card_categories: {
|
|
||||||
enable: true,
|
|
||||||
limit: 8,
|
|
||||||
expand: 'none',
|
|
||||||
sort_order: null
|
|
||||||
},
|
|
||||||
card_tags: {
|
|
||||||
enable: true,
|
|
||||||
limit: 40,
|
|
||||||
color: false,
|
|
||||||
orderby: 'random',
|
|
||||||
order: 1,
|
|
||||||
sort_order: null
|
|
||||||
},
|
|
||||||
card_archives: {
|
|
||||||
enable: true,
|
|
||||||
type: 'monthly',
|
|
||||||
format: 'MMMM YYYY',
|
|
||||||
order: -1,
|
|
||||||
limit: 8,
|
|
||||||
sort_order: null
|
|
||||||
},
|
|
||||||
card_post_series: {
|
|
||||||
enable: true,
|
|
||||||
series_title: false,
|
|
||||||
orderBy: 'date',
|
|
||||||
order: -1
|
|
||||||
},
|
|
||||||
card_webinfo: {
|
|
||||||
enable: true,
|
|
||||||
post_count: true,
|
|
||||||
last_push_date: true,
|
|
||||||
sort_order: null,
|
|
||||||
runtime_date: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
rightside_bottom: null,
|
|
||||||
translate: {
|
|
||||||
enable: false,
|
|
||||||
default: '繁',
|
|
||||||
defaultEncoding: 2,
|
|
||||||
translateDelay: 0,
|
|
||||||
msgToTraditionalChinese: '繁',
|
|
||||||
msgToSimplifiedChinese: '簡'
|
|
||||||
},
|
|
||||||
readmode: true,
|
|
||||||
darkmode: {
|
|
||||||
enable: true,
|
|
||||||
button: true,
|
|
||||||
autoChangeMode: false,
|
|
||||||
start: null,
|
|
||||||
end: null
|
|
||||||
},
|
|
||||||
rightside_scroll_percent: false,
|
|
||||||
rightside_item_order: {
|
|
||||||
enable: false,
|
|
||||||
hide: null,
|
|
||||||
show: null
|
|
||||||
},
|
|
||||||
anchor: {
|
|
||||||
auto_update: false,
|
|
||||||
click_to_scroll: false
|
|
||||||
},
|
|
||||||
photofigcaption: false,
|
|
||||||
copy: {
|
|
||||||
enable: true,
|
|
||||||
copyright: {
|
|
||||||
enable: false,
|
|
||||||
limit_count: 150
|
|
||||||
}
|
|
||||||
},
|
|
||||||
wordcount: {
|
|
||||||
enable: false,
|
|
||||||
post_wordcount: true,
|
|
||||||
min2read: true,
|
|
||||||
total_wordcount: true
|
|
||||||
},
|
|
||||||
busuanzi: {
|
|
||||||
site_uv: true,
|
|
||||||
site_pv: true,
|
|
||||||
page_pv: true
|
|
||||||
},
|
|
||||||
math: {
|
|
||||||
use: null,
|
|
||||||
per_page: true,
|
|
||||||
hide_scrollbar: false,
|
|
||||||
mathjax: {
|
|
||||||
enableMenu: true,
|
|
||||||
tags: 'none'
|
|
||||||
},
|
|
||||||
katex: {
|
|
||||||
copy_tex: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
use: null,
|
|
||||||
placeholder: null,
|
|
||||||
algolia_search: {
|
|
||||||
hitsPerPage: 6
|
|
||||||
},
|
|
||||||
local_search: {
|
|
||||||
preload: false,
|
|
||||||
top_n_per_article: 1,
|
|
||||||
unescape: false,
|
|
||||||
CDN: null
|
|
||||||
},
|
|
||||||
docsearch: {
|
|
||||||
appId: null,
|
|
||||||
apiKey: null,
|
|
||||||
indexName: null,
|
|
||||||
option: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
share: {
|
|
||||||
use: 'sharejs',
|
|
||||||
sharejs: {
|
|
||||||
sites: 'facebook,twitter,wechat,weibo,qq'
|
|
||||||
},
|
|
||||||
addtoany: {
|
|
||||||
item: 'facebook,twitter,wechat,sina_weibo,facebook_messenger,email,copy_link'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
comments: {
|
|
||||||
use: null,
|
|
||||||
text: true,
|
|
||||||
lazyload: false,
|
|
||||||
count: false,
|
|
||||||
card_post_count: false
|
|
||||||
},
|
|
||||||
disqus: {
|
|
||||||
shortname: null,
|
|
||||||
apikey: null
|
|
||||||
},
|
|
||||||
disqusjs: {
|
|
||||||
shortname: null,
|
|
||||||
apikey: null,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
livere: {
|
|
||||||
uid: null
|
|
||||||
},
|
|
||||||
gitalk: {
|
|
||||||
client_id: null,
|
|
||||||
client_secret: null,
|
|
||||||
repo: null,
|
|
||||||
owner: null,
|
|
||||||
admin: null,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
valine: {
|
|
||||||
appId: null,
|
|
||||||
appKey: null,
|
|
||||||
avatar: 'monsterid',
|
|
||||||
serverURLs: null,
|
|
||||||
bg: null,
|
|
||||||
visitor: false,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
waline: {
|
|
||||||
serverURL: null,
|
|
||||||
bg: null,
|
|
||||||
pageview: false,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
utterances: {
|
|
||||||
repo: null,
|
|
||||||
issue_term: 'pathname',
|
|
||||||
light_theme: 'github-light',
|
|
||||||
dark_theme: 'photon-dark',
|
|
||||||
js: null,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
facebook_comments: {
|
|
||||||
app_id: null,
|
|
||||||
user_id: null,
|
|
||||||
pageSize: 10,
|
|
||||||
order_by: 'social',
|
|
||||||
lang: 'en_US'
|
|
||||||
},
|
|
||||||
twikoo: {
|
|
||||||
envId: null,
|
|
||||||
region: null,
|
|
||||||
visitor: false,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
giscus: {
|
|
||||||
repo: null,
|
|
||||||
repo_id: null,
|
|
||||||
category_id: null,
|
|
||||||
light_theme: 'light',
|
|
||||||
dark_theme: 'dark',
|
|
||||||
js: null,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
remark42: {
|
|
||||||
host: null,
|
|
||||||
siteId: null,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
artalk: {
|
|
||||||
server: null,
|
|
||||||
site: null,
|
|
||||||
visitor: false,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
chat: {
|
|
||||||
use: null,
|
|
||||||
rightside_button: false,
|
|
||||||
button_hide_show: false
|
|
||||||
},
|
|
||||||
chatra: {
|
|
||||||
id: null
|
|
||||||
},
|
|
||||||
tidio: {
|
|
||||||
public_key: null
|
|
||||||
},
|
|
||||||
crisp: {
|
|
||||||
website_id: null
|
|
||||||
},
|
|
||||||
baidu_analytics: null,
|
|
||||||
google_analytics: null,
|
|
||||||
cloudflare_analytics: null,
|
|
||||||
microsoft_clarity: null,
|
|
||||||
umami_analytics: {
|
|
||||||
enable: false,
|
|
||||||
serverURL: null,
|
|
||||||
website_id: null,
|
|
||||||
option: null,
|
|
||||||
UV_PV: {
|
|
||||||
site_uv: false,
|
|
||||||
site_pv: false,
|
|
||||||
page_pv: false,
|
|
||||||
token: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
google_adsense: {
|
|
||||||
enable: false,
|
|
||||||
auto_ads: true,
|
|
||||||
js: 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js',
|
|
||||||
client: null,
|
|
||||||
enable_page_level_ads: true
|
|
||||||
},
|
|
||||||
ad: {
|
|
||||||
index: null,
|
|
||||||
aside: null,
|
|
||||||
post: null
|
|
||||||
},
|
|
||||||
site_verification: null,
|
|
||||||
category_ui: null,
|
|
||||||
tag_ui: null,
|
|
||||||
rounded_corners_ui: true,
|
|
||||||
text_align_justify: false,
|
|
||||||
mask: {
|
|
||||||
header: true,
|
|
||||||
footer: true
|
|
||||||
},
|
|
||||||
preloader: {
|
|
||||||
enable: false,
|
|
||||||
source: 1,
|
|
||||||
pace_css_url: null
|
|
||||||
},
|
|
||||||
enter_transitions: true,
|
|
||||||
display_mode: 'light',
|
|
||||||
beautify: {
|
|
||||||
enable: false,
|
|
||||||
field: 'post',
|
|
||||||
title_prefix_icon: null,
|
|
||||||
title_prefix_icon_color: null
|
|
||||||
},
|
|
||||||
font: {
|
|
||||||
global_font_size: null,
|
|
||||||
code_font_size: null,
|
|
||||||
font_family: null,
|
|
||||||
code_font_family: null
|
|
||||||
},
|
|
||||||
blog_title_font: {
|
|
||||||
font_link: null,
|
|
||||||
font_family: null
|
|
||||||
},
|
|
||||||
hr_icon: {
|
|
||||||
enable: true,
|
|
||||||
icon: null,
|
|
||||||
icon_top: null
|
|
||||||
},
|
|
||||||
activate_power_mode: {
|
|
||||||
enable: false,
|
|
||||||
colorful: true,
|
|
||||||
shake: true,
|
|
||||||
mobile: false
|
|
||||||
},
|
|
||||||
canvas_ribbon: {
|
|
||||||
enable: false,
|
|
||||||
size: 150,
|
|
||||||
alpha: 0.6,
|
|
||||||
zIndex: -1,
|
|
||||||
click_to_change: false,
|
|
||||||
mobile: false
|
|
||||||
},
|
|
||||||
canvas_fluttering_ribbon: {
|
|
||||||
enable: false,
|
|
||||||
mobile: false
|
|
||||||
},
|
|
||||||
canvas_nest: {
|
|
||||||
enable: false,
|
|
||||||
color: '0,0,255',
|
|
||||||
opacity: 0.7,
|
|
||||||
zIndex: -1,
|
|
||||||
count: 99,
|
|
||||||
mobile: false
|
|
||||||
},
|
|
||||||
fireworks: {
|
|
||||||
enable: false,
|
|
||||||
zIndex: 9999,
|
|
||||||
mobile: false
|
|
||||||
},
|
|
||||||
click_heart: {
|
|
||||||
enable: false,
|
|
||||||
mobile: false
|
|
||||||
},
|
|
||||||
clickShowText: {
|
|
||||||
enable: false,
|
|
||||||
text: null,
|
|
||||||
fontSize: '15px',
|
|
||||||
random: false,
|
|
||||||
mobile: false
|
|
||||||
},
|
|
||||||
lightbox: null,
|
|
||||||
series: {
|
|
||||||
enable: false,
|
|
||||||
orderBy: 'title',
|
|
||||||
order: 1,
|
|
||||||
number: true
|
|
||||||
},
|
|
||||||
abcjs: {
|
|
||||||
enable: false,
|
|
||||||
per_page: true
|
|
||||||
},
|
|
||||||
mermaid: {
|
|
||||||
enable: false,
|
|
||||||
code_write: false,
|
|
||||||
theme: {
|
|
||||||
light: 'default',
|
|
||||||
dark: 'dark'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
chartjs: {
|
|
||||||
enable: false,
|
|
||||||
fontColor: {
|
|
||||||
light: 'rgba(0, 0, 0, 0.8)',
|
|
||||||
dark: 'rgba(255, 255, 255, 0.8)'
|
|
||||||
},
|
|
||||||
borderColor: {
|
|
||||||
light: 'rgba(0, 0, 0, 0.1)',
|
|
||||||
dark: 'rgba(255, 255, 255, 0.2)'
|
|
||||||
},
|
|
||||||
scale_ticks_backdropColor: {
|
|
||||||
light: 'transparent',
|
|
||||||
dark: 'transparent'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
note: {
|
|
||||||
style: 'flat',
|
|
||||||
icons: true,
|
|
||||||
border_radius: 3,
|
|
||||||
light_bg_offset: 0
|
|
||||||
},
|
|
||||||
pjax: {
|
|
||||||
enable: false,
|
|
||||||
exclude: null
|
|
||||||
},
|
|
||||||
aplayerInject: {
|
|
||||||
enable: false,
|
|
||||||
per_page: true
|
|
||||||
},
|
|
||||||
snackbar: {
|
|
||||||
enable: false,
|
|
||||||
position: 'bottom-left',
|
|
||||||
bg_light: '#49b1f5',
|
|
||||||
bg_dark: '#1f1f1f'
|
|
||||||
},
|
|
||||||
instantpage: false,
|
|
||||||
lazyload: {
|
|
||||||
enable: false,
|
|
||||||
native: false,
|
|
||||||
field: 'site',
|
|
||||||
placeholder: null,
|
|
||||||
blur: false
|
|
||||||
},
|
|
||||||
pwa: {
|
|
||||||
enable: false,
|
|
||||||
manifest: null,
|
|
||||||
apple_touch_icon: null,
|
|
||||||
favicon_32_32: null,
|
|
||||||
favicon_16_16: null,
|
|
||||||
mask_icon: null
|
|
||||||
},
|
|
||||||
Open_Graph_meta: {
|
|
||||||
enable: true,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
structured_data: true,
|
|
||||||
css_prefix: true,
|
|
||||||
inject: {
|
|
||||||
head: null,
|
|
||||||
bottom: null
|
|
||||||
},
|
|
||||||
CDN: {
|
|
||||||
internal_provider: 'local',
|
|
||||||
third_party_provider: 'jsdelivr',
|
|
||||||
version: false,
|
|
||||||
custom_format: null,
|
|
||||||
option: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hexo.theme.config = deepMerge(defaultConfig, hexo.theme.config)
|
|
||||||
}, 1)
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* Butterfly
|
* Butterfly
|
||||||
* lazyload
|
* Lazyload filter
|
||||||
* replace src to data-lazy-src
|
* Replace src with data-lazy-src for lazy loading
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict'
|
'use strict'
|
||||||
@@ -10,11 +10,20 @@ const urlFor = require('hexo-util').url_for.bind(hexo)
|
|||||||
|
|
||||||
const lazyload = htmlContent => {
|
const lazyload = htmlContent => {
|
||||||
if (hexo.theme.config.lazyload.native) {
|
if (hexo.theme.config.lazyload.native) {
|
||||||
return htmlContent.replace(/(<img.*?)(>)/ig, '$1 loading=\'lazy\'$2')
|
// Use more precise replacement: only replace img tags in HTML, not content inside script tags
|
||||||
|
return htmlContent.replace(/(<img(?![^>]*?\bloading=)(?:\s[^>]*?)?>)(?![^<]*<\/script>)/gi, match => {
|
||||||
|
return match.replace(/>$/, ' loading=\'lazy\'>')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const bg = hexo.theme.config.lazyload.placeholder ? urlFor(hexo.theme.config.lazyload.placeholder) : 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
|
const bg = hexo.theme.config.lazyload.placeholder ? urlFor(hexo.theme.config.lazyload.placeholder) : 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
|
||||||
return htmlContent.replace(/(<img.*? src=)/ig, `$1 "${bg}" data-lazy-src=`)
|
|
||||||
|
// Handle src attributes with double quotes, single quotes, or no quotes (unified approach)
|
||||||
|
// Matches: src="..." or src='...' or src=... (e.g., after minification by hexo-minify)
|
||||||
|
return htmlContent.replace(/(<img(?![^>]*?\bdata-lazy-src=)(?:\s[^>]*?)?\ssrc=)(?:"([^"]*)"|'([^']*)'|([^\s>]+))(?![^<]*<\/script>)/gi, (match, prefix, srcDoubleQuote, srcSingleQuote, srcNoQuote) => {
|
||||||
|
const src = srcDoubleQuote || srcSingleQuote || srcNoQuote
|
||||||
|
return `${prefix}"${bg}" data-lazy-src="${src}"`
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
hexo.extend.filter.register('after_render:html', data => {
|
hexo.extend.filter.register('after_render:html', data => {
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
hexo.extend.helper.register('aside_archives', function (options = {}) {
|
hexo.extend.helper.register('aside_archives', function (options = {}) {
|
||||||
const { config, page, site, url_for, _p } = this
|
const { config, page, site, url_for: urlFor, _p } = this
|
||||||
const {
|
const { archive_dir: archiveDir, timezone, language } = config
|
||||||
archive_dir: archiveDir,
|
|
||||||
timezone,
|
|
||||||
language
|
|
||||||
} = config
|
|
||||||
|
|
||||||
// Destructure and set default options with object destructuring
|
// Destructure and set default options with object destructuring
|
||||||
const {
|
const {
|
||||||
@@ -22,33 +18,42 @@ hexo.extend.helper.register('aside_archives', function (options = {}) {
|
|||||||
const lang = toMomentLocale(page.lang || page.language || language)
|
const lang = toMomentLocale(page.lang || page.language || language)
|
||||||
|
|
||||||
// Memoize comparison function to improve performance
|
// Memoize comparison function to improve performance
|
||||||
const compareFunc = type === 'monthly'
|
const compareFunc =
|
||||||
? (yearA, monthA, yearB, monthB) => yearA === yearB && monthA === monthB
|
type === 'monthly'
|
||||||
: (yearA, yearB) => yearA === yearB
|
? (yearA, monthA, yearB, monthB) => yearA === yearB && monthA === monthB
|
||||||
|
: (yearA, yearB) => yearA === yearB
|
||||||
|
|
||||||
// Early return if no posts
|
// Early return if no posts
|
||||||
if (!site.posts.length) return ''
|
if (!site.posts.length) return ''
|
||||||
|
|
||||||
// Use reduce for more efficient data processing
|
// Use reduce for more efficient data processing
|
||||||
const data = site.posts
|
const data = site.posts.sort('date', order).reduce((acc, post) => {
|
||||||
.sort('date', order)
|
let date = post.date.clone()
|
||||||
.reduce((acc, post) => {
|
if (timezone) date = date.tz(timezone)
|
||||||
let date = post.date.clone()
|
|
||||||
if (timezone) date = date.tz(timezone)
|
|
||||||
|
|
||||||
const year = date.year()
|
const year = date.year()
|
||||||
const month = date.month() + 1
|
const month = date.month() + 1
|
||||||
|
|
||||||
if (lang) date = date.locale(lang)
|
if (lang) date = date.locale(lang)
|
||||||
|
|
||||||
// Find or create archive entry
|
// Find or create archive entry
|
||||||
const lastEntry = acc[acc.length - 1]
|
const lastEntry = acc[acc.length - 1]
|
||||||
if (!lastEntry || !compareFunc(
|
|
||||||
lastEntry.year,
|
if (type === 'yearly') {
|
||||||
lastEntry.month,
|
const existingYearIndex = acc.findIndex(entry => entry.year === year)
|
||||||
year,
|
if (existingYearIndex !== -1) {
|
||||||
month
|
acc[existingYearIndex].count++
|
||||||
)) {
|
} else {
|
||||||
|
// 否則創建新條目
|
||||||
|
acc.push({
|
||||||
|
name: date.format(format),
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
count: 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!lastEntry || !compareFunc(lastEntry.year, lastEntry.month, year, month)) {
|
||||||
acc.push({
|
acc.push({
|
||||||
name: date.format(format),
|
name: date.format(format),
|
||||||
year,
|
year,
|
||||||
@@ -58,9 +63,10 @@ hexo.extend.helper.register('aside_archives', function (options = {}) {
|
|||||||
} else {
|
} else {
|
||||||
lastEntry.count++
|
lastEntry.count++
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return acc
|
return acc
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// Create link generator function
|
// Create link generator function
|
||||||
const createArchiveLink = item => {
|
const createArchiveLink = item => {
|
||||||
@@ -68,43 +74,45 @@ hexo.extend.helper.register('aside_archives', function (options = {}) {
|
|||||||
if (type === 'monthly') {
|
if (type === 'monthly') {
|
||||||
url += item.month < 10 ? `0${item.month}/` : `${item.month}/`
|
url += item.month < 10 ? `0${item.month}/` : `${item.month}/`
|
||||||
}
|
}
|
||||||
return url_for(url)
|
return urlFor(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit results efficiently
|
// Limit results efficiently
|
||||||
const limitedData = limit > 0
|
const limitedData = limit > 0 ? data.slice(0, Math.min(data.length, limit)) : data
|
||||||
? data.slice(0, Math.min(data.length, limit))
|
|
||||||
: data
|
|
||||||
|
|
||||||
// Use template literal for better readability
|
// Use template literal for better readability
|
||||||
const archiveHeader = `
|
const archiveHeader = `
|
||||||
<div class="item-headline">
|
<div class="item-headline">
|
||||||
<i class="fas fa-archive"></i>
|
<i class="fas fa-archive"></i>
|
||||||
<span>${_p('aside.card_archives')}</span>
|
<span>${_p('aside.card_archives')}</span>
|
||||||
${data.length > limitedData.length
|
${
|
||||||
? `<a class="card-more-btn" href="${url_for(archiveDir)}/"
|
data.length > limitedData.length
|
||||||
|
? `<a class="card-more-btn" href="${urlFor(archiveDir)}/"
|
||||||
title="${_p('aside.more_button')}">
|
title="${_p('aside.more_button')}">
|
||||||
<i class="fas fa-angle-right"></i>
|
<i class="fas fa-angle-right"></i>
|
||||||
</a>`
|
</a>`
|
||||||
: ''}
|
: ''
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
|
|
||||||
// Use map for generating list items, join for performance
|
// Use map for generating list items, join for performance
|
||||||
const archiveList = `
|
const archiveList = `
|
||||||
<ul class="card-archive-list">
|
<ul class="card-archive-list">
|
||||||
${limitedData.map(item => `
|
${limitedData
|
||||||
|
.map(
|
||||||
|
item => `
|
||||||
<li class="card-archive-list-item">
|
<li class="card-archive-list-item">
|
||||||
<a class="card-archive-list-link" href="${createArchiveLink(item)}">
|
<a class="card-archive-list-link" href="${createArchiveLink(item)}">
|
||||||
<span class="card-archive-list-date">
|
<span class="card-archive-list-date">
|
||||||
${transform ? transform(item.name) : item.name}
|
${transform ? transform(item.name) : item.name}
|
||||||
</span>
|
</span>
|
||||||
${showCount
|
${showCount ? `<span class="card-archive-list-count">${item.count}</span>` : ''}
|
||||||
? `<span class="card-archive-list-count">${item.count}</span>`
|
|
||||||
: ''}
|
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
`).join('')}
|
`
|
||||||
|
)
|
||||||
|
.join('')}
|
||||||
</ul>
|
</ul>
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ hexo.extend.helper.register('inject_head_js', function () {
|
|||||||
const { darkmode, aside, pjax } = this.theme
|
const { darkmode, aside, pjax } = this.theme
|
||||||
const start = darkmode.start || 6
|
const start = darkmode.start || 6
|
||||||
const end = darkmode.end || 18
|
const end = darkmode.end || 18
|
||||||
const { theme_color } = hexo.theme.config
|
const { theme_color: themeColor } = hexo.theme.config
|
||||||
const themeColorLight = theme_color && theme_color.enable ? theme_color.meta_theme_color_light : '#ffffff'
|
const themeColorLight = themeColor && themeColor.enable ? themeColor.meta_theme_color_light : '#ffffff'
|
||||||
const themeColorDark = theme_color && theme_color.enable ? theme_color.meta_theme_color_dark : '#0d0d0d'
|
const themeColorDark = themeColor && themeColor.enable ? themeColor.meta_theme_color_dark : '#0d0d0d'
|
||||||
|
|
||||||
const createCustomJs = () => `
|
const createCustomJs = () => `
|
||||||
const saveToLocal = {
|
const saveToLocal = {
|
||||||
@@ -89,7 +89,7 @@ hexo.extend.helper.register('inject_head_js', function () {
|
|||||||
darkmodeJs += `
|
darkmodeJs += `
|
||||||
const mediaQueryDark = window.matchMedia('(prefers-color-scheme: dark)')
|
const mediaQueryDark = window.matchMedia('(prefers-color-scheme: dark)')
|
||||||
const mediaQueryLight = window.matchMedia('(prefers-color-scheme: light)')
|
const mediaQueryLight = window.matchMedia('(prefers-color-scheme: light)')
|
||||||
|
|
||||||
if (theme === undefined) {
|
if (theme === undefined) {
|
||||||
if (mediaQueryLight.matches) activateLightMode()
|
if (mediaQueryLight.matches) activateLightMode()
|
||||||
else if (mediaQueryDark.matches) activateDarkMode()
|
else if (mediaQueryDark.matches) activateDarkMode()
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ hexo.extend.helper.register('postDesc', data => {
|
|||||||
|
|
||||||
hexo.extend.helper.register('cloudTags', function (options = {}) {
|
hexo.extend.helper.register('cloudTags', function (options = {}) {
|
||||||
const env = this
|
const env = this
|
||||||
let { source, minfontsize, maxfontsize, limit, unit = 'px', orderby, order } = options
|
let { source, minfontsize, maxfontsize, limit, unit = 'px', orderby, order, page = 'tags' } = options
|
||||||
|
|
||||||
if (limit > 0) {
|
if (limit > 0) {
|
||||||
source = source.limit(limit)
|
source = source.limit(limit)
|
||||||
@@ -29,14 +29,19 @@ hexo.extend.helper.register('cloudTags', function (options = {}) {
|
|||||||
return `rgb(${Math.max(r, 50)}, ${Math.max(g, 50)}, ${Math.max(b, 50)})`
|
return `rgb(${Math.max(r, 50)}, ${Math.max(g, 50)}, ${Math.max(b, 50)})`
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateStyle = (size, unit) =>
|
const generateStyle = (size, unit, page) => {
|
||||||
`font-size: ${parseFloat(size.toFixed(2)) + unit}; color: ${getRandomColor()};`
|
if (page === 'tags') {
|
||||||
|
return `font-size: ${parseFloat(size.toFixed(2)) + unit}; background-color: ${getRandomColor()};`
|
||||||
|
} else {
|
||||||
|
return `font-size: ${parseFloat(size.toFixed(2)) + unit}; color: ${getRandomColor()};`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const length = sizes.length - 1
|
const length = sizes.length - 1
|
||||||
const result = source.sort(orderby, order).map(tag => {
|
const result = source.sort(orderby, order).map(tag => {
|
||||||
const ratio = length ? sizes.indexOf(tag.length) / length : 0
|
const ratio = length ? sizes.indexOf(tag.length) / length : 0
|
||||||
const size = minfontsize + ((maxfontsize - minfontsize) * ratio)
|
const size = minfontsize + ((maxfontsize - minfontsize) * ratio)
|
||||||
const style = generateStyle(size, unit)
|
const style = generateStyle(size, unit, page)
|
||||||
return `<a href="${env.url_for(tag.path)}" style="${style}">${tag.name}</a>`
|
return `<a href="${env.url_for(tag.path)}" style="${style}">${tag.name}</a>`
|
||||||
}).join('')
|
}).join('')
|
||||||
|
|
||||||
@@ -48,7 +53,7 @@ hexo.extend.helper.register('urlNoIndex', function (url = null, trailingIndex =
|
|||||||
})
|
})
|
||||||
|
|
||||||
hexo.extend.helper.register('md5', function (path) {
|
hexo.extend.helper.register('md5', function (path) {
|
||||||
return crypto.createHash('md5').update(decodeURI(this.url_for(path))).digest('hex')
|
return crypto.createHash('md5').update(decodeURI(this.url_for(path, { relative: false }))).digest('hex')
|
||||||
})
|
})
|
||||||
|
|
||||||
hexo.extend.helper.register('injectHtml', data => {
|
hexo.extend.helper.register('injectHtml', data => {
|
||||||
@@ -65,7 +70,7 @@ hexo.extend.helper.register('findArchivesTitle', function (page, menu, date) {
|
|||||||
const defaultTitle = this._p('page.archives')
|
const defaultTitle = this._p('page.archives')
|
||||||
if (!menu) return defaultTitle
|
if (!menu) return defaultTitle
|
||||||
|
|
||||||
const loop = (m) => {
|
const loop = m => {
|
||||||
for (const key in m) {
|
for (const key in m) {
|
||||||
if (typeof m[key] === 'object') {
|
if (typeof m[key] === 'object') {
|
||||||
const result = loop(m[key])
|
const result = loop(m[key])
|
||||||
@@ -81,7 +86,7 @@ hexo.extend.helper.register('findArchivesTitle', function (page, menu, date) {
|
|||||||
return loop(menu) || defaultTitle
|
return loop(menu) || defaultTitle
|
||||||
})
|
})
|
||||||
|
|
||||||
hexo.extend.helper.register('getBgPath', path => {
|
hexo.extend.helper.register('getBgPath', function (path) {
|
||||||
if (!path) return ''
|
if (!path) return ''
|
||||||
|
|
||||||
const absoluteUrlPattern = /^(?:[a-z][a-z\d+.-]*:)?\/\//i
|
const absoluteUrlPattern = /^(?:[a-z][a-z\d+.-]*:)?\/\//i
|
||||||
@@ -91,7 +96,7 @@ hexo.extend.helper.register('getBgPath', path => {
|
|||||||
if (colorPattern.test(path)) {
|
if (colorPattern.test(path)) {
|
||||||
return `background-color: ${path};`
|
return `background-color: ${path};`
|
||||||
} else if (absoluteUrlPattern.test(path) || relativeUrlPattern.test(path)) {
|
} else if (absoluteUrlPattern.test(path) || relativeUrlPattern.test(path)) {
|
||||||
return `background-image: url(${path});`
|
return `background-image: url(${this.url_for(path)});`
|
||||||
} else {
|
} else {
|
||||||
return `background: ${path};`
|
return `background: ${path};`
|
||||||
}
|
}
|
||||||
@@ -127,6 +132,8 @@ hexo.extend.helper.register('shuoshuoFN', (data, page) => {
|
|||||||
finalResult.forEach(item => {
|
finalResult.forEach(item => {
|
||||||
const utcDate = moment.utc(item.date).format('YYYY-MM-DD HH:mm:ss')
|
const utcDate = moment.utc(item.date).format('YYYY-MM-DD HH:mm:ss')
|
||||||
item.date = moment.tz(utcDate, hexo.config.timezone).format('YYYY-MM-DD HH:mm:ss')
|
item.date = moment.tz(utcDate, hexo.config.timezone).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
// markdown
|
||||||
|
item.content = hexo.render.renderSync({ text: item.content, engine: 'markdown' })
|
||||||
})
|
})
|
||||||
|
|
||||||
return finalResult
|
return finalResult
|
||||||
@@ -150,3 +157,12 @@ hexo.extend.helper.register('getVersion', () => {
|
|||||||
const { version } = require('../../package.json')
|
const { version } = require('../../package.json')
|
||||||
return { hexo: hexo.version, theme: version }
|
return { hexo: hexo.version, theme: version }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
hexo.extend.helper.register('safeJSON', data => {
|
||||||
|
// Safely serialize JSON for embedding in <script> tags
|
||||||
|
return JSON.stringify(data)
|
||||||
|
.replace(/</g, '\\u003c')
|
||||||
|
.replace(/>/g, '\\u003e')
|
||||||
|
.replace(/\u2028/g, '\\u2028')
|
||||||
|
.replace(/\u2029/g, '\\u2029')
|
||||||
|
})
|
||||||
|
|||||||
@@ -49,4 +49,4 @@ const hideToggle = (args, content) => {
|
|||||||
|
|
||||||
hexo.extend.tag.register('hideInline', hideInline)
|
hexo.extend.tag.register('hideInline', hideInline)
|
||||||
hexo.extend.tag.register('hideBlock', hideBlock, { ends: true })
|
hexo.extend.tag.register('hideBlock', hideBlock, { ends: true })
|
||||||
hexo.extend.tag.register('hideToggle', hideToggle, { ends: true })
|
hexo.extend.tag.register('hideToggle', hideToggle, { ends: true })
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
const { escapeHTML } = require('hexo-util')
|
const { escapeHTML } = require('hexo-util')
|
||||||
|
|
||||||
const mermaid = (args, content) => {
|
const mermaid = (args, content) => {
|
||||||
return `<div class="mermaid-wrap"><pre class="mermaid-src" hidden>
|
const config = args[0] || '{}'
|
||||||
|
return `<div class="mermaid-wrap"><pre class="mermaid-src" data-config="${escapeHTML(config)}" hidden>
|
||||||
${escapeHTML(content)}
|
${escapeHTML(content)}
|
||||||
</pre></div>`
|
</pre></div>`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,17 +6,45 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const score = (args, content) => {
|
const score = (args, content) => {
|
||||||
|
// Escape HTML tags and some special characters, including curly braces
|
||||||
const escapeHtmlTags = s => {
|
const escapeHtmlTags = s => {
|
||||||
const lookup = {
|
const lookup = {
|
||||||
'&': '&',
|
'&': '&',
|
||||||
'"': '"',
|
'"': '"',
|
||||||
'\'': ''',
|
"'": ''',
|
||||||
'<': '<',
|
'<': '<',
|
||||||
'>': '>'
|
'>': '>',
|
||||||
|
'{': '{',
|
||||||
|
'}': '}'
|
||||||
}
|
}
|
||||||
return s.replace(/[&"'<>]/g, c => lookup[c])
|
return s.replace(/[&"'<>{}]/g, c => lookup[c])
|
||||||
}
|
}
|
||||||
return `<div class="abc-music-sheet">${escapeHtmlTags(content)}</div>`
|
|
||||||
|
const trimmed = content.trim()
|
||||||
|
// Split content using six dashes as a delimiter
|
||||||
|
const parts = trimmed.split('------')
|
||||||
|
|
||||||
|
if (parts.length < 2) {
|
||||||
|
// If no delimiter is found, treat the entire content as the score
|
||||||
|
return `<div class="abc-music-sheet">${escapeHtmlTags(trimmed)}</div>`
|
||||||
|
}
|
||||||
|
|
||||||
|
// First part is parameters (JSON string), the rest is the score content
|
||||||
|
const paramPart = parts[0].trim()
|
||||||
|
const scorePart = parts.slice(1).join('------').trim()
|
||||||
|
|
||||||
|
let paramsObj = {}
|
||||||
|
try {
|
||||||
|
paramsObj = JSON.parse(paramPart)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to parse JSON in score tag:', e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use double quotes for data-params attribute value,
|
||||||
|
// ensuring JSON internal double quotes are escaped
|
||||||
|
return `<div class="abc-music-sheet" data-params="${escapeHtmlTags(JSON.stringify(paramsObj))}">
|
||||||
|
${escapeHtmlTags(scorePart)}
|
||||||
|
</div>`
|
||||||
}
|
}
|
||||||
|
|
||||||
hexo.extend.tag.register('score', score, { ends: true })
|
hexo.extend.tag.register('score', score, { ends: true })
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
.fontawesomeIcon
|
.fontawesomeIcon
|
||||||
display: inline-block
|
display: inline-block
|
||||||
font-weight: 600
|
font-weight: 600
|
||||||
font-family: 'Font Awesome 6 Free'
|
font-family: 'Font Awesome 7 Free', 'Font Awesome 6 Free'
|
||||||
text-rendering: auto
|
text-rendering: auto
|
||||||
-webkit-font-smoothing: antialiased
|
-webkit-font-smoothing: antialiased
|
||||||
|
|
||||||
@@ -181,6 +181,60 @@ if hexo-config('avatar.effect') == true
|
|||||||
.reward-main
|
.reward-main
|
||||||
animation: donate_effcet .3s .1s ease both
|
animation: donate_effcet .3s .1s ease both
|
||||||
|
|
||||||
|
.btn-effects
|
||||||
|
position: relative
|
||||||
|
overflow: hidden
|
||||||
|
transition: all .3s cubic-bezier(.4, 0, .2, 1)
|
||||||
|
transform: translateZ(0)
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, .15)
|
||||||
|
text-decoration: none
|
||||||
|
transform: translateY(-1px) scale(1.02)
|
||||||
|
|
||||||
|
&:active
|
||||||
|
transition-duration: .1s
|
||||||
|
transform: translateY(0) scale(.98)
|
||||||
|
|
||||||
|
i
|
||||||
|
display: inline-block
|
||||||
|
vertical-align: middle
|
||||||
|
transition: all .3s cubic-bezier(.4, 0, .2, 1)
|
||||||
|
|
||||||
|
&:hover i
|
||||||
|
animation: buttonIconBounce .6s ease-in-out
|
||||||
|
|
||||||
|
i + span
|
||||||
|
margin-left: 6px
|
||||||
|
vertical-align: middle
|
||||||
|
transition: margin-left .3s cubic-bezier(.4, 0, .2, 1)
|
||||||
|
|
||||||
|
&:hover i + span
|
||||||
|
margin-left: 8px
|
||||||
|
|
||||||
|
&::before
|
||||||
|
position: absolute
|
||||||
|
top: 0
|
||||||
|
left: -100%
|
||||||
|
z-index: 1
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .2), transparent)
|
||||||
|
content: ''
|
||||||
|
transition: left .5s cubic-bezier(.4, 0, .2, 1)
|
||||||
|
|
||||||
|
&:hover::before
|
||||||
|
left: 100%
|
||||||
|
|
||||||
|
& > *
|
||||||
|
position: relative
|
||||||
|
z-index: 2
|
||||||
|
|
||||||
|
.btn-effects-large
|
||||||
|
&:hover
|
||||||
|
box-shadow: 0 6px 16px rgba(0, 0, 0, .2)
|
||||||
|
transform: translateY(-2px) scale(1.03)
|
||||||
|
|
||||||
@keyframes scroll-down-effect
|
@keyframes scroll-down-effect
|
||||||
0%
|
0%
|
||||||
opacity: .4
|
opacity: .4
|
||||||
@@ -278,3 +332,17 @@ if hexo-config('avatar.effect') == true
|
|||||||
|
|
||||||
100%
|
100%
|
||||||
transform: translateX(0)
|
transform: translateX(0)
|
||||||
|
|
||||||
|
@keyframes buttonIconBounce
|
||||||
|
0%,
|
||||||
|
100%
|
||||||
|
transform: translateY(0) scale(1)
|
||||||
|
|
||||||
|
25%
|
||||||
|
transform: translateY(-3px) scale(1.1) rotateZ(-5deg)
|
||||||
|
|
||||||
|
50%
|
||||||
|
transform: translateY(0) scale(1.05) rotateZ(0)
|
||||||
|
|
||||||
|
75%
|
||||||
|
transform: translateY(-1px) scale(1.02) rotateZ(2deg)
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
--preloader-bg: $preloader-bg
|
--preloader-bg: $preloader-bg
|
||||||
--preloader-color: $preloader-word-color
|
--preloader-color: $preloader-word-color
|
||||||
--tab-border-color: $tab-border-color
|
--tab-border-color: $tab-border-color
|
||||||
--tab-botton-bg: $tab-botton-bg
|
--tab-button-bg: $tab-button-bg
|
||||||
--tab-botton-color: $tab-botton-color
|
--tab-button-color: $tab-button-color
|
||||||
--tab-button-hover-bg: $tab-button-hover-bg
|
--tab-button-hover-bg: $tab-button-hover-bg
|
||||||
--tab-button-active-bg: $tab-button-active-bg
|
--tab-button-active-bg: $tab-button-active-bg
|
||||||
--card-bg: $card-bg
|
--card-bg: $card-bg
|
||||||
@@ -39,6 +39,65 @@
|
|||||||
--zoom-bg: #fff
|
--zoom-bg: #fff
|
||||||
--mark-bg: alpha($dark-black, .3)
|
--mark-bg: alpha($dark-black, .3)
|
||||||
|
|
||||||
|
// tags plugin
|
||||||
|
:root
|
||||||
|
--btn-color: $btn-color
|
||||||
|
--btn-default-color: $btn-default-color
|
||||||
|
--tags-blue-color: $tagsP-blue-color
|
||||||
|
--tags-blue-color-lighten: lighten($tagsP-blue-color, 85%)
|
||||||
|
--tags-pink-color: $tagsP-pink-color
|
||||||
|
--tags-pink-color-lighten: lighten($tagsP-pink-color, 85%)
|
||||||
|
--tags-red-color: $tagsP-red-color
|
||||||
|
--tags-red-color-lighten: lighten($tagsP-red-color, 85%)
|
||||||
|
--tags-orange-color: $tagsP-orange-color
|
||||||
|
--tags-orange-color-lighten: lighten($tagsP-orange-color, 85%)
|
||||||
|
--tags-purple-color: $tagsP-purple-color
|
||||||
|
--tags-purple-color-lighten: lighten($tagsP-purple-color, 85%)
|
||||||
|
--tags-green-color: $tagsP-green-color
|
||||||
|
--tags-green-color-lighten: lighten($tagsP-green-color, 85%)
|
||||||
|
--note-default-border: $note-default-border
|
||||||
|
--note-default-bg: $note-default-bg
|
||||||
|
--note-default-text: $note-default-text
|
||||||
|
--note-modern-default-border: $note-modern-default-border
|
||||||
|
--note-modern-default-bg: $note-modern-default-bg
|
||||||
|
--note-modern-default-text: $note-modern-default-text
|
||||||
|
--note-modern-default-hover: $note-modern-default-hover
|
||||||
|
--note-primary-border: $note-primary-border
|
||||||
|
--note-primary-bg: $note-primary-bg
|
||||||
|
--note-primary-text: $note-primary-text
|
||||||
|
--note-modern-primary-border: $note-modern-primary-border
|
||||||
|
--note-modern-primary-bg: $note-modern-primary-bg
|
||||||
|
--note-modern-primary-text: $note-modern-primary-text
|
||||||
|
--note-modern-primary-hover: $note-modern-primary-hover
|
||||||
|
--note-info-border: $note-info-border
|
||||||
|
--note-info-bg: $note-info-bg
|
||||||
|
--note-info-text: $note-info-text
|
||||||
|
--note-modern-info-border: $note-modern-info-border
|
||||||
|
--note-modern-info-bg: $note-modern-info-bg
|
||||||
|
--note-modern-info-text: $note-modern-info-text
|
||||||
|
--note-modern-info-hover: $note-modern-info-hover
|
||||||
|
--note-success-border: $note-success-border
|
||||||
|
--note-success-bg: $note-success-bg
|
||||||
|
--note-success-text: $note-success-text
|
||||||
|
--note-modern-success-border: $note-modern-success-border
|
||||||
|
--note-modern-success-bg: $note-modern-success-bg
|
||||||
|
--note-modern-success-text: $note-modern-success-text
|
||||||
|
--note-modern-success-hover: $note-modern-success-hover
|
||||||
|
--note-warning-border: $note-warning-border
|
||||||
|
--note-warning-bg: $note-warning-bg
|
||||||
|
--note-warning-text: $note-warning-text
|
||||||
|
--note-modern-warning-border: $note-modern-warning-border
|
||||||
|
--note-modern-warning-bg: $note-modern-warning-bg
|
||||||
|
--note-modern-warning-text: $note-modern-warning-text
|
||||||
|
--note-modern-warning-hover: $note-modern-warning-hover
|
||||||
|
--note-danger-border: $note-danger-border
|
||||||
|
--note-danger-bg: $note-danger-bg
|
||||||
|
--note-danger-text: $note-danger-text
|
||||||
|
--note-modern-danger-border: $note-modern-danger-border
|
||||||
|
--note-modern-danger-bg: $note-modern-danger-bg
|
||||||
|
--note-modern-danger-text: $note-modern-danger-text
|
||||||
|
--note-modern-danger-hover: $note-modern-danger-hover
|
||||||
|
|
||||||
body
|
body
|
||||||
position: relative
|
position: relative
|
||||||
overflow-y: scroll
|
overflow-y: scroll
|
||||||
@@ -222,3 +281,7 @@ blockquote
|
|||||||
|
|
||||||
& > :last-child
|
& > :last-child
|
||||||
margin-bottom: 0 !important
|
margin-bottom: 0 !important
|
||||||
|
|
||||||
|
.fa-fw
|
||||||
|
width: 1.25em
|
||||||
|
text-align: center
|
||||||
@@ -100,16 +100,17 @@ $code-block
|
|||||||
.highlight-tools
|
.highlight-tools
|
||||||
display: flex
|
display: flex
|
||||||
align-items: center
|
align-items: center
|
||||||
|
overflow: hidden
|
||||||
padding: 0 8px
|
padding: 0 8px
|
||||||
min-height: 24px
|
min-height: 24px
|
||||||
height: 2.15em
|
height: 2.15em
|
||||||
background: var(--hltools-bg)
|
background: var(--hltools-bg)
|
||||||
color: var(--hltools-color)
|
color: var(--hltools-color)
|
||||||
font-size: $code-font-size
|
font-size: $code-font-size
|
||||||
overflow: hidden
|
|
||||||
|
|
||||||
& > *
|
& > *
|
||||||
padding: 5px
|
flex: 0 0 auto
|
||||||
|
margin: 2px
|
||||||
|
|
||||||
i
|
i
|
||||||
cursor: pointer
|
cursor: pointer
|
||||||
@@ -130,28 +131,25 @@ $code-block
|
|||||||
padding: 0
|
padding: 0
|
||||||
|
|
||||||
.code-lang
|
.code-lang
|
||||||
flex: 1
|
flex: 1 1 auto
|
||||||
|
overflow: hidden
|
||||||
|
padding-right: 10px
|
||||||
text-transform: uppercase
|
text-transform: uppercase
|
||||||
|
text-overflow: ellipsis
|
||||||
|
white-space: nowrap
|
||||||
font-weight: bold
|
font-weight: bold
|
||||||
font-size: 1.15em
|
font-size: 1.15em
|
||||||
user-select: none
|
user-select: none
|
||||||
-webkit-user-select: none
|
-webkit-user-select: none
|
||||||
padding 2px
|
|
||||||
|
|
||||||
.copy-notice
|
|
||||||
padding-right: 2px
|
|
||||||
opacity: 0
|
|
||||||
transition: opacity .4s
|
|
||||||
|
|
||||||
if hexo-config('code_blocks.language')
|
if hexo-config('code_blocks.language')
|
||||||
.code-lang
|
margin-right: auto
|
||||||
flex: 1
|
else if !$highlight_macstyle && hexo-config('code_blocks.shrink') != 'none'
|
||||||
else if (!$highlight_macstyle && hexo-config('code_blocks.shrink') != 'none')
|
& > :nth-child(2)
|
||||||
& > div:nth-child(2)
|
margin-right: auto
|
||||||
flex: 1
|
|
||||||
else
|
else
|
||||||
.macStyle
|
& > :nth-child(1)
|
||||||
flex: 1
|
margin-right: auto
|
||||||
|
|
||||||
.gutter
|
.gutter
|
||||||
user-select: none
|
user-select: none
|
||||||
@@ -163,11 +161,21 @@ $code-block
|
|||||||
td
|
td
|
||||||
border: none
|
border: none
|
||||||
|
|
||||||
|
.copy-notice
|
||||||
|
position: absolute
|
||||||
|
z-index: 99999
|
||||||
|
padding: 2px 6px
|
||||||
|
border-radius: 3px
|
||||||
|
background: var(--hltools-bg)
|
||||||
|
white-space: nowrap
|
||||||
|
font-size: 12px
|
||||||
|
pointer-events: none
|
||||||
|
|
||||||
if $highlight_macstyle
|
if $highlight_macstyle
|
||||||
.container
|
.container
|
||||||
figure.highlight
|
figure.highlight
|
||||||
margin: 0 0 24px
|
margin: 0 0 24px
|
||||||
border-radius: 7px
|
border-radius: 8px
|
||||||
box-shadow: 0 5px 10px 0 $highlight-mac-border
|
box-shadow: 0 5px 10px 0 $highlight-mac-border
|
||||||
-webkit-transform: translateZ(0)
|
-webkit-transform: translateZ(0)
|
||||||
|
|
||||||
@@ -231,7 +239,7 @@ if hexo-config('code_blocks.height_limit')
|
|||||||
& ~ pre
|
& ~ pre
|
||||||
overflow: hidden
|
overflow: hidden
|
||||||
height: unit(hexo-config('code_blocks.height_limit'), px)
|
height: unit(hexo-config('code_blocks.height_limit'), px)
|
||||||
|
|
||||||
@keyframes code-expand-key
|
@keyframes code-expand-key
|
||||||
0%
|
0%
|
||||||
opacity: .6
|
opacity: .6
|
||||||
@@ -264,8 +272,8 @@ if hexo-config('code_blocks.fullpage')
|
|||||||
& ~ table
|
& ~ table
|
||||||
display: block
|
display: block
|
||||||
overflow: auto
|
overflow: auto
|
||||||
height: calc(100vh - 2.15em)
|
|
||||||
margin-bottom: 0
|
margin-bottom: 0
|
||||||
|
height: calc(100vh - 2.15em)
|
||||||
|
|
||||||
@keyframes code-fullpage
|
@keyframes code-fullpage
|
||||||
0%,
|
0%,
|
||||||
|
|||||||
@@ -23,8 +23,8 @@
|
|||||||
&:not(#card-toc)
|
&:not(#card-toc)
|
||||||
display: none
|
display: none
|
||||||
|
|
||||||
&:last-child
|
// &:last-child
|
||||||
margin-bottom: 0
|
// margin-bottom: 0
|
||||||
|
|
||||||
.card-info
|
.card-info
|
||||||
.author-info
|
.author-info
|
||||||
@@ -60,6 +60,7 @@
|
|||||||
text-align: center
|
text-align: center
|
||||||
line-height: 2.4
|
line-height: 2.4
|
||||||
addBorderRadius(7)
|
addBorderRadius(7)
|
||||||
|
@extend .btn-effects
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
background-color: var(--btn-hover-color)
|
background-color: var(--btn-hover-color)
|
||||||
@@ -180,6 +181,11 @@
|
|||||||
.card-category-list
|
.card-category-list
|
||||||
&.child
|
&.child
|
||||||
padding: 0 0 0 16px
|
padding: 0 0 0 16px
|
||||||
|
overflow: hidden
|
||||||
|
max-height: 0
|
||||||
|
opacity: 0
|
||||||
|
visibility: hidden
|
||||||
|
transition: max-height .3s ease, opacity .3s ease
|
||||||
|
|
||||||
> .parent
|
> .parent
|
||||||
> a
|
> a
|
||||||
@@ -188,7 +194,9 @@
|
|||||||
transform: rotate(-90deg)
|
transform: rotate(-90deg)
|
||||||
|
|
||||||
& + .child
|
& + .child
|
||||||
display: block
|
max-height: 1000px
|
||||||
|
opacity: 1
|
||||||
|
visibility: visible
|
||||||
|
|
||||||
.card-category-list
|
.card-category-list
|
||||||
&-name
|
&-name
|
||||||
@@ -207,7 +215,9 @@
|
|||||||
|
|
||||||
if hexo-config('aside.card_categories.expand') == false
|
if hexo-config('aside.card_categories.expand') == false
|
||||||
> .child
|
> .child
|
||||||
display: none
|
max-height: 0
|
||||||
|
opacity: 0
|
||||||
|
visibility: hidden
|
||||||
|
|
||||||
.card-webinfo
|
.card-webinfo
|
||||||
.webinfo
|
.webinfo
|
||||||
|
|||||||
@@ -13,17 +13,16 @@
|
|||||||
background-color: var(--mark-bg)
|
background-color: var(--mark-bg)
|
||||||
content: ''
|
content: ''
|
||||||
|
|
||||||
#footer-wrap
|
& > *
|
||||||
position: relative
|
position: relative
|
||||||
padding: 40px 20px
|
color: var(--light-grey)
|
||||||
color: var(--light-grey)
|
|
||||||
text-align: center
|
|
||||||
|
|
||||||
a
|
a
|
||||||
color: var(--light-grey)
|
color: var(--light-grey)
|
||||||
|
transition: all .3s ease-in-out
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
text-decoration: underline
|
color: $light-blue
|
||||||
|
|
||||||
.footer-separator
|
.footer-separator
|
||||||
margin: 0 4px
|
margin: 0 4px
|
||||||
@@ -33,3 +32,56 @@
|
|||||||
max-height: 1.4em
|
max-height: 1.4em
|
||||||
width: auto
|
width: auto
|
||||||
vertical-align: text-bottom
|
vertical-align: text-bottom
|
||||||
|
|
||||||
|
.footer-flex
|
||||||
|
display: flex
|
||||||
|
flex-direction: row
|
||||||
|
flex-wrap: wrap
|
||||||
|
justify-content: space-between
|
||||||
|
margin: 0 auto
|
||||||
|
padding: 40px 60px
|
||||||
|
max-width: 1200px
|
||||||
|
width: 100%
|
||||||
|
text-align: left
|
||||||
|
gap: 13px
|
||||||
|
|
||||||
|
+maxWidth768()
|
||||||
|
padding: 30px
|
||||||
|
gap: 10px
|
||||||
|
|
||||||
|
.footer-flex-items
|
||||||
|
flex-shrink: 0
|
||||||
|
min-width: 100px
|
||||||
|
text-align: left
|
||||||
|
white-space: nowrap
|
||||||
|
|
||||||
|
.footer-flex-title
|
||||||
|
margin-bottom: 5px
|
||||||
|
white-space: nowrap
|
||||||
|
font-weight: 600
|
||||||
|
font-size: 1.4em
|
||||||
|
|
||||||
|
.footer-flex-item
|
||||||
|
margin: 10px 0
|
||||||
|
white-space: nowrap
|
||||||
|
|
||||||
|
a
|
||||||
|
display: block
|
||||||
|
white-space: nowrap
|
||||||
|
|
||||||
|
.footer-other
|
||||||
|
padding: 40px 20px
|
||||||
|
width: 100%
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
if hexo-config('footer.nav')
|
||||||
|
padding: 10px 8px
|
||||||
|
background-color: rgba(0, 0, 0, .1)
|
||||||
|
|
||||||
|
.copyright,
|
||||||
|
.framework-info,
|
||||||
|
.footer_custom_text
|
||||||
|
font-size: .9em
|
||||||
|
else
|
||||||
|
.framework-info
|
||||||
|
display: block
|
||||||
@@ -435,4 +435,29 @@
|
|||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
&:after
|
&:after
|
||||||
width: 100%
|
width: 100%
|
||||||
|
|
||||||
|
.nav-page-title
|
||||||
|
position: relative
|
||||||
|
overflow: hidden
|
||||||
|
|
||||||
|
& > :first-child,
|
||||||
|
& > :last-child
|
||||||
|
display: inline-block
|
||||||
|
transition: all .3s ease-in-out
|
||||||
|
|
||||||
|
& > :last-child
|
||||||
|
position: absolute
|
||||||
|
top: 50%
|
||||||
|
left: 0
|
||||||
|
opacity: 0
|
||||||
|
transform: translateY(-50%) translateY(-10px)
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
& > :last-child
|
||||||
|
opacity: 1
|
||||||
|
transform: translateY(-50%) translateY(0)
|
||||||
|
|
||||||
|
& > :first-child
|
||||||
|
opacity: 0
|
||||||
|
transform: translateY(10px)
|
||||||
@@ -15,9 +15,12 @@
|
|||||||
color: var(--btn-color)
|
color: var(--btn-color)
|
||||||
cursor: pointer
|
cursor: pointer
|
||||||
addBorderRadius()
|
addBorderRadius()
|
||||||
|
@extend .btn-effects
|
||||||
|
@extend .btn-effects-large
|
||||||
|
|
||||||
i
|
i
|
||||||
margin-right: 5px
|
margin-right: 5px
|
||||||
|
vertical-align: baseline
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
.reward-button
|
.reward-button
|
||||||
@@ -28,7 +31,7 @@
|
|||||||
|
|
||||||
.reward-main
|
.reward-main
|
||||||
position: absolute
|
position: absolute
|
||||||
bottom: 40px
|
bottom: 50px
|
||||||
left: 0
|
left: 0
|
||||||
z-index: 100
|
z-index: 100
|
||||||
display: none
|
display: none
|
||||||
|
|||||||
@@ -38,9 +38,13 @@
|
|||||||
font-size: 16px
|
font-size: 16px
|
||||||
line-height: w
|
line-height: w
|
||||||
addBorderRadius(5)
|
addBorderRadius(5)
|
||||||
|
@extend .btn-effects
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
background-color: var(--btn-hover-color)
|
background-color: var(--btn-hover-color)
|
||||||
|
|
||||||
|
i
|
||||||
|
vertical-align: baseline
|
||||||
|
|
||||||
#mobile-toc-button
|
#mobile-toc-button
|
||||||
display: none
|
display: none
|
||||||
@@ -54,19 +58,52 @@
|
|||||||
|
|
||||||
if hexo-config('rightside_scroll_percent')
|
if hexo-config('rightside_scroll_percent')
|
||||||
#go-up
|
#go-up
|
||||||
|
position: relative
|
||||||
|
|
||||||
.scroll-percent
|
.scroll-percent
|
||||||
|
position: absolute
|
||||||
|
top: 0
|
||||||
|
left: 0
|
||||||
display: none
|
display: none
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
opacity: 0
|
||||||
|
transition: all .4s cubic-bezier(.4, 0, .2, 1)
|
||||||
|
transform: scale(.8)
|
||||||
|
|
||||||
|
i
|
||||||
|
position: relative
|
||||||
|
z-index: 1
|
||||||
|
width: 100%
|
||||||
|
opacity: 1
|
||||||
|
transition: all .4s cubic-bezier(.4, 0, .2, 1)
|
||||||
|
transform: scale(1)
|
||||||
|
|
||||||
&.show-percent
|
&.show-percent
|
||||||
.scroll-percent
|
.scroll-percent
|
||||||
display: block
|
display: block
|
||||||
|
opacity: 1
|
||||||
|
transform: scale(1)
|
||||||
|
animation: fadeInScale .4s ease-out
|
||||||
|
|
||||||
& + i
|
i
|
||||||
display: none
|
opacity: 0
|
||||||
|
transform: scale(.8)
|
||||||
|
|
||||||
&:hover
|
&:hover.show-percent
|
||||||
.scroll-percent
|
.scroll-percent
|
||||||
display: none
|
opacity: 0
|
||||||
|
transform: scale(.8)
|
||||||
|
|
||||||
& + i
|
i
|
||||||
display: block
|
opacity: 1 !important
|
||||||
|
transform: scale(1) !important
|
||||||
|
|
||||||
|
@keyframes fadeInScale
|
||||||
|
from
|
||||||
|
opacity: 0
|
||||||
|
transform: scale(.8)
|
||||||
|
|
||||||
|
to
|
||||||
|
opacity: 1
|
||||||
|
transform: scale(1)
|
||||||
@@ -52,7 +52,10 @@
|
|||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
background: var(--text-bg-hover)
|
background: var(--text-bg-hover)
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, .1)
|
||||||
color: var(--white)
|
color: var(--white)
|
||||||
|
transition: all .2s ease
|
||||||
|
transform: translateX(3px)
|
||||||
|
|
||||||
i:first-child
|
i:first-child
|
||||||
width: 15%
|
width: 15%
|
||||||
@@ -70,9 +73,25 @@
|
|||||||
transform: rotate(90deg)
|
transform: rotate(90deg)
|
||||||
|
|
||||||
& + .menus_item_child
|
& + .menus_item_child
|
||||||
display: none
|
overflow: hidden
|
||||||
|
max-height: 0
|
||||||
|
opacity: 0
|
||||||
|
transform: scaleY(0)
|
||||||
|
transform-origin: top
|
||||||
|
|
||||||
.menus_item_child
|
.menus_item_child
|
||||||
margin: 0
|
margin: 0
|
||||||
padding-left: 25px
|
padding-left: 25px
|
||||||
list-style: none
|
max-height: 0
|
||||||
|
list-style: none
|
||||||
|
opacity: 0
|
||||||
|
transition: transform .3s ease, opacity .3s ease, max-height .3s ease
|
||||||
|
transform: scaleY(0)
|
||||||
|
transform-origin: top
|
||||||
|
will-change: transform, opacity, max-height
|
||||||
|
|
||||||
|
// 當父元素沒有 .hide 類時,顯示子目錄
|
||||||
|
.site-page.group:not(.hide) + .menus_item_child
|
||||||
|
max-height: 1000px
|
||||||
|
opacity: 1
|
||||||
|
transform: scaleY(1)
|
||||||
@@ -10,8 +10,8 @@ if hexo-config('darkmode.enable') || hexo-config('display_mode') == 'dark'
|
|||||||
--preloader-bg: darken(#121212, 2)
|
--preloader-bg: darken(#121212, 2)
|
||||||
--preloader-color: alpha(#FFFFFF, .7)
|
--preloader-color: alpha(#FFFFFF, .7)
|
||||||
--tab-border-color: #2c2c2c
|
--tab-border-color: #2c2c2c
|
||||||
--tab-botton-bg: #2c2c2c
|
--tab-button-bg: #2c2c2c
|
||||||
--tab-botton-color: alpha(#FFFFFF, .7)
|
--tab-button-color: alpha(#FFFFFF, .7)
|
||||||
--tab-button-hover-bg: lighten(#121212, 15)
|
--tab-button-hover-bg: lighten(#121212, 15)
|
||||||
--tab-button-active-bg: #121212
|
--tab-button-active-bg: #121212
|
||||||
--card-bg: #121212
|
--card-bg: #121212
|
||||||
@@ -33,6 +33,56 @@ if hexo-config('darkmode.enable') || hexo-config('display_mode') == 'dark'
|
|||||||
--timeline-bg: lighten(#121212, 5)
|
--timeline-bg: lighten(#121212, 5)
|
||||||
--zoom-bg: #121212
|
--zoom-bg: #121212
|
||||||
--mark-bg: alpha($dark-black, .6)
|
--mark-bg: alpha($dark-black, .6)
|
||||||
|
--btn-color: darken($btn-color, 20%)
|
||||||
|
--btn-default-color: lighten($btn-default-color, 20%)
|
||||||
|
--tags-blue-color: desaturate(darken($tagsP-blue-color, 20%), 25%)
|
||||||
|
--tags-blue-color-lighten: rgba(66, 139, 202, .15)
|
||||||
|
--tags-pink-color: desaturate(darken($tagsP-pink-color, 22%), 30%)
|
||||||
|
--tags-pink-color-lighten: rgba(255, 105, 180, .15)
|
||||||
|
--tags-red-color: desaturate(darken($tagsP-red-color, 25%), 28%)
|
||||||
|
--tags-red-color-lighten: rgba(255, 0, 0, .15)
|
||||||
|
--tags-orange-color: desaturate(darken($tagsP-orange-color, 22%), 32%)
|
||||||
|
--tags-orange-color-lighten: rgba(255, 140, 0, .15)
|
||||||
|
--tags-purple-color: desaturate(darken($tagsP-purple-color, 18%), 30%)
|
||||||
|
--tags-purple-color-lighten: rgba(111, 66, 193, .15)
|
||||||
|
--tags-green-color: desaturate(darken($tagsP-green-color, 20%), 28%)
|
||||||
|
--tags-green-color-lighten: rgba(92, 184, 92, .15)
|
||||||
|
--note-default-border: $note-dark-default-border
|
||||||
|
--note-default-bg: $note-dark-default-bg
|
||||||
|
--note-default-text: $note-dark-default-text
|
||||||
|
--note-modern-default-border: $note-dark-modern-default-border
|
||||||
|
--note-modern-default-bg: $note-dark-modern-default-bg
|
||||||
|
--note-modern-default-text: $note-dark-modern-default-text
|
||||||
|
--note-primary-border: $note-dark-primary-border
|
||||||
|
--note-primary-bg: $note-dark-primary-bg
|
||||||
|
--note-primary-text: $note-dark-primary-text
|
||||||
|
--note-modern-primary-border: $note-dark-modern-primary-border
|
||||||
|
--note-modern-primary-bg: $note-dark-modern-primary-bg
|
||||||
|
--note-modern-primary-text: $note-dark-modern-primary-text
|
||||||
|
--note-info-border: $note-dark-info-border
|
||||||
|
--note-info-bg: $note-dark-info-bg
|
||||||
|
--note-info-text: $note-dark-info-text
|
||||||
|
--note-modern-info-border: $note-dark-modern-info-border
|
||||||
|
--note-modern-info-bg: $note-dark-modern-info-bg
|
||||||
|
--note-modern-info-text: $note-dark-modern-info-text
|
||||||
|
--note-success-border: $note-dark-success-border
|
||||||
|
--note-success-bg: $note-dark-success-bg
|
||||||
|
--note-success-text: $note-dark-success-text
|
||||||
|
--note-modern-success-border: $note-dark-modern-success-border
|
||||||
|
--note-modern-success-bg: $note-dark-modern-success-bg
|
||||||
|
--note-modern-success-text: $note-dark-modern-success-text
|
||||||
|
--note-warning-border: $note-dark-warning-border
|
||||||
|
--note-warning-bg: $note-dark-warning-bg
|
||||||
|
--note-warning-text: $note-dark-warning-text
|
||||||
|
--note-modern-warning-border: $note-dark-modern-warning-border
|
||||||
|
--note-modern-warning-bg: $note-dark-modern-warning-bg
|
||||||
|
--note-modern-warning-text: $note-dark-modern-warning-text
|
||||||
|
--note-danger-border: $note-dark-danger-border
|
||||||
|
--note-danger-bg: $note-dark-danger-bg
|
||||||
|
--note-danger-text: $note-dark-danger-text
|
||||||
|
--note-modern-danger-border: $note-dark-modern-danger-border
|
||||||
|
--note-modern-danger-bg: $note-dark-modern-danger-bg
|
||||||
|
--note-modern-danger-text: $note-dark-modern-danger-text
|
||||||
|
|
||||||
#web_bg:before
|
#web_bg:before
|
||||||
position: absolute
|
position: absolute
|
||||||
@@ -78,23 +128,17 @@ if hexo-config('darkmode.enable') || hexo-config('display_mode') == 'dark'
|
|||||||
#switch-btn
|
#switch-btn
|
||||||
filter: brightness(.8)
|
filter: brightness(.8)
|
||||||
|
|
||||||
// note
|
|
||||||
if hexo-config('note.style') == 'modern' || hexo-config('note.style') == 'flat'
|
|
||||||
.note
|
|
||||||
filter: brightness(.8)
|
|
||||||
|
|
||||||
// hide-tags
|
// hide-tags
|
||||||
.hide-button,
|
.hide-button,
|
||||||
.btn-beautify,
|
|
||||||
.hl-label,
|
|
||||||
#post-outdate-notice,
|
#post-outdate-notice,
|
||||||
.error-img,
|
.error-img,
|
||||||
.container iframe,
|
.container iframe,
|
||||||
.gist,
|
.gist,
|
||||||
.ads-wrap
|
.ads-wrap,
|
||||||
|
.tag-cloud-list > a
|
||||||
filter: brightness(.8)
|
filter: brightness(.8)
|
||||||
|
|
||||||
img
|
img:not(.cover)
|
||||||
if hexo-config('lazyload.enable') && hexo-config('lazyload.blur') && !hexo-config('lazyload.placeholder')
|
if hexo-config('lazyload.enable') && hexo-config('lazyload.blur') && !hexo-config('lazyload.placeholder')
|
||||||
filter: blur(0) brightness(.8)
|
filter: blur(0) brightness(.8)
|
||||||
else
|
else
|
||||||
@@ -148,7 +192,7 @@ if hexo-config('darkmode.enable') || hexo-config('display_mode') == 'dark'
|
|||||||
--at-color-font: alpha(#FFFFFF, .7)
|
--at-color-font: alpha(#FFFFFF, .7)
|
||||||
--at-color-meta: alpha(#FFFFFF, .7)
|
--at-color-meta: alpha(#FFFFFF, .7)
|
||||||
--at-color-grey: alpha(#FFFFFF, .7)
|
--at-color-grey: alpha(#FFFFFF, .7)
|
||||||
|
|
||||||
.atk-send-btn,
|
.atk-send-btn,
|
||||||
.atk-badge
|
.atk-badge
|
||||||
color: alpha(#FFFFFF, .7) !important
|
color: alpha(#FFFFFF, .7) !important
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
padding: 20px 5px
|
padding: 20px 5px
|
||||||
|
|
||||||
+minWidth2000()
|
+minWidth2000()
|
||||||
max-width: 70%
|
max-width: 60%
|
||||||
|
|
||||||
& > div:first-child:not(.nc)
|
& > div:first-child:not(.nc)
|
||||||
@extend .cardHover
|
@extend .cardHover
|
||||||
|
|||||||
@@ -171,4 +171,5 @@ $indexEnable = hexo-config('cover.index_enable')
|
|||||||
|
|
||||||
& > .content
|
& > .content
|
||||||
@extend .limit-more-line
|
@extend .limit-more-line
|
||||||
-webkit-line-clamp: 2
|
-webkit-line-clamp: 2
|
||||||
|
word-break: break-word
|
||||||
@@ -76,3 +76,110 @@
|
|||||||
|
|
||||||
&.no-comment
|
&.no-comment
|
||||||
display: none
|
display: none
|
||||||
|
|
||||||
|
.shuoshuo-navigation
|
||||||
|
display: flex
|
||||||
|
justify-content: center
|
||||||
|
align-items: center
|
||||||
|
margin-top: 20px
|
||||||
|
padding: 20px 0
|
||||||
|
|
||||||
|
button
|
||||||
|
display: flex
|
||||||
|
justify-content: center
|
||||||
|
align-items: center
|
||||||
|
width: 2.7em
|
||||||
|
height: 2.7em
|
||||||
|
background-color: var(--btn-bg)
|
||||||
|
color: var(--btn-color)
|
||||||
|
font-size: .9em
|
||||||
|
line-height: 2.5em
|
||||||
|
transition: all .2s ease-in-out
|
||||||
|
addBorderRadius(6)
|
||||||
|
|
||||||
|
&:not(:disabled)
|
||||||
|
@extend .btn-effects
|
||||||
|
|
||||||
|
&:hover:not(:disabled)
|
||||||
|
background-color: var(--btn-hover-color)
|
||||||
|
|
||||||
|
&:disabled
|
||||||
|
background: #f5f5f5
|
||||||
|
color: #ccc
|
||||||
|
opacity: .5
|
||||||
|
cursor: not-allowed
|
||||||
|
|
||||||
|
.shuoshuo-page-info
|
||||||
|
margin: 0 15px
|
||||||
|
color: #858585
|
||||||
|
white-space: nowrap
|
||||||
|
font-size: .9em
|
||||||
|
|
||||||
|
.shuoshuo-page-input
|
||||||
|
margin-right: 12px
|
||||||
|
padding: 0 15px
|
||||||
|
height: 2.7em
|
||||||
|
border: 1px solid var(--btn-bg)
|
||||||
|
background: var(--card-bg)
|
||||||
|
color: #858585
|
||||||
|
text-align: center
|
||||||
|
font-size: .9em
|
||||||
|
transition: all .2s ease-in-out
|
||||||
|
addBorderRadius(6)
|
||||||
|
|
||||||
|
&:focus
|
||||||
|
outline: none
|
||||||
|
border-width: 2px
|
||||||
|
|
||||||
|
&::placeholder
|
||||||
|
color: transparent
|
||||||
|
|
||||||
|
/* 隱藏 number 輸入框的上下箭頭 */
|
||||||
|
&::-webkit-outer-spin-button,
|
||||||
|
&::-webkit-inner-spin-button
|
||||||
|
margin: 0
|
||||||
|
-webkit-appearance: none
|
||||||
|
|
||||||
|
/* Firefox */
|
||||||
|
-moz-appearance: textfield
|
||||||
|
|
||||||
|
/* 當作為頁碼按鈕時的樣式 */
|
||||||
|
&.shuoshuo-page-num
|
||||||
|
min-width: 40px
|
||||||
|
width: 40px
|
||||||
|
border: none
|
||||||
|
background: $light-blue
|
||||||
|
color: var(--white)
|
||||||
|
font-weight: 500
|
||||||
|
cursor: text
|
||||||
|
|
||||||
|
&:focus
|
||||||
|
border: 1px solid $light-blue
|
||||||
|
background: var(--white)
|
||||||
|
color: #333
|
||||||
|
|
||||||
|
/* 超出範圍時的紅色樣式 */
|
||||||
|
&.invalid
|
||||||
|
border-color: #ff4757
|
||||||
|
background-color: #ffeaea
|
||||||
|
color: #ff4757
|
||||||
|
animation: shake .5s ease-in-out
|
||||||
|
|
||||||
|
/* 震動動畫 */
|
||||||
|
@keyframes shake
|
||||||
|
0%,
|
||||||
|
100%
|
||||||
|
transform: translateX(0)
|
||||||
|
|
||||||
|
10%,
|
||||||
|
30%,
|
||||||
|
50%,
|
||||||
|
70%,
|
||||||
|
90%
|
||||||
|
transform: translateX(-2px)
|
||||||
|
|
||||||
|
20%,
|
||||||
|
40%,
|
||||||
|
60%,
|
||||||
|
80%
|
||||||
|
transform: translateX(2px)
|
||||||
|
|||||||
@@ -1,27 +1,91 @@
|
|||||||
.tag-cloud
|
.tag-cloud
|
||||||
&-list
|
&-list
|
||||||
|
animation: tagsFadeIn .6s cubic-bezier(.4, 0, .2, 1)
|
||||||
|
|
||||||
|
&:hover a:not(:hover)
|
||||||
|
opacity: .7
|
||||||
|
transform: scale(.98)
|
||||||
|
|
||||||
a
|
a
|
||||||
|
position: relative
|
||||||
display: inline-block
|
display: inline-block
|
||||||
margin: 2px
|
margin: 5px
|
||||||
padding: 2px 7px
|
padding: 3px 12px
|
||||||
line-height: 1.7
|
line-height: 1.7
|
||||||
transition: all .3s
|
transition: all .3s cubic-bezier(.4, 0, .2, 1)
|
||||||
addBorderRadius(5)
|
addBorderRadius(7)
|
||||||
|
overflow: hidden
|
||||||
|
color: white
|
||||||
|
transform: translateY(0) scale(1)
|
||||||
|
will-change: transform, background-color, box-shadow
|
||||||
|
|
||||||
|
&::before
|
||||||
|
position: absolute
|
||||||
|
top: 0
|
||||||
|
left: -100%
|
||||||
|
z-index: -1
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .1), transparent)
|
||||||
|
content: ''
|
||||||
|
transition: left .6s cubic-bezier(.4, 0, .2, 1)
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
background: var(--btn-bg) !important
|
background: var(--btn-hover-color) !important
|
||||||
box-shadow: 2px 2px 6px rgba(0, 0, 0, .2)
|
box-shadow:
|
||||||
|
0 6px 20px rgba(0, 0, 0, .12),
|
||||||
|
0 4px 8px rgba(0, 0, 0, .08),
|
||||||
|
0 0 0 1px rgba(255, 255, 255, .05)
|
||||||
color: var(--btn-color) !important
|
color: var(--btn-color) !important
|
||||||
|
transform: translateY(-2px) scale(1.02)
|
||||||
|
|
||||||
|
&::before
|
||||||
|
left: 100%
|
||||||
|
|
||||||
|
&:active
|
||||||
|
box-shadow:
|
||||||
|
0 3px 8px rgba(0, 0, 0, .15),
|
||||||
|
0 1px 3px rgba(0, 0, 0, .1)
|
||||||
|
transition: all .15s cubic-bezier(.4, 0, .2, 1)
|
||||||
|
transform: translateY(-1px) scale(.98)
|
||||||
|
|
||||||
+maxWidth768()
|
+maxWidth768()
|
||||||
zoom: .85
|
zoom: .85
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
transform: translateY(-1px) scale(1.01)
|
||||||
|
|
||||||
|
&:active
|
||||||
|
transform: translateY(0) scale(.99)
|
||||||
|
|
||||||
|
&::before
|
||||||
|
display: none
|
||||||
|
|
||||||
&-title
|
&-title
|
||||||
font-size: 2.57em
|
font-size: 2.57em
|
||||||
|
animation: titleSlideIn .8s cubic-bezier(.4, 0, .2, 1)
|
||||||
|
|
||||||
+maxWidth768()
|
+maxWidth768()
|
||||||
font-size: 2em
|
font-size: 2em
|
||||||
|
|
||||||
|
@keyframes tagsFadeIn
|
||||||
|
from
|
||||||
|
opacity: 0
|
||||||
|
transform: translateY(20px)
|
||||||
|
|
||||||
|
to
|
||||||
|
opacity: 1
|
||||||
|
transform: translateY(0)
|
||||||
|
|
||||||
|
@keyframes titleSlideIn
|
||||||
|
from
|
||||||
|
opacity: 0
|
||||||
|
transform: translateX(-30px)
|
||||||
|
|
||||||
|
to
|
||||||
|
opacity: 1
|
||||||
|
transform: translateX(0)
|
||||||
|
|
||||||
.page-title
|
.page-title
|
||||||
& + .tag-cloud-list
|
& + .tag-cloud-list
|
||||||
text-align: left
|
text-align: left
|
||||||
|
|||||||
@@ -1,93 +1,13 @@
|
|||||||
#algolia-search
|
#algolia-search
|
||||||
.search-dialog
|
.search-dialog
|
||||||
.ais-SearchBox
|
|
||||||
input
|
|
||||||
padding: 5px 14px
|
|
||||||
width: 100%
|
|
||||||
outline: none
|
|
||||||
border: 2px solid $search-color
|
|
||||||
border-radius: 40px
|
|
||||||
background: var(--search-bg)
|
|
||||||
color: var(--search-input-color)
|
|
||||||
|
|
||||||
.ais-SearchBox-loadingIndicator
|
|
||||||
position: absolute
|
|
||||||
top: 18px
|
|
||||||
left: 67px
|
|
||||||
|
|
||||||
.ais-Hits-list
|
.ais-Hits-list
|
||||||
margin: 0
|
+maxWidth768()
|
||||||
padding: 0
|
min-height: calc(var(--search-height) - 245px)
|
||||||
@extend .list-beauty
|
|
||||||
|
|
||||||
a
|
|
||||||
color: var(--search-a-color)
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
color: $search-color
|
|
||||||
|
|
||||||
mark
|
|
||||||
background: transparent
|
|
||||||
color: $search-keyword-highlight
|
|
||||||
font-weight: bold
|
|
||||||
|
|
||||||
.algolia-hits-item-title
|
|
||||||
font-weight: 600
|
|
||||||
|
|
||||||
.algolia-hit-item-content
|
|
||||||
margin: 0 0 8px
|
|
||||||
word-break: break-word
|
|
||||||
|
|
||||||
.ais-Pagination
|
|
||||||
margin: 15px 0 0
|
|
||||||
padding: 0
|
|
||||||
text-align: center
|
|
||||||
|
|
||||||
.ais-Pagination-list
|
|
||||||
margin: 0
|
|
||||||
padding: 0
|
|
||||||
list-style: none
|
|
||||||
|
|
||||||
.ais-Pagination-item
|
|
||||||
display: inline
|
|
||||||
margin: 0 4px
|
|
||||||
padding: 0
|
|
||||||
|
|
||||||
.ais-Pagination-link
|
|
||||||
display: inline-block
|
|
||||||
min-width: 24px
|
|
||||||
height: 24px
|
|
||||||
text-align: center
|
|
||||||
line-height: 24px
|
|
||||||
addBorderRadius()
|
|
||||||
|
|
||||||
.ais-Pagination-item--selected
|
|
||||||
a
|
|
||||||
background: $theme-paginator-color
|
|
||||||
color: #eee
|
|
||||||
cursor: default
|
|
||||||
|
|
||||||
.ais-Pagination-item--disabled
|
|
||||||
visibility: hidden
|
|
||||||
|
|
||||||
#algolia-hits
|
|
||||||
> div
|
|
||||||
overflow-y: overlay
|
|
||||||
margin: 0 -20px
|
|
||||||
padding: 0 22px
|
|
||||||
max-height: calc(80vh - 220px)
|
|
||||||
|
|
||||||
+maxWidth768()
|
|
||||||
max-height: none
|
|
||||||
height: calc(var(--search-height) - 235px)
|
|
||||||
|
|
||||||
#algolia-info
|
#algolia-info
|
||||||
div
|
|
||||||
display: inline
|
|
||||||
|
|
||||||
.algolia-poweredBy
|
.algolia-poweredBy
|
||||||
float: right
|
float: right
|
||||||
vertical-align: text-top
|
padding-top: 2px
|
||||||
|
|
||||||
svg
|
svg
|
||||||
height: 1.1em
|
height: 1.1em
|
||||||
@@ -20,22 +20,174 @@
|
|||||||
border-radius: 0
|
border-radius: 0
|
||||||
|
|
||||||
.search-nav
|
.search-nav
|
||||||
margin: 0 0 14px
|
display: flex
|
||||||
|
justify-content: space-between
|
||||||
|
align-items: center
|
||||||
|
margin-bottom: 14px
|
||||||
color: $search-color
|
color: $search-color
|
||||||
font-size: 1.4em
|
font-size: 1.4em
|
||||||
line-height: 1
|
line-height: 1
|
||||||
|
|
||||||
.search-dialog-title
|
.search-dialog-title
|
||||||
margin-right: 10px
|
margin-right: 4px
|
||||||
|
|
||||||
|
#loading-status
|
||||||
|
&[hidden]
|
||||||
|
display: none !important
|
||||||
|
|
||||||
.search-close-button
|
.search-close-button
|
||||||
float: right
|
flex: 1
|
||||||
color: $grey
|
color: $grey
|
||||||
transition: color .2s ease-in-out
|
text-align: right
|
||||||
|
transition: all .2s ease
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
color: $search-color
|
color: $search-color
|
||||||
|
|
||||||
|
.local-search-input,
|
||||||
|
#algolia-search-input
|
||||||
|
margin: 0 auto
|
||||||
|
max-width: 100%
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
input,
|
||||||
|
.ais-SearchBox-input
|
||||||
|
padding: 5px 14px
|
||||||
|
width: 100%
|
||||||
|
outline: none
|
||||||
|
border: 2px solid $search-color
|
||||||
|
border-radius: 40px
|
||||||
|
background: var(--search-bg)
|
||||||
|
color: var(--search-input-color)
|
||||||
|
-webkit-appearance: none
|
||||||
|
|
||||||
|
&::placeholder
|
||||||
|
color: var(--text-color)
|
||||||
|
|
||||||
|
.search-result-list,
|
||||||
|
.ais-Hits-list
|
||||||
|
overflow-y: overlay
|
||||||
|
margin: 0 -20px
|
||||||
|
padding: 0 22px
|
||||||
|
max-height: calc(80vh - 220px)
|
||||||
|
|
||||||
|
.local-search-hit-item,
|
||||||
|
.ais-Hits-item
|
||||||
|
display: flex
|
||||||
|
align-items: flex-start
|
||||||
|
margin: 3px 0
|
||||||
|
line-height: 1.8
|
||||||
|
transition: all .2s ease-in-out
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
transform: translateY(-1px)
|
||||||
|
|
||||||
|
&:not([value])::before
|
||||||
|
display: none
|
||||||
|
|
||||||
|
&[value]::before
|
||||||
|
display: inline-flex
|
||||||
|
flex-shrink: 0
|
||||||
|
justify-content: center
|
||||||
|
align-items: center
|
||||||
|
margin-right: 6px
|
||||||
|
margin-top: 3px
|
||||||
|
min-width: 24px
|
||||||
|
color: $search-color
|
||||||
|
content: attr(value) '.'
|
||||||
|
font-weight: bold
|
||||||
|
font-style: italic
|
||||||
|
font-size: .9em
|
||||||
|
|
||||||
|
&::marker
|
||||||
|
content: none
|
||||||
|
|
||||||
|
a
|
||||||
|
flex: 1
|
||||||
|
color: var(--search-a-color)
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color: $search-color
|
||||||
|
|
||||||
|
.search-result-title,
|
||||||
|
.algolia-hits-item-title
|
||||||
|
font-weight: 600
|
||||||
|
|
||||||
|
.search-result,
|
||||||
|
.algolia-hit-item-content
|
||||||
|
margin: 0 0 8px
|
||||||
|
word-break: break-all
|
||||||
|
font-size: .9em
|
||||||
|
|
||||||
|
.ais-Pagination
|
||||||
|
margin: 15px 0 0
|
||||||
|
padding: 0
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
.ais-Pagination-list
|
||||||
|
display: flex
|
||||||
|
flex-wrap: wrap
|
||||||
|
justify-content: center
|
||||||
|
align-items: center
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
list-style: none
|
||||||
|
gap: 6px
|
||||||
|
|
||||||
|
.ais-Pagination-item
|
||||||
|
display: flex
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
&:not(.ais-Pagination-item--selected):not(.ais-Pagination-item--ellipsis):not(.ais-Pagination-item--disabled)
|
||||||
|
.ais-Pagination-link:hover
|
||||||
|
background: var(--btn-hover-color)
|
||||||
|
transform: translateY(-1px)
|
||||||
|
|
||||||
|
.ais-Pagination-link
|
||||||
|
display: inline-flex
|
||||||
|
justify-content: center
|
||||||
|
align-items: center
|
||||||
|
padding: 4px 8px
|
||||||
|
min-width: 28px
|
||||||
|
height: 28px
|
||||||
|
border-radius: 6px
|
||||||
|
background: var(--btn-bg)
|
||||||
|
color: var(--btn-color)
|
||||||
|
transition: all .2s ease
|
||||||
|
|
||||||
|
&.ais-Pagination-link--disabled
|
||||||
|
opacity: .3
|
||||||
|
cursor: not-allowed
|
||||||
|
|
||||||
|
i
|
||||||
|
font-size: 12px
|
||||||
|
|
||||||
|
.ais-Pagination-item--selected
|
||||||
|
.ais-Pagination-link
|
||||||
|
background: $theme-paginator-color
|
||||||
|
font-weight: 600
|
||||||
|
cursor: default
|
||||||
|
|
||||||
|
.ais-Pagination-item--ellipsis
|
||||||
|
.ais-Pagination-link
|
||||||
|
padding: 4px 2px
|
||||||
|
border: none
|
||||||
|
background: transparent
|
||||||
|
color: var(--text-color)
|
||||||
|
cursor: default
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background: transparent
|
||||||
|
transform: none
|
||||||
|
|
||||||
|
.ais-Pagination-item--disabled
|
||||||
|
.ais-Pagination-link
|
||||||
|
opacity: .4
|
||||||
|
|
||||||
|
+maxWidth768()
|
||||||
|
.ais-Pagination-list
|
||||||
|
gap: 4px
|
||||||
|
|
||||||
hr
|
hr
|
||||||
margin: 15px auto
|
margin: 15px auto
|
||||||
@extend .custom-hr
|
@extend .custom-hr
|
||||||
@@ -50,6 +202,38 @@
|
|||||||
display: none
|
display: none
|
||||||
background: rgba($dark-black, .6)
|
background: rgba($dark-black, .6)
|
||||||
|
|
||||||
|
.search-result-stats,
|
||||||
|
.ais-Stats-text
|
||||||
|
margin: 15px 0 0
|
||||||
|
color: var(--text-color)
|
||||||
|
text-align: center
|
||||||
|
font-size: .9em
|
||||||
|
|
||||||
|
.search-keyword
|
||||||
|
background: transparent
|
||||||
|
color: $search-keyword-highlight
|
||||||
|
font-weight: 600
|
||||||
|
|
||||||
|
.search-loading
|
||||||
|
display: flex
|
||||||
|
justify-content: center
|
||||||
|
align-items: center
|
||||||
|
padding: 20px
|
||||||
|
color: var(--text-color)
|
||||||
|
|
||||||
|
&::before
|
||||||
|
width: 16px
|
||||||
|
height: 16px
|
||||||
|
border: 2px solid var(--text-color)
|
||||||
|
border-top-color: transparent
|
||||||
|
border-radius: 50%
|
||||||
|
content: ''
|
||||||
|
animation: spin 1s linear infinite
|
||||||
|
|
||||||
|
@keyframes spin
|
||||||
|
to
|
||||||
|
transform: rotate(360deg)
|
||||||
|
|
||||||
if hexo-config('search.use') == 'algolia_search'
|
if hexo-config('search.use') == 'algolia_search'
|
||||||
@require 'algolia'
|
@require 'algolia'
|
||||||
else if hexo-config('search.use') == 'local_search'
|
else if hexo-config('search.use') == 'local_search'
|
||||||
|
|||||||
@@ -1,57 +1,18 @@
|
|||||||
#local-search
|
#local-search
|
||||||
.search-dialog
|
.search-dialog
|
||||||
.local-search-box
|
|
||||||
margin: 0 auto
|
|
||||||
max-width: 100%
|
|
||||||
width: 100%
|
|
||||||
|
|
||||||
input
|
|
||||||
padding: 5px 14px
|
|
||||||
width: 100%
|
|
||||||
outline: none
|
|
||||||
border: 2px solid $search-color
|
|
||||||
border-radius: 40px
|
|
||||||
background: var(--search-bg)
|
|
||||||
color: var(--search-input-color)
|
|
||||||
-webkit-appearance: none
|
|
||||||
|
|
||||||
.search-wrap
|
|
||||||
display: none
|
|
||||||
|
|
||||||
.local-search-hit-item
|
|
||||||
margin-left: 24px
|
|
||||||
padding-left: 3px
|
|
||||||
line-height: 1.8
|
|
||||||
|
|
||||||
&::marker
|
|
||||||
color: $search-color
|
|
||||||
font-weight: bold
|
|
||||||
font-style: italic
|
|
||||||
|
|
||||||
a
|
|
||||||
color: var(--search-a-color)
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
color: $search-color
|
|
||||||
|
|
||||||
.search-result-title
|
|
||||||
font-weight: 600
|
|
||||||
|
|
||||||
.search-result
|
|
||||||
margin: 0 0 8px
|
|
||||||
word-break: break-all
|
|
||||||
font-size: .9em
|
|
||||||
|
|
||||||
.search-result-list
|
.search-result-list
|
||||||
overflow-y: overlay
|
|
||||||
margin: 0 -20px
|
|
||||||
padding: 0 22px
|
|
||||||
max-height: calc(80vh - 180px)
|
|
||||||
|
|
||||||
+maxWidth768()
|
+maxWidth768()
|
||||||
max-height: calc(var(--search-height) - 190px) !important
|
if hexo-config('search.local_search.pagination.enable')
|
||||||
|
min-height: calc(var(--search-height) - 255px) !important
|
||||||
|
else
|
||||||
|
max-height: calc(var(--search-height) - 200px) !important
|
||||||
|
|
||||||
.search-keyword
|
#local-search-stats
|
||||||
background: transparent
|
.search-result-stats
|
||||||
color: $search-keyword-highlight
|
text-align: left
|
||||||
font-weight: 600
|
|
||||||
|
.search-keyword
|
||||||
|
font-weight: 600
|
||||||
|
|
||||||
|
#loading-database ~ *
|
||||||
|
visibility: hidden
|
||||||
@@ -7,21 +7,20 @@
|
|||||||
display: inline-block
|
display: inline-block
|
||||||
margin: 0 4px 6px
|
margin: 0 4px 6px
|
||||||
padding: 0 15px
|
padding: 0 15px
|
||||||
background-color: var(--btn-beautify-color, $btn-default-color)
|
background-color: var(--btn-beautify-color, var(--btn-default-color))
|
||||||
color: $btn-color
|
color: var(--btn-color, $btn-color)
|
||||||
|
vertical-align: top
|
||||||
line-height: 2
|
line-height: 2
|
||||||
addBorderRadius()
|
addBorderRadius()
|
||||||
|
@extend .btn-effects
|
||||||
|
|
||||||
for $type in $color-types
|
for $type in $color-types
|
||||||
&.{$type}
|
&.{$type}
|
||||||
--btn-beautify-color: lookup('$tagsP-' + $type + '-color')
|
--btn-beautify-color: unquote('var(--tags-' + $type + '-color)')
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
background-color: var(--btn-hover-color)
|
background-color: var(--btn-hover-color)
|
||||||
|
|
||||||
i + span
|
|
||||||
margin-left: 6px
|
|
||||||
|
|
||||||
&:not(.block) + .btn-beautify:not(.block)
|
&:not(.block) + .btn-beautify:not(.block)
|
||||||
margin: 0 4px 20px
|
margin: 0 4px 20px
|
||||||
|
|
||||||
@@ -39,18 +38,26 @@
|
|||||||
|
|
||||||
&.larger
|
&.larger
|
||||||
padding: 6px 15px
|
padding: 6px 15px
|
||||||
|
@extend .btn-effects-large
|
||||||
&:hover
|
|
||||||
text-decoration: none
|
|
||||||
|
|
||||||
&.outline
|
&.outline
|
||||||
border: 1px solid transparent
|
border: 1px solid transparent
|
||||||
border-color: var(--btn-beautify-color, $btn-default-color)
|
border-color: var(--btn-beautify-color, var(--btn-default-color))
|
||||||
background-color: transparent
|
background-color: transparent
|
||||||
color: var(--btn-beautify-color, $btn-default-color)
|
color: var(--btn-beautify-color, var(--btn-default-color))
|
||||||
|
|
||||||
|
i,
|
||||||
|
span
|
||||||
|
transition: color .3s cubic-bezier(.4, 0, .2, 1)
|
||||||
|
|
||||||
|
&::before
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(0, 0, 0, .1), transparent)
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
background-color: var(--btn-beautify-color, $btn-default-color)
|
border-color: var(--btn-beautify-color, var(--btn-default-color))
|
||||||
|
background-color: var(--btn-beautify-color, var(--btn-default-color))
|
||||||
|
color: var(--btn-color) !important
|
||||||
|
|
||||||
&:hover
|
i,
|
||||||
color: white !important
|
span
|
||||||
|
color: var(--btn-color)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
background: $tag-hide-bg
|
background: $tag-hide-bg
|
||||||
color: var(--white)
|
color: var(--white)
|
||||||
addBorderRadius()
|
addBorderRadius()
|
||||||
|
@extend .btn-effects
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
background-color: var(--btn-hover-color)
|
background-color: var(--btn-hover-color)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
.hl-label
|
.hl-label
|
||||||
padding: 2px 4px
|
padding: 2px 4px
|
||||||
color: $btn-color
|
color: var(--btn-color, $btn-color)
|
||||||
addBorderRadius(3)
|
addBorderRadius(3)
|
||||||
|
|
||||||
&.default
|
&.default
|
||||||
background-color: $btn-default-color
|
background-color: var(--btn-default-color)
|
||||||
|
|
||||||
for $type in $color-types
|
for $type in $color-types
|
||||||
&.{$type}
|
&.{$type}
|
||||||
background-color: lookup('$tagsP-' + $type + '-color')
|
background-color: unquote('var(--tags-' + $type + '-color)')
|
||||||
|
|||||||
@@ -19,32 +19,32 @@
|
|||||||
for $type in $color-types
|
for $type in $color-types
|
||||||
&.{$type}
|
&.{$type}
|
||||||
&:not(.disabled)
|
&:not(.disabled)
|
||||||
border-left-color: lookup('$tagsP-' + $type + '-color') !important
|
border-left-color: unquote('var(--tags-' + $type + '-color)') !important
|
||||||
|
|
||||||
&.modern
|
&.modern
|
||||||
border-left-color: transparent !important
|
border-left-color: transparent !important
|
||||||
color: lookup('$tagsP-' + $type + '-color')
|
color: unquote('var(--tags-' + $type + '-color)')
|
||||||
|
|
||||||
&:not(.simple)
|
&:not(.simple)
|
||||||
background: lighten(lookup('$tagsP-' + $type + '-color'), 85%) !important
|
background: unquote('var(--tags-' + $type + '-color-lighten)')
|
||||||
|
|
||||||
& > .note-icon
|
& > .note-icon
|
||||||
color: lookup('$tagsP-' + $type + '-color')
|
color: unquote('var(--tags-' + $type + '-color)')
|
||||||
|
|
||||||
&.simple
|
&.simple
|
||||||
border: 1px solid #EEEEEE
|
border: 1px solid var(--note-default-border)
|
||||||
border-left-width: 5px
|
border-left-width: 5px
|
||||||
|
|
||||||
&.modern
|
&.modern
|
||||||
border: 1px solid transparent !important
|
border: 1px solid transparent !important
|
||||||
background-color: #f5f5f5
|
background-color: var(--note-modern-default-bg)
|
||||||
color: $font-black
|
color: var(--note-modern-default-text)
|
||||||
|
|
||||||
&.flat
|
&.flat
|
||||||
border: initial
|
border: initial
|
||||||
border-left: 5px solid #EEEEEE
|
border-left: 5px solid var(--note-default-border)
|
||||||
background-color: lighten(#EEEEEE, 65%)
|
background-color: var(--note-default-bg)
|
||||||
color: $font-black
|
color: var(--note-default-text)
|
||||||
|
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
@@ -90,29 +90,30 @@
|
|||||||
for $type in $note-types
|
for $type in $note-types
|
||||||
&.{$type}
|
&.{$type}
|
||||||
&.flat
|
&.flat
|
||||||
background: lookup('$note-' + $type + '-bg')
|
background: unquote('var(--note-' + $type + '-bg)')
|
||||||
|
color: var(--font-color)
|
||||||
|
|
||||||
&.modern
|
&.modern
|
||||||
border-color: lookup('$note-modern-' + $type + '-border')
|
border-color: unquote('var(--note-modern-' + $type + '-border)') !important
|
||||||
background: lookup('$note-modern-' + $type + '-bg')
|
background: unquote('var(--note-modern-' + $type + '-bg)')
|
||||||
color: lookup('$note-modern-' + $type + '-text')
|
color: unquote('var(--note-modern-' + $type + '-text)')
|
||||||
|
|
||||||
a
|
a
|
||||||
&:not(.btn)
|
&:not(.btn)
|
||||||
color: lookup('$note-modern-' + $type + '-text')
|
color: unquote('var(--note-modern-' + $type + '-text)')
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
color: lookup('$note-modern-' + $type + '-hover')
|
color: unquote('var(--note-modern-' + $type + '-hover)')
|
||||||
|
|
||||||
&:not(.modern)
|
&:not(.modern)
|
||||||
border-left-color: lookup('$note-' + $type + '-border')
|
border-left-color: unquote('var(--note-' + $type + '-border)')
|
||||||
|
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6
|
h6
|
||||||
color: lookup('$note-' + $type + '-text')
|
color: unquote('var(--note-' + $type + '-text)')
|
||||||
|
|
||||||
if $note-icons
|
if $note-icons
|
||||||
&:not(.no-icon)
|
&:not(.no-icon)
|
||||||
@@ -121,4 +122,4 @@
|
|||||||
|
|
||||||
&:not(.modern)
|
&:not(.modern)
|
||||||
&::before
|
&::before
|
||||||
color: lookup('$note-' + $type + '-text')
|
color: unquote('var(--note-' + $type + '-text)')
|
||||||
|
|||||||
@@ -14,14 +14,14 @@
|
|||||||
flex-wrap: wrap
|
flex-wrap: wrap
|
||||||
margin: 0
|
margin: 0
|
||||||
padding: 0
|
padding: 0
|
||||||
background: var(--tab-botton-bg)
|
background: var(--tab-button-bg)
|
||||||
|
|
||||||
> .tab
|
> .tab
|
||||||
flex-grow: 1
|
flex-grow: 1
|
||||||
padding: 8px 18px
|
padding: 8px 18px
|
||||||
border-top: 2px solid var(--tab-border-color)
|
border-top: 2px solid var(--tab-border-color)
|
||||||
background: var(--tab-botton-bg)
|
background: var(--tab-button-bg)
|
||||||
color: var(--tab-botton-color)
|
color: var(--tab-button-color)
|
||||||
line-height: 2
|
line-height: 2
|
||||||
transition: all .4s
|
transition: all .4s
|
||||||
|
|
||||||
|
|||||||
@@ -13,19 +13,17 @@ $code-foreground = $themeColorEnable && hexo-config('theme_color.code_foreground
|
|||||||
$code-background = $themeColorEnable && hexo-config('theme_color.code_background') ? convert(hexo-config('theme_color.code_background')) : rgba(27, 31, 35, .05)
|
$code-background = $themeColorEnable && hexo-config('theme_color.code_background') ? convert(hexo-config('theme_color.code_background')) : rgba(27, 31, 35, .05)
|
||||||
$theme-toc-color = $themeColorEnable && hexo-config('theme_color.toc_color') ? convert(hexo-config('theme_color.toc_color')) : $strong-cyan
|
$theme-toc-color = $themeColorEnable && hexo-config('theme_color.toc_color') ? convert(hexo-config('theme_color.toc_color')) : $strong-cyan
|
||||||
// font
|
// font
|
||||||
|
$chineseFont = $language == 'zh-CN' ? 'Microsoft YaHei' : 'Microsoft JhengHei'
|
||||||
$chinseFont = $language == 'zh-CN' ? 'Microsoft YaHei' : 'Microsoft JhengHei'
|
$default-font-family = -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Lato, Roboto, 'PingFang SC', $chineseFont, sans-serif
|
||||||
$dafault-font-family = -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Lato, Roboto, 'PingFang SC', $chinseFont, sans-serif
|
$default-code-font = consolas, Menlo, monospace, 'PingFang SC', $chineseFont, sans-serif
|
||||||
$dafault-code-font = consolas, Menlo, monospace, 'PingFang SC', $chinseFont, sans-serif
|
$font-family = hexo-config('font.font_family') ? unquote(hexo-config('font.font_family')) : $default-font-family
|
||||||
|
$code-font-family = hexo-config('font.code_font_family') ? unquote(hexo-config('font.code_font_family')) : $default-code-font
|
||||||
$font-family = hexo-config('font.font_family') ? unquote(hexo-config('font.font_family')) : $dafault-font-family
|
|
||||||
$code-font-family = hexo-config('font.code_font_family') ? unquote(hexo-config('font.code_font_family')) : $dafault-code-font
|
|
||||||
$site-name-font = hexo-config('blog_title_font.font_family') && unquote(hexo-config('blog_title_font.font_family'))
|
$site-name-font = hexo-config('blog_title_font.font_family') && unquote(hexo-config('blog_title_font.font_family'))
|
||||||
// hr
|
// hr
|
||||||
$hrEnable = hexo-config('hr_icon') && hexo-config('hr_icon.enable')
|
$hrEnable = hexo-config('hr_icon') && hexo-config('hr_icon.enable')
|
||||||
$hr-icon = $hrEnable && hexo-config('hr_icon.icon') ? hexo-config('hr_icon.icon') : '\f0c4'
|
$hr-icon = $hrEnable && hexo-config('hr_icon.icon') ? hexo-config('hr_icon.icon') : '\f0c4'
|
||||||
$hr-icon-top = $hrEnable && hexo-config('hr_icon.icon_top') ? convert(hexo-config('hr_icon.icon_top')) : -10px
|
$hr-icon-top = $hrEnable && hexo-config('hr_icon.icon_top') ? convert(hexo-config('hr_icon.icon_top')) : -10px
|
||||||
// page beatutify
|
// page beautify
|
||||||
$beautifyEnable = hexo-config('beautify.enable')
|
$beautifyEnable = hexo-config('beautify.enable')
|
||||||
$title-prefix-icon = $beautifyEnable && hexo-config('beautify.title_prefix_icon') ? hexo-config('beautify.title_prefix_icon') : '\f0c1'
|
$title-prefix-icon = $beautifyEnable && hexo-config('beautify.title_prefix_icon') ? hexo-config('beautify.title_prefix_icon') : '\f0c1'
|
||||||
$title-prefix-icon-color = $beautifyEnable && hexo-config('beautify.title_prefix_icon_color') ? convert(hexo-config('beautify.title_prefix_icon_color')) : $light-red
|
$title-prefix-icon-color = $beautifyEnable && hexo-config('beautify.title_prefix_icon_color') ? convert(hexo-config('beautify.title_prefix_icon_color')) : $light-red
|
||||||
@@ -175,8 +173,8 @@ $tagsP-purple-color = #6f42c1
|
|||||||
$tagsP-green-color = #5cb85c
|
$tagsP-green-color = #5cb85c
|
||||||
// Tag Plugins - Tab
|
// Tag Plugins - Tab
|
||||||
$tab-border-color = #f0f0f0
|
$tab-border-color = #f0f0f0
|
||||||
$tab-botton-bg = #f0f0f0
|
$tab-button-bg = #f0f0f0
|
||||||
$tab-botton-color = $font-color
|
$tab-button-color = $font-color
|
||||||
$tab-button-hover-bg = darken($tab-border-color, 8)
|
$tab-button-hover-bg = darken($tab-border-color, 8)
|
||||||
$tab-active-border-color = $theme-color
|
$tab-active-border-color = $theme-color
|
||||||
$tab-button-active-bg = $card-bg
|
$tab-button-active-bg = $card-bg
|
||||||
@@ -184,3 +182,52 @@ $tab-to-top-color = #99a9bf
|
|||||||
$tab-to-top-hover-color = $theme-color
|
$tab-to-top-hover-color = $theme-color
|
||||||
// Tag Plugins - timeline
|
// Tag Plugins - timeline
|
||||||
$timeline-default-color = $theme-color
|
$timeline-default-color = $theme-color
|
||||||
|
// note - darkmode
|
||||||
|
// Default
|
||||||
|
$note-dark-default-border = #5a5a5a
|
||||||
|
$note-dark-default-bg = #2b2b2b
|
||||||
|
$note-dark-default-text = #b3b3b3
|
||||||
|
$note-dark-modern-default-border = #9a9a9a
|
||||||
|
$note-dark-modern-default-bg = #353535
|
||||||
|
$note-dark-modern-default-text = #c4c4c4
|
||||||
|
$note-dark-modern-default-hover = #d0d0d0
|
||||||
|
// Primary
|
||||||
|
$note-dark-primary-border = #5935a1
|
||||||
|
$note-dark-primary-bg = #2e1c3e
|
||||||
|
$note-dark-primary-text = #a47dd4
|
||||||
|
$note-dark-modern-primary-border = #9985cc
|
||||||
|
$note-dark-modern-primary-bg = #3c2d4c
|
||||||
|
$note-dark-modern-primary-text = #b693e6
|
||||||
|
$note-dark-modern-primary-hover = #c9a8f0
|
||||||
|
// Info
|
||||||
|
$note-dark-info-border = #346fa2
|
||||||
|
$note-dark-info-bg = #1f2e3b
|
||||||
|
$note-dark-info-text = #7bb3db
|
||||||
|
$note-dark-modern-info-border = #7ca8b5
|
||||||
|
$note-dark-modern-info-bg = #2b3c44
|
||||||
|
$note-dark-modern-info-text = #8fc6e0
|
||||||
|
$note-dark-modern-info-hover = #a3d4ea
|
||||||
|
// Success
|
||||||
|
$note-dark-success-border = #4a944a
|
||||||
|
$note-dark-success-bg = #202e20
|
||||||
|
$note-dark-success-text = #82c682
|
||||||
|
$note-dark-modern-success-border = #8bb087
|
||||||
|
$note-dark-modern-success-bg = #2c3d2c
|
||||||
|
$note-dark-modern-success-text = #96d196
|
||||||
|
$note-dark-modern-success-hover = #a8dca8
|
||||||
|
// Warning
|
||||||
|
$note-dark-warning-border = #c08a3e
|
||||||
|
$note-dark-warning-bg = #3e301f
|
||||||
|
$note-dark-warning-text = #e6ba6b
|
||||||
|
$note-dark-modern-warning-border = #b8a285
|
||||||
|
$note-dark-modern-warning-bg = #4b3c2b
|
||||||
|
$note-dark-modern-warning-text = #d4b373
|
||||||
|
$note-dark-modern-warning-hover = #e0c080
|
||||||
|
// Danger
|
||||||
|
$note-dark-danger-border = #b34440
|
||||||
|
$note-dark-danger-bg = #3b201f
|
||||||
|
$note-dark-danger-text = #e67572
|
||||||
|
$note-dark-modern-danger-border = #c7898c
|
||||||
|
$note-dark-modern-danger-bg = #4d2b2e
|
||||||
|
$note-dark-modern-danger-text = #d98b8e
|
||||||
|
$note-dark-modern-danger-hover = #e59fa2
|
||||||
@@ -68,7 +68,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const isPrismjs = plugin === 'prismjs'
|
const isPrismjs = plugin === 'prismjs'
|
||||||
const highlightShrinkClass = isHighlightShrink === true ? 'closed' : ''
|
const highlightShrinkClass = isHighlightShrink === true ? 'closed' : ''
|
||||||
const highlightShrinkEle = isHighlightShrink !== undefined ? '<i class="fas fa-angle-down expand"></i>' : ''
|
const highlightShrinkEle = isHighlightShrink !== undefined ? '<i class="fas fa-angle-down expand"></i>' : ''
|
||||||
const highlightCopyEle = highlightCopy ? '<div class="copy-notice"></div><i class="fas fa-paste copy-button"></i>' : ''
|
const highlightCopyEle = highlightCopy ? '<i class="fas fa-paste copy-button"></i>' : ''
|
||||||
const highlightMacStyleEle = '<div class="macStyle"><div class="mac-close"></div><div class="mac-minimize"></div><div class="mac-maximize"></div></div>'
|
const highlightMacStyleEle = '<div class="macStyle"><div class="mac-close"></div><div class="mac-minimize"></div><div class="mac-maximize"></div></div>'
|
||||||
const highlightFullpageEle = highlightFullpage ? '<i class="fa-solid fa-up-right-and-down-left-from-center fullpage-button"></i>' : ''
|
const highlightFullpageEle = highlightFullpage ? '<i class="fa-solid fa-up-right-and-down-left-from-center fullpage-button"></i>' : ''
|
||||||
|
|
||||||
@@ -76,12 +76,41 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
if (GLOBAL_CONFIG.Snackbar !== undefined) {
|
if (GLOBAL_CONFIG.Snackbar !== undefined) {
|
||||||
btf.snackbarShow(text)
|
btf.snackbarShow(text)
|
||||||
} else {
|
} else {
|
||||||
ele.textContent = text
|
const newEle = document.createElement('div')
|
||||||
ele.style.opacity = 1
|
newEle.className = 'copy-notice'
|
||||||
setTimeout(() => { ele.style.opacity = 0 }, 800)
|
newEle.textContent = text
|
||||||
|
document.body.appendChild(newEle)
|
||||||
|
|
||||||
|
const buttonRect = ele.getBoundingClientRect()
|
||||||
|
const scrollTop = window.pageYOffset || document.documentElement.scrollTop
|
||||||
|
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft
|
||||||
|
const finalTop = buttonRect.top + scrollTop - 40
|
||||||
|
const finalLeft = buttonRect.left + scrollLeft + buttonRect.width / 2
|
||||||
|
|
||||||
|
const topValue = ele.closest('figure.highlight').classList.contains('code-fullpage') ? finalTop + 60 : finalTop
|
||||||
|
|
||||||
|
newEle.style.cssText = `
|
||||||
|
top: ${topValue + 10}px;
|
||||||
|
left: ${finalLeft}px;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease, top 0.3s ease;
|
||||||
|
`
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
newEle.style.opacity = '1'
|
||||||
|
newEle.style.top = `${topValue}px`
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
newEle.style.opacity = '0'
|
||||||
|
newEle.style.top = `${topValue + 10}px`
|
||||||
|
setTimeout(() => {
|
||||||
|
newEle?.remove()
|
||||||
|
}, 300)
|
||||||
|
}, 800)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const copy = async (text, ctx) => {
|
const copy = async (text, ctx) => {
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(text)
|
await navigator.clipboard.writeText(text)
|
||||||
@@ -99,7 +128,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const preCodeSelector = isPrismjs ? 'pre code' : 'table .code pre'
|
const preCodeSelector = isPrismjs ? 'pre code' : 'table .code pre'
|
||||||
const codeElement = $buttonParent.querySelector(preCodeSelector)
|
const codeElement = $buttonParent.querySelector(preCodeSelector)
|
||||||
if (!codeElement) return
|
if (!codeElement) return
|
||||||
copy(codeElement.innerText, clickEle.previousElementSibling)
|
copy(codeElement.innerText, clickEle)
|
||||||
$buttonParent.classList.remove('copy-true')
|
$buttonParent.classList.remove('copy-true')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -698,7 +727,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const addCopyright = () => {
|
const addCopyright = () => {
|
||||||
const { limitCount, languages } = GLOBAL_CONFIG.copyright
|
const { limitCount, languages } = GLOBAL_CONFIG.copyright
|
||||||
|
|
||||||
const handleCopy = (e) => {
|
const handleCopy = e => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
const copyFont = window.getSelection(0).toString()
|
const copyFont = window.getSelection(0).toString()
|
||||||
let textFont = copyFont
|
let textFont = copyFont
|
||||||
|
|||||||
@@ -26,7 +26,12 @@ window.addEventListener('load', () => {
|
|||||||
const openSearch = () => {
|
const openSearch = () => {
|
||||||
btf.overflowPaddingR.add()
|
btf.overflowPaddingR.add()
|
||||||
animateElements(true)
|
animateElements(true)
|
||||||
setTimeout(() => { document.querySelector('#algolia-search .ais-SearchBox-input').focus() }, 100)
|
showLoading(false)
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
const searchInput = document.querySelector('#algolia-search-input .ais-SearchBox-input')
|
||||||
|
if (searchInput) searchInput.focus()
|
||||||
|
}, 100)
|
||||||
|
|
||||||
const handleEscape = event => {
|
const handleEscape = event => {
|
||||||
if (event.code === 'Escape') {
|
if (event.code === 'Escape') {
|
||||||
@@ -55,9 +60,38 @@ window.addEventListener('load', () => {
|
|||||||
document.querySelector('#algolia-search .search-close-button').addEventListener('click', closeSearch)
|
document.querySelector('#algolia-search .search-close-button').addEventListener('click', closeSearch)
|
||||||
}
|
}
|
||||||
|
|
||||||
const cutContent = (content) => {
|
const cutContent = content => {
|
||||||
if (!content) return ''
|
if (!content) return ''
|
||||||
const firstOccur = content.indexOf('<mark>')
|
|
||||||
|
let contentStr = ''
|
||||||
|
if (typeof content === 'string') {
|
||||||
|
contentStr = content.trim()
|
||||||
|
} else if (typeof content === 'object') {
|
||||||
|
if (content.value !== undefined) {
|
||||||
|
contentStr = String(content.value).trim()
|
||||||
|
if (!contentStr) return ''
|
||||||
|
} else if (content.matchedWords || content.matchLevel || content.fullyHighlighted !== undefined) {
|
||||||
|
return ''
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
contentStr = JSON.stringify(content).trim()
|
||||||
|
if (contentStr === '{}' || contentStr === '[]' || contentStr === '""') {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (content.toString && typeof content.toString === 'function') {
|
||||||
|
contentStr = content.toString().trim()
|
||||||
|
if (contentStr === '[object Object]' || contentStr === '[object Array]') {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstOccur = contentStr.indexOf('<mark>')
|
||||||
let start = firstOccur - 30
|
let start = firstOccur - 30
|
||||||
let end = firstOccur + 120
|
let end = firstOccur + 120
|
||||||
let pre = ''
|
let pre = ''
|
||||||
@@ -70,94 +104,454 @@ window.addEventListener('load', () => {
|
|||||||
pre = '...'
|
pre = '...'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (end > content.length) {
|
if (end > contentStr.length) {
|
||||||
end = content.length
|
end = contentStr.length
|
||||||
} else {
|
} else {
|
||||||
post = '...'
|
post = '...'
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${pre}${content.substring(start, end)}${post}`
|
// Ensure we don't cut off HTML tags in the middle
|
||||||
|
let substr = contentStr.substring(start, end)
|
||||||
|
|
||||||
|
// Handle tag completeness
|
||||||
|
// Check for incomplete opening tags at the beginning
|
||||||
|
const firstCloseBracket = substr.indexOf('>')
|
||||||
|
const firstOpenBracket = substr.indexOf('<')
|
||||||
|
|
||||||
|
// If there's a closing bracket but no opening bracket before it, we've cut a tag
|
||||||
|
if (firstCloseBracket !== -1 && (firstOpenBracket === -1 || firstCloseBracket < firstOpenBracket)) {
|
||||||
|
substr = substr.substring(firstCloseBracket + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for incomplete closing tags at the end
|
||||||
|
const lastOpenBracket = substr.lastIndexOf('<')
|
||||||
|
const lastCloseBracket = substr.lastIndexOf('>')
|
||||||
|
|
||||||
|
// If there's an opening bracket after the last closing bracket, we've cut a tag
|
||||||
|
if (lastOpenBracket !== -1 && lastOpenBracket > lastCloseBracket) {
|
||||||
|
substr = substr.substring(0, lastOpenBracket)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Balance tags in the substring
|
||||||
|
const tagStack = []
|
||||||
|
let balancedStr = ''
|
||||||
|
let i = 0
|
||||||
|
|
||||||
|
while (i < substr.length) {
|
||||||
|
if (substr[i] === '<') {
|
||||||
|
// Check if it's a closing tag
|
||||||
|
if (substr[i + 1] === '/') {
|
||||||
|
const closeTagEnd = substr.indexOf('>', i)
|
||||||
|
if (closeTagEnd !== -1) {
|
||||||
|
const closeTagName = substr.substring(i + 2, closeTagEnd)
|
||||||
|
// Remove matching opening tag from stack
|
||||||
|
for (let j = tagStack.length - 1; j >= 0; j--) {
|
||||||
|
if (tagStack[j] === closeTagName) {
|
||||||
|
tagStack.splice(j, 1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
balancedStr += substr.substring(i, closeTagEnd + 1)
|
||||||
|
i = closeTagEnd + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else if (substr.substr(i, 2) === '<!' || (substr.indexOf('/>', i) !== -1 && substr.indexOf('/>', i) < substr.indexOf('>', i))) {
|
||||||
|
const tagEnd = substr.indexOf('>', i)
|
||||||
|
if (tagEnd !== -1) {
|
||||||
|
balancedStr += substr.substring(i, tagEnd + 1)
|
||||||
|
i = tagEnd + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const tagEnd = substr.indexOf('>', i)
|
||||||
|
if (tagEnd !== -1) {
|
||||||
|
const tagName = substr.substring(i + 1, (substr.indexOf(' ', i) > -1 && substr.indexOf(' ', i) < tagEnd)
|
||||||
|
? substr.indexOf(' ', i)
|
||||||
|
: tagEnd).split(/\s/)[0]
|
||||||
|
tagStack.push(tagName)
|
||||||
|
balancedStr += substr.substring(i, tagEnd + 1)
|
||||||
|
i = tagEnd + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
balancedStr += substr[i]
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close any unclosed tags
|
||||||
|
while (tagStack.length > 0) {
|
||||||
|
const tagName = tagStack.pop()
|
||||||
|
balancedStr += `</${tagName}>`
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we removed content from the beginning, add prefix
|
||||||
|
if (start > 0 || pre) {
|
||||||
|
const actualFirstOpenBracket = contentStr.indexOf('<', start > 0 ? start - 30 : 0)
|
||||||
|
const actualFirstMark = contentStr.indexOf('<mark>', start > 0 ? start - 30 : 0)
|
||||||
|
|
||||||
|
if (actualFirstOpenBracket !== -1 &&
|
||||||
|
(actualFirstMark === -1 || actualFirstOpenBracket < actualFirstMark)) {
|
||||||
|
pre = '...'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
substr = balancedStr
|
||||||
|
return `${pre}${substr}${post}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const disableDiv = [
|
// Helper function to handle Algolia highlight results
|
||||||
document.getElementById('algolia-hits'),
|
const extractHighlightValue = highlightObj => {
|
||||||
document.getElementById('algolia-pagination'),
|
if (!highlightObj) return ''
|
||||||
document.querySelector('#algolia-info .algolia-stats')
|
|
||||||
]
|
|
||||||
|
|
||||||
const searchClient = typeof algoliasearch === 'function' ? algoliasearch : window['algoliasearch/lite'].liteClient
|
if (typeof highlightObj === 'string') {
|
||||||
const search = instantsearch({
|
return highlightObj.trim()
|
||||||
indexName,
|
|
||||||
searchClient: searchClient(appId, apiKey),
|
|
||||||
searchFunction (helper) {
|
|
||||||
disableDiv.forEach(item => {
|
|
||||||
item.style.display = helper.state.query ? '' : 'none'
|
|
||||||
})
|
|
||||||
if (helper.state.query) helper.search()
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
const widgets = [
|
if (typeof highlightObj === 'object' && highlightObj.value !== undefined) {
|
||||||
instantsearch.widgets.configure({ hitsPerPage }),
|
return String(highlightObj.value).trim()
|
||||||
instantsearch.widgets.searchBox({
|
}
|
||||||
container: '#algolia-search-input',
|
|
||||||
showReset: false,
|
return ''
|
||||||
showSubmit: false,
|
}
|
||||||
placeholder: languages.input_placeholder,
|
|
||||||
showLoadingIndicator: true
|
// Initialize Algolia client
|
||||||
}),
|
let searchClient
|
||||||
instantsearch.widgets.hits({
|
|
||||||
container: '#algolia-hits',
|
if (window['algoliasearch/lite'] && typeof window['algoliasearch/lite'].liteClient === 'function') {
|
||||||
templates: {
|
searchClient = window['algoliasearch/lite'].liteClient(appId, apiKey)
|
||||||
item (data) {
|
} else if (typeof window.algoliasearch === 'function') {
|
||||||
const link = data.permalink || (GLOBAL_CONFIG.root + data.path)
|
searchClient = window.algoliasearch(appId, apiKey)
|
||||||
const result = data._highlightResult
|
} else {
|
||||||
const content = result.contentStripTruncate
|
return console.error('Algolia search client not found!')
|
||||||
? cutContent(result.contentStripTruncate.value)
|
}
|
||||||
: result.contentStrip
|
|
||||||
? cutContent(result.contentStrip.value)
|
if (!searchClient) {
|
||||||
: result.content
|
return console.error('Failed to initialize Algolia search client')
|
||||||
? cutContent(result.content.value)
|
}
|
||||||
: ''
|
|
||||||
return `
|
// Search state
|
||||||
<a href="${link}" class="algolia-hit-item-link">
|
let currentQuery = ''
|
||||||
<span class="algolia-hits-item-title">${result.title.value || 'no-title'}</span>
|
|
||||||
${content ? `<div class="algolia-hit-item-content">${content}</div>` : ''}
|
// Show loading state
|
||||||
</a>`
|
const showLoading = show => {
|
||||||
},
|
const loadingIndicator = document.getElementById('loading-status')
|
||||||
empty (data) {
|
if (loadingIndicator) {
|
||||||
return `<div id="algolia-hits-empty">${languages.hits_empty.replace(/\$\{query}/, data.query)}</div>`
|
loadingIndicator.hidden = !show
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache frequently used elements
|
||||||
|
const elements = {
|
||||||
|
get searchInput () { return document.querySelector('#algolia-search-input .ais-SearchBox-input') },
|
||||||
|
get hits () { return document.getElementById('algolia-hits') },
|
||||||
|
get hitsEmpty () { return document.getElementById('algolia-hits-empty') },
|
||||||
|
get hitsList () { return document.querySelector('#algolia-hits .ais-Hits-list') },
|
||||||
|
get hitsWrapper () { return document.querySelector('#algolia-hits .ais-Hits') },
|
||||||
|
get pagination () { return document.getElementById('algolia-pagination') },
|
||||||
|
get paginationList () { return document.querySelector('#algolia-pagination .ais-Pagination-list') },
|
||||||
|
get stats () { return document.querySelector('#algolia-info .ais-Stats-text') },
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show/hide search results area
|
||||||
|
const toggleResultsVisibility = hasResults => {
|
||||||
|
elements.pagination.style.display = hasResults ? '' : 'none'
|
||||||
|
elements.stats.style.display = hasResults ? '' : 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render search results
|
||||||
|
const renderHits = (hits, query, page = 0) => {
|
||||||
|
if (hits.length === 0 && query) {
|
||||||
|
elements.hitsEmpty.textContent = languages.hits_empty.replace(/\$\{query}/, query)
|
||||||
|
elements.hitsEmpty.style.display = ''
|
||||||
|
elements.hitsWrapper.style.display = 'none'
|
||||||
|
elements.stats.style.display = 'none'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
elements.hitsEmpty.style.display = 'none'
|
||||||
|
|
||||||
|
const hitsHTML = hits.map((hit, index) => {
|
||||||
|
const itemNumber = page * hitsPerPage + index + 1
|
||||||
|
const link = hit.permalink || (GLOBAL_CONFIG.root + hit.path)
|
||||||
|
const result = hit._highlightResult || hit
|
||||||
|
|
||||||
|
// Content extraction
|
||||||
|
let content = ''
|
||||||
|
try {
|
||||||
|
if (result.contentStripTruncate) {
|
||||||
|
content = cutContent(result.contentStripTruncate)
|
||||||
|
} else if (result.contentStrip) {
|
||||||
|
content = cutContent(result.contentStrip)
|
||||||
|
} else if (result.content) {
|
||||||
|
content = cutContent(result.content)
|
||||||
|
} else if (hit.contentStripTruncate) {
|
||||||
|
content = cutContent(hit.contentStripTruncate)
|
||||||
|
} else if (hit.contentStrip) {
|
||||||
|
content = cutContent(hit.contentStrip)
|
||||||
|
} else if (hit.content) {
|
||||||
|
content = cutContent(hit.content)
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
content = ''
|
||||||
}
|
}
|
||||||
}),
|
|
||||||
instantsearch.widgets.stats({
|
// Title handling
|
||||||
container: '#algolia-info > .algolia-stats',
|
let title = 'no-title'
|
||||||
templates: {
|
try {
|
||||||
text (data) {
|
if (result.title) {
|
||||||
const stats = languages.hits_stats
|
title = extractHighlightValue(result.title) || 'no-title'
|
||||||
.replace(/\$\{hits}/, data.nbHits)
|
} else if (hit.title) {
|
||||||
.replace(/\$\{time}/, data.processingTimeMS)
|
title = extractHighlightValue(hit.title) || 'no-title'
|
||||||
return `<hr>${stats}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!title || title === 'no-title') {
|
||||||
|
if (typeof hit.title === 'string' && hit.title.trim()) {
|
||||||
|
title = hit.title.trim()
|
||||||
|
} else if (hit.title && typeof hit.title === 'object' && hit.title.value) {
|
||||||
|
title = String(hit.title.value).trim() || 'no-title'
|
||||||
|
} else {
|
||||||
|
title = 'no-title'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
title = 'no-title'
|
||||||
}
|
}
|
||||||
}),
|
|
||||||
instantsearch.widgets.poweredBy({
|
return `
|
||||||
container: '#algolia-info > .algolia-poweredBy'
|
<li class="ais-Hits-item" value="${itemNumber}">
|
||||||
}),
|
<a href="${link}" class="algolia-hit-item-link">
|
||||||
instantsearch.widgets.pagination({
|
<span class="algolia-hits-item-title">${title}</span>
|
||||||
container: '#algolia-pagination',
|
${content ? `<div class="algolia-hit-item-content">${content}</div>` : ''}
|
||||||
totalPages: 5,
|
</a>
|
||||||
templates: {
|
</li>`
|
||||||
first: '<i class="fas fa-angle-double-left"></i>',
|
}).join('')
|
||||||
last: '<i class="fas fa-angle-double-right"></i>',
|
|
||||||
previous: '<i class="fas fa-angle-left"></i>',
|
elements.hitsList.innerHTML = hitsHTML
|
||||||
next: '<i class="fas fa-angle-right"></i>'
|
elements.hitsWrapper.style.display = query ? '' : 'none'
|
||||||
|
|
||||||
|
if (hits.length > 0) {
|
||||||
|
elements.stats.style.display = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render pagination
|
||||||
|
const renderPagination = (page, nbPages) => {
|
||||||
|
if (nbPages <= 1) {
|
||||||
|
elements.pagination.style.display = 'none'
|
||||||
|
elements.paginationList.innerHTML = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
elements.pagination.style.display = 'block'
|
||||||
|
|
||||||
|
const isFirstPage = page === 0
|
||||||
|
const isLastPage = page === nbPages - 1
|
||||||
|
|
||||||
|
// Responsive page display
|
||||||
|
const isMobile = window.innerWidth < 768
|
||||||
|
const maxVisiblePages = isMobile ? 3 : 5
|
||||||
|
let startPage = Math.max(0, page - Math.floor(maxVisiblePages / 2))
|
||||||
|
const endPage = Math.min(nbPages - 1, startPage + maxVisiblePages - 1)
|
||||||
|
|
||||||
|
// Adjust starting page to maintain max visible pages
|
||||||
|
if (endPage - startPage + 1 < maxVisiblePages) {
|
||||||
|
startPage = Math.max(0, endPage - maxVisiblePages + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
let pagesHTML = ''
|
||||||
|
|
||||||
|
// Only add ellipsis and first page when there are many pages
|
||||||
|
if (nbPages > maxVisiblePages && startPage > 0) {
|
||||||
|
pagesHTML += `
|
||||||
|
<li class="ais-Pagination-item ais-Pagination-item--page">
|
||||||
|
<a class="ais-Pagination-link" aria-label="Page 1" href="#" data-page="0">1</a>
|
||||||
|
</li>`
|
||||||
|
if (startPage > 1) {
|
||||||
|
pagesHTML += `
|
||||||
|
<li class="ais-Pagination-item ais-Pagination-item--ellipsis">
|
||||||
|
<span class="ais-Pagination-link">...</span>
|
||||||
|
</li>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add middle page numbers
|
||||||
|
for (let i = startPage; i <= endPage; i++) {
|
||||||
|
const isSelected = i === page
|
||||||
|
if (isSelected) {
|
||||||
|
pagesHTML += `
|
||||||
|
<li class="ais-Pagination-item ais-Pagination-item--page ais-Pagination-item--selected">
|
||||||
|
<span class="ais-Pagination-link" aria-label="Page ${i + 1}">${i + 1}</span>
|
||||||
|
</li>`
|
||||||
|
} else {
|
||||||
|
pagesHTML += `
|
||||||
|
<li class="ais-Pagination-item ais-Pagination-item--page">
|
||||||
|
<a class="ais-Pagination-link" aria-label="Page ${i + 1}" href="#" data-page="${i}">${i + 1}</a>
|
||||||
|
</li>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only add ellipsis and last page when there are many pages
|
||||||
|
if (nbPages > maxVisiblePages && endPage < nbPages - 1) {
|
||||||
|
if (endPage < nbPages - 2) {
|
||||||
|
pagesHTML += `
|
||||||
|
<li class="ais-Pagination-item ais-Pagination-item--ellipsis">
|
||||||
|
<span class="ais-Pagination-link">...</span>
|
||||||
|
</li>`
|
||||||
|
}
|
||||||
|
pagesHTML += `
|
||||||
|
<li class="ais-Pagination-item ais-Pagination-item--page">
|
||||||
|
<a class="ais-Pagination-link" aria-label="Page ${nbPages}" href="#" data-page="${nbPages - 1}">${nbPages}</a>
|
||||||
|
</li>`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nbPages > 1) {
|
||||||
|
elements.paginationList.innerHTML = `
|
||||||
|
<li class="ais-Pagination-item ais-Pagination-item--previousPage ${isFirstPage ? 'ais-Pagination-item--disabled' : ''}">
|
||||||
|
${isFirstPage
|
||||||
|
? '<span class="ais-Pagination-link ais-Pagination-link--disabled" aria-label="Previous Page"><i class="fas fa-angle-left"></i></span>'
|
||||||
|
: `<a class="ais-Pagination-link" aria-label="Previous Page" href="#" data-page="${page - 1}"><i class="fas fa-angle-left"></i></a>`
|
||||||
|
}
|
||||||
|
</li>
|
||||||
|
${pagesHTML}
|
||||||
|
<li class="ais-Pagination-item ais-Pagination-item--nextPage ${isLastPage ? 'ais-Pagination-item--disabled' : ''}">
|
||||||
|
${isLastPage
|
||||||
|
? '<span class="ais-Pagination-link ais-Pagination-link--disabled" aria-label="Next Page"><i class="fas fa-angle-right"></i></span>'
|
||||||
|
: `<a class="ais-Pagination-link" aria-label="Next Page" href="#" data-page="${page + 1}"><i class="fas fa-angle-right"></i></a>`
|
||||||
|
}
|
||||||
|
</li>`
|
||||||
|
elements.pagination.style.display = currentQuery ? '' : 'none'
|
||||||
|
} else {
|
||||||
|
elements.pagination.style.display = 'none'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render statistics
|
||||||
|
const renderStats = (nbHits, processingTimeMS, query) => {
|
||||||
|
if (query) {
|
||||||
|
const stats = languages.hits_stats
|
||||||
|
.replace(/\$\{hits}/, nbHits)
|
||||||
|
.replace(/\$\{time}/, processingTimeMS)
|
||||||
|
elements.stats.innerHTML = `<hr>${stats}`
|
||||||
|
elements.stats.style.display = ''
|
||||||
|
} else {
|
||||||
|
elements.stats.style.display = 'none'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform search
|
||||||
|
const performSearch = async (query, page = 0) => {
|
||||||
|
if (!query.trim()) {
|
||||||
|
currentQuery = ''
|
||||||
|
renderHits([], '', 0)
|
||||||
|
renderPagination(0, 0)
|
||||||
|
renderStats(0, 0, '')
|
||||||
|
toggleResultsVisibility(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
showLoading(true)
|
||||||
|
currentQuery = query
|
||||||
|
|
||||||
|
try {
|
||||||
|
let result
|
||||||
|
|
||||||
|
if (searchClient && typeof searchClient.search === 'function') {
|
||||||
|
// v5 multi-index search
|
||||||
|
const searchResult = await searchClient.search([{
|
||||||
|
indexName,
|
||||||
|
query,
|
||||||
|
params: {
|
||||||
|
page,
|
||||||
|
hitsPerPage,
|
||||||
|
highlightPreTag: '<mark>',
|
||||||
|
highlightPostTag: '</mark>',
|
||||||
|
attributesToHighlight: ['title', 'content', 'contentStrip', 'contentStripTruncate']
|
||||||
|
}
|
||||||
|
}])
|
||||||
|
result = searchResult.results[0]
|
||||||
|
} else if (searchClient && typeof searchClient.initIndex === 'function') {
|
||||||
|
// v4 single-index search
|
||||||
|
const index = searchClient.initIndex(indexName)
|
||||||
|
result = await index.search(query, {
|
||||||
|
page,
|
||||||
|
hitsPerPage,
|
||||||
|
highlightPreTag: '<mark>',
|
||||||
|
highlightPostTag: '</mark>',
|
||||||
|
attributesToHighlight: ['title', 'content', 'contentStrip', 'contentStripTruncate']
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
throw new Error('Algolia: No compatible search method available')
|
||||||
|
}
|
||||||
|
|
||||||
|
renderHits(result.hits || [], query, page)
|
||||||
|
|
||||||
|
const actualNbPages = result.nbHits <= hitsPerPage ? 1 : (result.nbPages || 0)
|
||||||
|
renderPagination(page, actualNbPages)
|
||||||
|
renderStats(result.nbHits || 0, result.processingTimeMS || 0, query)
|
||||||
|
|
||||||
|
const hasResults = result.hits && result.hits.length > 0
|
||||||
|
toggleResultsVisibility(hasResults)
|
||||||
|
|
||||||
|
// Refresh Pjax links
|
||||||
|
if (window.pjax) {
|
||||||
|
window.pjax.refresh(document.getElementById('algolia-hits'))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Algolia search error:', error)
|
||||||
|
renderHits([], query, page)
|
||||||
|
renderPagination(0, 0)
|
||||||
|
renderStats(0, 0, query)
|
||||||
|
} finally {
|
||||||
|
showLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debounced search
|
||||||
|
let searchTimeout
|
||||||
|
const debouncedSearch = (query, delay = 300) => {
|
||||||
|
clearTimeout(searchTimeout)
|
||||||
|
searchTimeout = setTimeout(() => performSearch(query), delay)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize search box and events
|
||||||
|
const initializeSearch = () => {
|
||||||
|
showLoading(false)
|
||||||
|
|
||||||
|
if (elements.searchInput) {
|
||||||
|
elements.searchInput.addEventListener('input', e => {
|
||||||
|
const query = e.target.value
|
||||||
|
debouncedSearch(query)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchForm = document.querySelector('#algolia-search-input .ais-SearchBox-form')
|
||||||
|
if (searchForm) {
|
||||||
|
searchForm.addEventListener('submit', e => {
|
||||||
|
e.preventDefault()
|
||||||
|
const query = elements.searchInput.value
|
||||||
|
performSearch(query)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pagination event delegation
|
||||||
|
elements.pagination.addEventListener('click', e => {
|
||||||
|
e.preventDefault()
|
||||||
|
const link = e.target.closest('a[data-page]')
|
||||||
|
if (link) {
|
||||||
|
const page = parseInt(link.dataset.page, 10)
|
||||||
|
if (!isNaN(page) && currentQuery) {
|
||||||
|
performSearch(currentQuery, page)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
]
|
|
||||||
|
|
||||||
search.addWidgets(widgets)
|
// Initial state
|
||||||
search.start()
|
toggleResultsVisibility(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
initializeSearch()
|
||||||
searchClickFn()
|
searchClickFn()
|
||||||
searchFnOnce()
|
searchFnOnce()
|
||||||
|
|
||||||
@@ -165,10 +559,4 @@ window.addEventListener('load', () => {
|
|||||||
if (!btf.isHidden($searchMask)) closeSearch()
|
if (!btf.isHidden($searchMask)) closeSearch()
|
||||||
searchClickFn()
|
searchClickFn()
|
||||||
})
|
})
|
||||||
|
|
||||||
if (window.pjax) {
|
|
||||||
search.on('render', () => {
|
|
||||||
window.pjax.refresh(document.getElementById('algolia-hits'))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -156,10 +156,10 @@ class LocalSearch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
slicesOfContent.forEach(slice => {
|
slicesOfContent.forEach(slice => {
|
||||||
resultItem += `<p class="search-result">${this.highlightKeyword(content, slice)}...</p></a>`
|
resultItem += `<p class="search-result">${this.highlightKeyword(content, slice)}...</p>`
|
||||||
})
|
})
|
||||||
|
|
||||||
resultItem += '</li>'
|
resultItem += '</a></li>'
|
||||||
resultItems.push({
|
resultItems.push({
|
||||||
item: resultItem,
|
item: resultItem,
|
||||||
id: resultItems.length,
|
id: resultItems.length,
|
||||||
@@ -236,40 +236,229 @@ class LocalSearch {
|
|||||||
|
|
||||||
window.addEventListener('load', () => {
|
window.addEventListener('load', () => {
|
||||||
// Search
|
// Search
|
||||||
const { path, top_n_per_article, unescape, languages } = GLOBAL_CONFIG.localSearch
|
const { path, top_n_per_article, unescape, languages, pagination } = GLOBAL_CONFIG.localSearch
|
||||||
|
const enablePagination = pagination && pagination.enable
|
||||||
const localSearch = new LocalSearch({
|
const localSearch = new LocalSearch({
|
||||||
path,
|
path,
|
||||||
top_n_per_article,
|
top_n_per_article,
|
||||||
unescape
|
unescape
|
||||||
})
|
})
|
||||||
|
|
||||||
const input = document.querySelector('#local-search-input input')
|
const input = document.querySelector('.local-search-input input')
|
||||||
const statsItem = document.getElementById('local-search-stats-wrap')
|
const statsItem = document.getElementById('local-search-stats')
|
||||||
const $loadingStatus = document.getElementById('loading-status')
|
const $loadingStatus = document.getElementById('loading-status')
|
||||||
const isXml = !path.endsWith('json')
|
const isXml = !path.endsWith('json')
|
||||||
|
|
||||||
|
// Pagination variables (only initialize if pagination is enabled)
|
||||||
|
let currentPage = 0
|
||||||
|
const hitsPerPage = pagination.hitsPerPage || 10
|
||||||
|
|
||||||
|
let currentResultItems = []
|
||||||
|
|
||||||
|
if (!enablePagination) {
|
||||||
|
// If pagination is disabled, we don't need these variables
|
||||||
|
currentPage = undefined
|
||||||
|
currentResultItems = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache frequently used elements
|
||||||
|
const elements = {
|
||||||
|
get pagination () { return document.getElementById('local-search-pagination') },
|
||||||
|
get paginationList () { return document.querySelector('#local-search-pagination .ais-Pagination-list') }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show/hide search results area
|
||||||
|
const toggleResultsVisibility = hasResults => {
|
||||||
|
if (enablePagination) {
|
||||||
|
elements.pagination.style.display = hasResults ? '' : 'none'
|
||||||
|
} else {
|
||||||
|
elements.pagination.style.display = 'none'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render search results for current page
|
||||||
|
const renderResults = (searchText, resultItems) => {
|
||||||
|
const container = document.getElementById('local-search-results')
|
||||||
|
|
||||||
|
// Determine items to display based on pagination mode
|
||||||
|
const itemsToDisplay = enablePagination
|
||||||
|
? currentResultItems.slice(currentPage * hitsPerPage, (currentPage + 1) * hitsPerPage)
|
||||||
|
: resultItems
|
||||||
|
|
||||||
|
// Handle empty page in pagination mode
|
||||||
|
if (enablePagination && itemsToDisplay.length === 0 && currentResultItems.length > 0) {
|
||||||
|
currentPage = 0
|
||||||
|
renderResults(searchText, resultItems)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add numbering to items
|
||||||
|
const numberedItems = itemsToDisplay.map((result, index) => {
|
||||||
|
const itemNumber = enablePagination
|
||||||
|
? currentPage * hitsPerPage + index + 1
|
||||||
|
: index + 1
|
||||||
|
return result.item.replace(
|
||||||
|
'<li class="local-search-hit-item">',
|
||||||
|
`<li class="local-search-hit-item" value="${itemNumber}">`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
container.innerHTML = `<ol class="search-result-list">${numberedItems.join('')}</ol>`
|
||||||
|
|
||||||
|
// Update stats
|
||||||
|
const displayCount = enablePagination ? currentResultItems.length : resultItems.length
|
||||||
|
const stats = languages.hits_stats.replace(/\$\{hits}/, displayCount)
|
||||||
|
statsItem.innerHTML = `<hr><div class="search-result-stats">${stats}</div>`
|
||||||
|
|
||||||
|
// Handle pagination
|
||||||
|
if (enablePagination) {
|
||||||
|
const nbPages = Math.ceil(currentResultItems.length / hitsPerPage)
|
||||||
|
renderPagination(currentPage, nbPages, searchText)
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasResults = resultItems.length > 0
|
||||||
|
toggleResultsVisibility(hasResults)
|
||||||
|
|
||||||
|
window.pjax && window.pjax.refresh(container)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render pagination
|
||||||
|
const renderPagination = (page, nbPages, query) => {
|
||||||
|
if (nbPages <= 1) {
|
||||||
|
elements.pagination.style.display = 'none'
|
||||||
|
elements.paginationList.innerHTML = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
elements.pagination.style.display = 'block'
|
||||||
|
|
||||||
|
const isFirstPage = page === 0
|
||||||
|
const isLastPage = page === nbPages - 1
|
||||||
|
|
||||||
|
// Responsive page display
|
||||||
|
const isMobile = window.innerWidth < 768
|
||||||
|
const maxVisiblePages = isMobile ? 3 : 5
|
||||||
|
let startPage = Math.max(0, page - Math.floor(maxVisiblePages / 2))
|
||||||
|
const endPage = Math.min(nbPages - 1, startPage + maxVisiblePages - 1)
|
||||||
|
|
||||||
|
// Adjust starting page to maintain max visible pages
|
||||||
|
if (endPage - startPage + 1 < maxVisiblePages) {
|
||||||
|
startPage = Math.max(0, endPage - maxVisiblePages + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
let pagesHTML = ''
|
||||||
|
|
||||||
|
// Only add ellipsis and first page when there are many pages
|
||||||
|
if (nbPages > maxVisiblePages && startPage > 0) {
|
||||||
|
pagesHTML += `
|
||||||
|
<li class="ais-Pagination-item ais-Pagination-item--page">
|
||||||
|
<a class="ais-Pagination-link" aria-label="Page 1" href="#" data-page="0">1</a>
|
||||||
|
</li>`
|
||||||
|
if (startPage > 1) {
|
||||||
|
pagesHTML += `
|
||||||
|
<li class="ais-Pagination-item ais-Pagination-item--ellipsis">
|
||||||
|
<span class="ais-Pagination-link">...</span>
|
||||||
|
</li>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add middle page numbers
|
||||||
|
for (let i = startPage; i <= endPage; i++) {
|
||||||
|
const isSelected = i === page
|
||||||
|
if (isSelected) {
|
||||||
|
pagesHTML += `
|
||||||
|
<li class="ais-Pagination-item ais-Pagination-item--page ais-Pagination-item--selected">
|
||||||
|
<span class="ais-Pagination-link" aria-label="Page ${i + 1}">${i + 1}</span>
|
||||||
|
</li>`
|
||||||
|
} else {
|
||||||
|
pagesHTML += `
|
||||||
|
<li class="ais-Pagination-item ais-Pagination-item--page">
|
||||||
|
<a class="ais-Pagination-link" aria-label="Page ${i + 1}" href="#" data-page="${i}">${i + 1}</a>
|
||||||
|
</li>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only add ellipsis and last page when there are many pages
|
||||||
|
if (nbPages > maxVisiblePages && endPage < nbPages - 1) {
|
||||||
|
if (endPage < nbPages - 2) {
|
||||||
|
pagesHTML += `
|
||||||
|
<li class="ais-Pagination-item ais-Pagination-item--ellipsis">
|
||||||
|
<span class="ais-Pagination-link">...</span>
|
||||||
|
</li>`
|
||||||
|
}
|
||||||
|
pagesHTML += `
|
||||||
|
<li class="ais-Pagination-item ais-Pagination-item--page">
|
||||||
|
<a class="ais-Pagination-link" aria-label="Page ${nbPages}" href="#" data-page="${nbPages - 1}">${nbPages}</a>
|
||||||
|
</li>`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nbPages > 1) {
|
||||||
|
elements.paginationList.innerHTML = `
|
||||||
|
<li class="ais-Pagination-item ais-Pagination-item--previousPage ${isFirstPage ? 'ais-Pagination-item--disabled' : ''}">
|
||||||
|
${isFirstPage
|
||||||
|
? '<span class="ais-Pagination-link ais-Pagination-link--disabled" aria-label="Previous Page"><i class="fas fa-angle-left"></i></span>'
|
||||||
|
: `<a class="ais-Pagination-link" aria-label="Previous Page" href="#" data-page="${page - 1}"><i class="fas fa-angle-left"></i></a>`
|
||||||
|
}
|
||||||
|
</li>
|
||||||
|
${pagesHTML}
|
||||||
|
<li class="ais-Pagination-item ais-Pagination-item--nextPage ${isLastPage ? 'ais-Pagination-item--disabled' : ''}">
|
||||||
|
${isLastPage
|
||||||
|
? '<span class="ais-Pagination-link ais-Pagination-link--disabled" aria-label="Next Page"><i class="fas fa-angle-right"></i></span>'
|
||||||
|
: `<a class="ais-Pagination-link" aria-label="Next Page" href="#" data-page="${page + 1}"><i class="fas fa-angle-right"></i></a>`
|
||||||
|
}
|
||||||
|
</li>`
|
||||||
|
} else {
|
||||||
|
elements.pagination.style.display = 'none'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear search results and stats
|
||||||
|
const clearSearchResults = () => {
|
||||||
|
const container = document.getElementById('local-search-results')
|
||||||
|
container.textContent = ''
|
||||||
|
statsItem.textContent = ''
|
||||||
|
toggleResultsVisibility(false)
|
||||||
|
if (enablePagination) {
|
||||||
|
currentResultItems = []
|
||||||
|
currentPage = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show no results message
|
||||||
|
const showNoResults = searchText => {
|
||||||
|
const container = document.getElementById('local-search-results')
|
||||||
|
container.textContent = ''
|
||||||
|
const statsDiv = document.createElement('div')
|
||||||
|
statsDiv.className = 'search-result-stats'
|
||||||
|
statsDiv.textContent = languages.hits_empty.replace(/\$\{query}/, searchText)
|
||||||
|
statsItem.innerHTML = statsDiv.outerHTML
|
||||||
|
toggleResultsVisibility(false)
|
||||||
|
if (enablePagination) {
|
||||||
|
currentResultItems = []
|
||||||
|
currentPage = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const inputEventFunction = () => {
|
const inputEventFunction = () => {
|
||||||
if (!localSearch.isfetched) return
|
if (!localSearch.isfetched) return
|
||||||
let searchText = input.value.trim().toLowerCase()
|
let searchText = input.value.trim().toLowerCase()
|
||||||
isXml && (searchText = searchText.replace(/</g, '<').replace(/>/g, '>'))
|
isXml && (searchText = searchText.replace(/</g, '<').replace(/>/g, '>'))
|
||||||
if (searchText !== '') $loadingStatus.innerHTML = '<i class="fas fa-spinner fa-pulse"></i>'
|
|
||||||
|
if (searchText !== '') $loadingStatus.hidden = false
|
||||||
|
|
||||||
const keywords = searchText.split(/[-\s]+/)
|
const keywords = searchText.split(/[-\s]+/)
|
||||||
const container = document.getElementById('local-search-results')
|
|
||||||
let resultItems = []
|
let resultItems = []
|
||||||
|
|
||||||
if (searchText.length > 0) {
|
if (searchText.length > 0) {
|
||||||
// Perform local searching
|
|
||||||
resultItems = localSearch.getResultItems(keywords)
|
resultItems = localSearch.getResultItems(keywords)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keywords.length === 1 && keywords[0] === '') {
|
if (keywords.length === 1 && keywords[0] === '') {
|
||||||
container.textContent = ''
|
clearSearchResults()
|
||||||
statsItem.textContent = ''
|
|
||||||
} else if (resultItems.length === 0) {
|
} else if (resultItems.length === 0) {
|
||||||
container.textContent = ''
|
showNoResults(searchText)
|
||||||
const statsDiv = document.createElement('div')
|
|
||||||
statsDiv.className = 'search-result-stats'
|
|
||||||
statsDiv.textContent = languages.hits_empty.replace(/\$\{query}/, searchText)
|
|
||||||
statsItem.innerHTML = statsDiv.outerHTML
|
|
||||||
} else {
|
} else {
|
||||||
|
// Sort results by relevance
|
||||||
resultItems.sort((left, right) => {
|
resultItems.sort((left, right) => {
|
||||||
if (left.includedCount !== right.includedCount) {
|
if (left.includedCount !== right.includedCount) {
|
||||||
return right.includedCount - left.includedCount
|
return right.includedCount - left.includedCount
|
||||||
@@ -279,14 +468,14 @@ window.addEventListener('load', () => {
|
|||||||
return right.id - left.id
|
return right.id - left.id
|
||||||
})
|
})
|
||||||
|
|
||||||
const stats = languages.hits_stats.replace(/\$\{hits}/, resultItems.length)
|
if (enablePagination) {
|
||||||
|
currentResultItems = resultItems
|
||||||
container.innerHTML = `<ol class="search-result-list">${resultItems.map(result => result.item).join('')}</ol>`
|
currentPage = 0
|
||||||
statsItem.innerHTML = `<hr><div class="search-result-stats">${stats}</div>`
|
}
|
||||||
window.pjax && window.pjax.refresh(container)
|
renderResults(searchText, resultItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
$loadingStatus.textContent = ''
|
$loadingStatus.hidden = true
|
||||||
}
|
}
|
||||||
|
|
||||||
let loadFlag = false
|
let loadFlag = false
|
||||||
@@ -340,11 +529,29 @@ window.addEventListener('load', () => {
|
|||||||
localSearch.fetchData()
|
localSearch.fetchData()
|
||||||
}
|
}
|
||||||
localSearch.highlightSearchWords(document.getElementById('article-container'))
|
localSearch.highlightSearchWords(document.getElementById('article-container'))
|
||||||
|
|
||||||
|
// Pagination event delegation - only add if pagination is enabled
|
||||||
|
if (enablePagination) {
|
||||||
|
elements.pagination.addEventListener('click', e => {
|
||||||
|
e.preventDefault()
|
||||||
|
const link = e.target.closest('a[data-page]')
|
||||||
|
if (link) {
|
||||||
|
const page = parseInt(link.dataset.page, 10)
|
||||||
|
if (!isNaN(page) && currentResultItems.length > 0) {
|
||||||
|
currentPage = page
|
||||||
|
renderResults(input.value.trim().toLowerCase(), currentResultItems)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial state
|
||||||
|
toggleResultsVisibility(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('search:loaded', () => {
|
window.addEventListener('search:loaded', () => {
|
||||||
const $loadDataItem = document.getElementById('loading-database')
|
const $loadDataItem = document.getElementById('loading-database')
|
||||||
$loadDataItem.nextElementSibling.style.display = 'block'
|
$loadDataItem.nextElementSibling.style.visibility = 'visible'
|
||||||
$loadDataItem.remove()
|
$loadDataItem.remove()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -106,7 +106,7 @@
|
|||||||
|
|
||||||
loadComment: (dom, callback) => {
|
loadComment: (dom, callback) => {
|
||||||
if ('IntersectionObserver' in window) {
|
if ('IntersectionObserver' in window) {
|
||||||
const observerItem = new IntersectionObserver((entries) => {
|
const observerItem = new IntersectionObserver(entries => {
|
||||||
if (entries[0].isIntersecting) {
|
if (entries[0].isIntersecting) {
|
||||||
callback()
|
callback()
|
||||||
observerItem.disconnect()
|
observerItem.disconnect()
|
||||||
@@ -186,6 +186,7 @@
|
|||||||
|
|
||||||
if (service === 'medium_zoom') {
|
if (service === 'medium_zoom') {
|
||||||
mediumZoom(ele, { background: 'var(--zoom-bg)' })
|
mediumZoom(ele, { background: 'var(--zoom-bg)' })
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (service === 'fancybox') {
|
if (service === 'fancybox') {
|
||||||
@@ -198,35 +199,71 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!window.fancyboxRun) {
|
if (!window.fancyboxRun) {
|
||||||
Fancybox.bind('[data-fancybox]', {
|
let options = ''
|
||||||
Hash: false,
|
if (Fancybox.version < '6') {
|
||||||
Thumbs: {
|
options = {
|
||||||
showOnStart: false
|
Hash: false,
|
||||||
},
|
Thumbs: {
|
||||||
Images: {
|
showOnStart: false
|
||||||
Panzoom: {
|
},
|
||||||
maxScale: 4
|
Images: {
|
||||||
}
|
Panzoom: {
|
||||||
},
|
maxScale: 4
|
||||||
Carousel: {
|
}
|
||||||
transition: 'slide'
|
},
|
||||||
},
|
Carousel: {
|
||||||
Toolbar: {
|
transition: 'slide'
|
||||||
display: {
|
},
|
||||||
left: ['infobar'],
|
Toolbar: {
|
||||||
middle: [
|
display: {
|
||||||
'zoomIn',
|
left: ['infobar'],
|
||||||
'zoomOut',
|
middle: [
|
||||||
'toggle1to1',
|
'zoomIn',
|
||||||
'rotateCCW',
|
'zoomOut',
|
||||||
'rotateCW',
|
'toggle1to1',
|
||||||
'flipX',
|
'rotateCCW',
|
||||||
'flipY'
|
'rotateCW',
|
||||||
],
|
'flipX',
|
||||||
right: ['slideshow', 'thumbs', 'close']
|
'flipY'
|
||||||
|
],
|
||||||
|
right: ['slideshow', 'thumbs', 'close']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
} else {
|
||||||
|
options = {
|
||||||
|
Hash: false,
|
||||||
|
Carousel: {
|
||||||
|
transition: 'slide',
|
||||||
|
Thumbs: {
|
||||||
|
showOnStart: false
|
||||||
|
},
|
||||||
|
Toolbar: {
|
||||||
|
display: {
|
||||||
|
left: ['counter'],
|
||||||
|
middle: [
|
||||||
|
'zoomIn',
|
||||||
|
'zoomOut',
|
||||||
|
'toggle1to1',
|
||||||
|
'rotateCCW',
|
||||||
|
'rotateCW',
|
||||||
|
'flipX',
|
||||||
|
'flipY',
|
||||||
|
'reset'
|
||||||
|
],
|
||||||
|
right: ['autoplay', 'thumbs', 'close']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Zoomable: {
|
||||||
|
Panzoom: {
|
||||||
|
maxScale: 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Fancybox.bind('[data-fancybox]', options)
|
||||||
window.fancyboxRun = true
|
window.fancyboxRun = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user