由於整合了多個設定並更改了部分設定名稱,升級到 5.0 版本時,請重新設定 _config.yml 文件。

1. 新增 macstyle 設定,取消 mac / mac light 主題設定
2. 整合搜索相關設定
3. 修改程式碼區塊設定
4. 主頁文章新增多種版面配置
5. 新增說說頁面
6. 適配 hexo-blog-encrypt 加密插件
7. 改善手機端目錄的開啟效果
8. 新增平滑滾動功能
9. 支持以程式碼區塊方式撰寫 mermaid 圖表
10. 可自訂文章標題位置
11. 新增程式碼全螢幕按鈕
12. 友情連結頭像改為圓角設計
13. 優化程式碼,使用 hexo-util 的參數和 hexo 內建參數
14. 可自訂搜索框提示文字
15. 未設定選單時,不顯示側邊欄目錄和按鈕
16. 螢幕寬度超過 2000px 時,增加卡片高度
17. 根據語言設定調整字體:簡體中文使用雅黑,其他使用正黑體
18. 更新 plugins.yml
19. 全新的側邊欄界面設計
20. 新增 giscus 的 js 設定
21. 調整 utterances js 的設定位置
22. 新增 utterances option 設定
23. 修改 giscus 的主題設定
24. 多個界面元素改為圓角設計
25. 可選擇圓角或直角界面風格
26. 圖庫加載按鈕新增圖標
27. 改善標籤頁面的滑鼠懸停效果
28. 調整側邊欄的滑鼠懸停效果
29. 微調部分界面元素

1. 修復 Hexo 新版本下 Prism.js 無法正確高亮的問題
2. 修復文章標籤為空時可能出現的錯誤
3. 修正 mermaid 圖表可能出現的錯誤
4. 解決未設定選單時控制台報錯的問題
5. 修復 Algolia 搜索的每頁顯示數量設定無效的問題
6. 解決 Algolia 搜索結果出現滾動條的問題
7. 修正滾動條出現上下按鈕的問題
8. 修復圖庫遠程連結未加前綴的問題
9. 修正 label 標籤外掛右側多餘空格的問題
10. 解決 APlayer 報告內存洩漏的問題

1. 優化 PJAX 下的函數調用
2. 整體代碼優化
3. 提升兼容性
4. 改善 Lighthouse 評分
5. 在 PJAX 關閉時減少不必要的全局變量
6. 優化 Waline 的 import 兼容性
7. 改善頁面進入效果
8. 優化程式碼區塊工具列顯示邏輯
9. 改善不同螢幕寬度下文章標題位置的顯示
10. 優化標籤顏色生成算法,避免過暗或過亮
11. 調整 Artalk 和 Waline 在夜間模式下的字體顏色,與主題保持一致
12. 調整 Algolia 搜索加載動畫位置,避免換行
13. 優化 Algolia 搜索結果為空時的處理
14. 改善系列文章的滑鼠懸停效果
15. 優化 404 頁面代碼
16. 解決搜索和側邊欄開啟時窗口抖動的問題
17. 優化 tabs 標籤外掛的代碼和效能
18. 改善 tabs 中使用 gallery 標籤外掛時的圖片加載邏輯
19. 優化目錄滾動效果,使當前標題保持在中間
20. 調整螢幕寬度超過 1024px 時 gallerygroup 的顯示數量
This commit is contained in:
Jerry
2024-08-03 19:05:57 +08:00
parent 0d52505331
commit 06f543ed96
123 changed files with 2226 additions and 1792 deletions

View File

@@ -12,7 +12,9 @@ hexo.extend.generator.register('404', function (locals) {
layout: ['page'],
data: {
type: '404',
top_img: false
top_img: false,
comments: false,
aside: false
}
}
})

View File

@@ -1,3 +1,5 @@
const { deepMerge } = require('hexo-util')
hexo.extend.filter.register('before_generate', () => {
const defaultConfig = {
nav: {
@@ -6,33 +8,36 @@ hexo.extend.filter.register('before_generate', () => {
fixed: false
},
menu: null,
highlight_theme: 'light',
highlight_theme_macStyle: false,
highlight_copy: true,
highlight_lang: true,
highlight_shrink: false,
highlight_fullpage: true,
highlight_height_limit: false,
code_word_wrap: false,
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: 'https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png',
img: '/img/butterfly-icon.png',
effect: false
},
disable_top_img: false,
index_img: null,
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,
position: 'both',
default_cover: null
},
error_img: {
@@ -42,7 +47,7 @@ hexo.extend.filter.register('before_generate', () => {
error_404: {
enable: false,
subtitle: 'Page Not Found',
background: 'https://i.loli.net/2020/05/19/aKOcLiyPl2JQdFD.png'
background: '/img/error-page.png'
},
post_meta: {
page: {
@@ -61,22 +66,20 @@ hexo.extend.filter.register('before_generate', () => {
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
},
anchor: {
auto_update: false,
click_to_scroll: false
},
photofigcaption: false,
copy: {
enable: true,
copyright: {
enable: false,
limit_count: 50
}
},
toc: {
post: true,
page: false,
@@ -110,7 +113,7 @@ hexo.extend.filter.register('before_generate', () => {
noticeOutdate: {
enable: false,
style: 'flat',
limit_day: 500,
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.'
@@ -118,7 +121,7 @@ hexo.extend.filter.register('before_generate', () => {
footer: {
owner: {
enable: true,
since: 2020
since: 2019
},
custom_text: null,
copyright: true
@@ -154,6 +157,13 @@ hexo.extend.filter.register('before_generate', () => {
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,
@@ -176,35 +186,21 @@ hexo.extend.filter.register('before_generate', () => {
limit: 8,
sort_order: null
},
card_webinfo: {
enable: true,
post_count: true,
last_push_date: true,
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
}
},
busuanzi: {
site_uv: true,
site_pv: true,
page_pv: true
},
runtimeshow: {
enable: false,
publish_date: null
},
newest_comments: {
enable: false,
sort_order: null,
limit: 6,
storage: 10,
avatar: true
},
rightside_bottom: null,
translate: {
enable: false,
default: '繁',
@@ -227,42 +223,68 @@ hexo.extend.filter.register('before_generate', () => {
hide: null,
show: null
},
mathjax: {
enable: false,
per_page: false
anchor: {
auto_update: false,
click_to_scroll: false
},
katex: {
enable: false,
per_page: false,
hide_scrollbar: true
},
algolia_search: {
enable: false,
hits: {
per_page: 6
photofigcaption: false,
copy: {
enable: true,
copyright: {
enable: false,
limit_count: 150
}
},
local_search: {
wordcount: {
enable: false,
preload: false,
top_n_per_article: 1,
unescape: false,
CDN: null
post_wordcount: true,
min2read: true,
total_wordcount: true
},
docsearch: {
enable: false,
appId: null,
apiKey: null,
indexName: null,
option: null
busuanzi: {
site_uv: true,
site_pv: true,
page_pv: true
},
sharejs: {
enable: true,
sites: 'facebook,twitter,wechat,weibo,qq'
math: {
use: null,
per_page: true,
hide_scrollbar: false,
mathjax: {
enableMenu: true,
tags: 'none'
},
katex: {
copy_tex: false
}
},
addtoany: {
enable: false,
item: 'facebook,twitter,wechat,sina_weibo,facebook_messenger,email,copy_link'
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,
@@ -310,7 +332,9 @@ hexo.extend.filter.register('before_generate', () => {
repo: null,
issue_term: 'pathname',
light_theme: 'github-light',
dark_theme: 'photon-dark'
dark_theme: 'photon-dark',
js: null,
option: null
},
facebook_comments: {
app_id: null,
@@ -329,10 +353,9 @@ hexo.extend.filter.register('before_generate', () => {
repo: null,
repo_id: null,
category_id: null,
theme: {
light: 'light',
dark: 'dark'
},
light_theme: 'light',
dark_theme: 'dark',
js: null,
option: null
},
remark42: {
@@ -346,26 +369,24 @@ hexo.extend.filter.register('before_generate', () => {
visitor: false,
option: null
},
chat_btn: false,
chat_hide_show: false,
chat: {
use: null,
rightside_button: false,
button_hide_show: false
},
chatra: {
enable: false,
id: null
},
tidio: {
enable: false,
public_key: null
},
daovoice: {
enable: false,
app_id: null
},
crisp: {
enable: false,
website_id: null
},
messenger: {
enable: false,
pageID: null,
lang: 'zh_TW'
},
@@ -380,20 +401,48 @@ hexo.extend.filter.register('before_generate', () => {
client: null,
enable_page_level_ads: true
},
ad: {
index: null,
aside: null,
post: null
},
site_verification: null,
index_site_info_top: null,
index_top_img_height: null,
category_ui: null,
tag_ui: null,
rounded_corners_ui: true,
text_align_justify: false,
background: null,
footer_bg: false,
mask: {
header: true,
footer: true
},
rightside_bottom: null,
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,
@@ -436,50 +485,9 @@ hexo.extend.filter.register('before_generate', () => {
random: false,
mobile: false
},
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
},
subtitle: {
enable: false,
effect: true,
typed_option: null,
source: false,
sub: null
},
preloader: {
enable: false,
source: 1,
pace_css_url: null
},
wordcount: {
enable: false,
post_wordcount: true,
min2read: true,
total_wordcount: true
},
medium_zoom: false,
fancybox: true,
lightbox: null,
series: {
enable: true,
enable: false,
orderBy: 'title',
order: 1,
number: true
@@ -527,6 +535,14 @@ hexo.extend.filter.register('before_generate', () => {
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
@@ -539,11 +555,11 @@ hexo.extend.filter.register('before_generate', () => {
CDN: {
internal_provider: 'local',
third_party_provider: 'jsdelivr',
version: true,
version: false,
custom_format: null,
option: null
}
}
hexo.theme.config = Object.assign(defaultConfig, hexo.theme.config)
hexo.theme.config = deepMerge(defaultConfig, hexo.theme.config)
}, 1)

View File

@@ -19,5 +19,6 @@ hexo.extend.filter.register('stylus:renderer', style => {
.define('$highlight_line_number', highlightLineNumber)
.define('$prismjs_enable', prismjsEnable)
.define('$prismjs_line_number', prismjsLineNumber)
.define('$language', hexo.config.language)
// .import(`${this.source_dir.replace(/\\/g, '/')}_data/css/*`)
})

View File

@@ -1,58 +0,0 @@
hexo.extend.helper.register('getArchiveLength', function () {
const { archive_generator: archiveGenerator } = hexo.config
if (archiveGenerator && archiveGenerator.enable === false) return this.site.posts.length
const { yearly, monthly, daily } = archiveGenerator
const { year, month, day } = this.page
if (yearly === false || !year) return this.site.posts.length
const posts = this.site.posts.sort('date')
const compareFunc = (type, y1, m1, d1, y2, m2, d2) => {
switch (type) {
case 'year':
return y1 === y2
case 'month':
return y1 === y2 && m1 === m2
case 'day':
return y1 === y2 && m1 === m2 && d1 === d2
default:
return false
}
}
const generateDateObj = (type) => {
return posts.reduce((dateObj, post) => {
const date = post.date.clone()
const year = date.year()
const month = date.month() + 1
const day = date.date()
const lastData = dateObj[dateObj.length - 1]
if (!lastData || !compareFunc(type, lastData.year, lastData.month, lastData.day, year, month, day)) {
const name = type === 'year' ? year : type === 'month' ? `${year}-${month}` : `${year}-${month}-${day}`
dateObj.push({
name,
year,
month,
day,
count: 1
})
} else {
lastData.count++
}
return dateObj
}, [])
}
const data = this.fragment_cache('createArchiveObj', () => {
const dateObjs = []
if (yearly) dateObjs.push(...generateDateObj('year'))
if (monthly) dateObjs.push(...generateDateObj('month'))
if (daily) dateObjs.push(...generateDateObj('day'))
return dateObjs
})
const name = month ? (day ? `${year}-${month}-${day}` : `${year}-${month}`) : year
return data.find(item => item.name === name).count
})

View File

@@ -0,0 +1,45 @@
hexo.extend.helper.register('getArchiveLength', function () {
const archiveGenerator = hexo.config.archive_generator
const posts = this.site.posts
const { yearly, monthly, daily } = archiveGenerator
const { year, month, day } = this.page
// Archives Page
if (!year) return posts.length
// Function to generate a unique key based on the granularity
const getKey = (post, type) => {
const date = post.date.clone()
const y = date.year()
const m = date.month() + 1
const d = date.date()
if (type === 'year') return `${y}`
if (type === 'month') return `${y}-${m}`
if (type === 'day') return `${y}-${m}-${d}`
}
// Create a map to count posts per period
const mapData = this.fragment_cache('createArchiveObj', () => {
const map = new Map()
posts.forEach(post => {
const keyYear = getKey(post, 'year')
const keyMonth = getKey(post, 'month')
const keyDay = getKey(post, 'day')
if (yearly) map.set(keyYear, (map.get(keyYear) || 0) + 1)
if (monthly) map.set(keyMonth, (map.get(keyMonth) || 0) + 1)
if (daily) map.set(keyDay, (map.get(keyDay) || 0) + 1)
})
return map
})
// Determine the appropriate key to fetch based on current page context
let key
if (yearly && year) key = `${year}`
if (monthly && month) key = `${year}-${month}`
if (daily && day) key = `${year}-${month}-${day}`
// Return the count for the current period or default to the total posts
return mapData.get(key) || posts.length
})

View File

@@ -1,6 +1,12 @@
/**
* Butterfly
* inject js to head
*
* addGlobalFn
* pjaxSendOnce - remove in pjaxSend
* pjaxCompleteOnce - remove in pjaxComplete
* pjaxSend - run in pjaxSend
* pjaxComplete - run in pjaxComplete
*/
'use strict'

View File

@@ -1,18 +1,10 @@
'use strict'
const { stripHTML, escapeHTML, prettyUrls } = require('hexo-util')
const { stripHTML, prettyUrls, truncate } = require('hexo-util')
const crypto = require('crypto')
hexo.extend.helper.register('page_description', function () {
const { config, page } = this
let description = page.description || page.content || page.title || config.description
if (description) {
description = escapeHTML(stripHTML(description).substring(0, 150)
.trim()
).replace(/\n/g, ' ')
return description
}
hexo.extend.helper.register('truncate', (content, length) => {
return truncate(stripHTML(content), { length, separator: ' ' }).replace(/\n/g, ' ')
})
hexo.extend.helper.register('cloudTags', function (options = {}) {
@@ -32,13 +24,26 @@ hexo.extend.helper.register('cloudTags', function (options = {}) {
sizes.push(length)
})
const getRandomColor = () => {
const randomColor = () => Math.floor(Math.random() * 201)
const r = randomColor()
const g = randomColor()
const b = randomColor()
// 確保顏色不是太暗,通過增加一個最低值
return `rgb(${Math.max(r, 50)}, ${Math.max(g, 50)}, ${Math.max(b, 50)})`
}
const generateStyle = (size, unit) => {
const fontSize = parseFloat(size.toFixed(2)) + unit
const color = getRandomColor()
return `font-size: ${fontSize}; color: ${color};`
}
const length = sizes.length - 1
source.sort(orderby, order).forEach(tag => {
const ratio = length ? sizes.indexOf(tag.length) / length : 0
const size = minfontsize + ((maxfontsize - minfontsize) * ratio)
let style = `font-size: ${parseFloat(size.toFixed(2))}${unit};`
const color = 'rgb(' + Math.floor(Math.random() * 201) + ', ' + Math.floor(Math.random() * 201) + ', ' + Math.floor(Math.random() * 201) + ')' // 0,0,0 -> 200,200,200
style += ` color: ${color}`
const style = generateStyle(size, unit)
result += `<a href="${env.url_for(tag.path)}" style="${style}">${tag.name}</a>`
})
return result
@@ -84,5 +89,5 @@ hexo.extend.helper.register('findArchivesTitle', function (page, menu, date) {
hexo.extend.helper.register('isImgOrUrl', function (path) {
const imgTestReg = /\.(png|jpe?g|gif|svg|webp)(\?.*)?$/i
return path.indexOf('//') !== -1 || imgTestReg.test(path)
return path.includes('//') || imgTestReg.test(path)
})

View File

@@ -8,9 +8,10 @@
hexo.extend.helper.register('related_posts', function (currentPost, allPosts) {
let relatedPosts = []
currentPost.tags.forEach(function (tag) {
const tagsData = currentPost.tags
tagsData.length && tagsData.forEach(function (tag) {
allPosts.forEach(function (post) {
if (isTagRelated(tag.name, post.tags)) {
if (currentPost.path !== post.path && isTagRelated(tag.name, post.tags)) {
const relatedPost = {
title: post.title,
path: post.path,
@@ -24,13 +25,12 @@ hexo.extend.helper.register('related_posts', function (currentPost, allPosts) {
if (index !== -1) {
relatedPosts[index].weight += 1
} else {
if (currentPost.path !== post.path) {
relatedPosts.push(relatedPost)
}
relatedPosts.push(relatedPost)
}
}
})
})
if (relatedPosts.length === 0) {
return ''
}
@@ -52,7 +52,7 @@ hexo.extend.helper.register('related_posts', function (currentPost, allPosts) {
for (let i = 0; i < Math.min(relatedPosts.length, limitNum); i++) {
const cover = relatedPosts[i].cover || 'var(--default-bg-color)'
const title = this.escape_html(relatedPosts[i].title)
result += `<div><a href="${this.url_for(relatedPosts[i].path)}" title="${title}">`
result += `<a href="${this.url_for(relatedPosts[i].path)}" title="${title}">`
if (relatedPosts[i].cover_type === 'img') {
result += `<img class="cover" src="${this.url_for(cover)}" alt="cover">`
} else {
@@ -64,7 +64,7 @@ hexo.extend.helper.register('related_posts', function (currentPost, allPosts) {
result += `<div class="content is-center"><div class="date"><i class="fas fa-history fa-fw"></i> ${this.date(relatedPosts[i].updated, hexoConfig.date_format)}</div>`
}
result += `<div class="title">${title}</div>`
result += '</div></a></div>'
result += '</div></a>'
}
result += '</div></div>'
@@ -72,29 +72,14 @@ hexo.extend.helper.register('related_posts', function (currentPost, allPosts) {
}
})
function isTagRelated (tagName, TBDtags) {
let result = false
TBDtags.forEach(function (tag) {
if (tagName === tag.name) {
result = true
}
})
return result
function isTagRelated (tagName, tags) {
return tags.some(tag => tag.name === tagName)
}
function findItem (arrayToSearch, attr, val) {
for (let i = 0; i < arrayToSearch.length; i++) {
if (arrayToSearch[i][attr] === val) {
return i
}
}
return -1
return arrayToSearch.findIndex(item => item[attr] === val)
}
function compare (attr) {
return function (a, b) {
const val1 = a[attr]
const val2 = b[attr]
return val2 - val1
}
return (a, b) => b[attr] - a[attr]
}

View File

@@ -19,6 +19,7 @@ const gallery = (args, content) => {
if (args[0] === 'url') {
[type, dataStr, button] = args // url,[link],[lazyload]
dataStr = urlFor(dataStr)
} else {
[button] = args // [lazyload]
const regex = /!\[(.*?)\]\(([^\s]*)\s*(?:["'](.*?)["']?)?\s*\)/g
@@ -39,9 +40,7 @@ const gallery = (args, content) => {
}
return `<div class="gallery-container" data-type="${type}" data-button="${button}">
<div class="gallery-data">${dataStr}</div>
<div class="gallery-items">
</div>
<div class="gallery-items">${dataStr}</div>
</div>`
}

View File

@@ -8,7 +8,7 @@
const addLabel = args => {
const [text, className = 'default'] = args
return `<mark class="hl-label ${className}">${text}</mark> `
return `<mark class="hl-label ${className}">${text}</mark>`
}
hexo.extend.tag.register('label', addLabel, { ends: false })

View File

@@ -63,7 +63,7 @@ function series (args) {
result += `<li><a href="${urlFor(ele.path)}" title="${ele.title}">${ele.title}</a></li>`
})
return series.number ? `<ol>${result}</ol>` : `<ul>${result}</ul>`
return series.number ? `<ol class="series-items">${result}</ol>` : `<ul class="series-items">${result}</ul>`
}
hexo.extend.tag.register('series', series, { ends: false })

View File

@@ -9,7 +9,7 @@
const postTabs = (args, content) => {
const tabBlock = /<!--\s*tab (.*?)\s*-->\n([\w\W\s\S]*?)<!--\s*endtab\s*-->/g
args = args.join(' ').split(',')
const tabName = args[0]
const tabName = args[0] || 'tab'
const tabActive = Number(args[1]) || 0
const matches = []
let match
@@ -18,8 +18,6 @@ const postTabs = (args, content) => {
let tabContent = ''
let noDefault = true
!tabName && hexo.log.warn('Tabs block must have unique name!')
while ((match = tabBlock.exec(content)) !== null) {
matches.push(match[1], match[2])
}
@@ -29,34 +27,31 @@ const postTabs = (args, content) => {
let postContent = matches[i + 1]
let tabCaption = tabParameters[0] || ''
let tabIcon = tabParameters[1] || ''
let tabHref = ''
postContent = hexo.render.renderSync({ text: postContent, engine: 'markdown' }).trim()
tabId += 1
tabHref = (tabName + ' ' + tabId).toLowerCase().split(' ').join('-');
((tabCaption.length === 0) && (tabIcon.length === 0)) && (tabCaption = tabName + ' ' + tabId)
if (tabCaption.length === 0 && tabIcon.length === 0) tabCaption = tabName + ' ' + tabId
const isOnlyicon = tabIcon.length > 0 && tabCaption.length === 0 ? ' style="text-align: center;"' : ''
const icon = tabIcon.trim()
tabIcon.length > 0 && (tabIcon = `<i class="${icon}"${isOnlyicon}></i>`)
tabIcon.length > 0 && (tabIcon = `<i class="${icon}"></i>`)
let isActive = ''
if ((tabActive > 0 && tabActive === tabId) || (tabActive === 0 && tabId === 1)) {
isActive = ' active'
noDefault = false
}
tabNav += `<button type="button" class="tab ${isActive}" data-href="${tabHref}">${tabIcon + tabCaption.trim()}</button>`
tabContent += `<div class="tab-item-content${isActive}" id="${tabHref}">${postContent}</div>`
tabNav += `<button type="button" class="tab${isActive}">${tabIcon + tabCaption.trim()}</button>`
tabContent += `<div class="tab-item-content${isActive}">${postContent}</div>`
}
const toTop = '<div class="tab-to-top"><button type="button" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div>'
tabNav = `<ul class="nav-tabs${noDefault ? ' no-default' : ''}">${tabNav}</ul>`
tabNav = `<div class="nav-tabs${noDefault ? ' no-default' : ''}">${tabNav}</div>`
tabContent = `<div class="tab-contents">${tabContent}</div>`
return `<div class="tabs" id="${tabName.toLowerCase().split(' ').join('-')}">${tabNav + tabContent + toTop}</div>`
return `<div class="tabs">${tabNav + tabContent + toTop}</div>`
}
hexo.extend.tag.register('tabs', postTabs, { ends: true })