diff --git a/_config.butterfly.yml b/_config.butterfly.yml index 748a8b8..3ec64bd 100644 --- a/_config.butterfly.yml +++ b/_config.butterfly.yml @@ -1056,8 +1056,10 @@ inject: - - - + bottom: # - + # - - - - @@ -1066,6 +1068,7 @@ inject: - - - + # - # CDN Settings diff --git a/package-lock.json b/package-lock.json index e5dd254..59ecfbb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,8 @@ "hexo-theme-landscape": "^1.0.0", "hexo-wordcount": "^6.0.1", "node-fetch": "^3.3.2", - "p-limit": "^6.2.0" + "p-limit": "^6.2.0", + "vite-plugin-require-transform": "^1.0.21" } }, "node_modules/@adobe/css-tools": { @@ -55,6 +56,45 @@ "lru-cache": "^10.4.3" } }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -97,6 +137,61 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/traverse/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/@babel/types": { "version": "7.28.2", "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.2.tgz", @@ -330,6 +425,41 @@ } } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.30", + "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@types/trusted-types": { "version": "2.0.7", "resolved": "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz", @@ -3048,6 +3178,18 @@ } } }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmmirror.com/jsonparse/-/jsonparse-1.3.1.tgz", @@ -4422,6 +4564,18 @@ "node": ">= 0.8" } }, + "node_modules/vite-plugin-require-transform": { + "version": "1.0.21", + "resolved": "https://registry.npmmirror.com/vite-plugin-require-transform/-/vite-plugin-require-transform-1.0.21.tgz", + "integrity": "sha512-A3SrHhVg9tCW35O7E8kcuB71YTEdVd3EaM1zh6gbH4zxy4WzXSfcNf0UiWmaHHhr6wdFhiiAGdpR6S0SUxXkGQ==", + "license": "ISC", + "dependencies": { + "@babel/generator": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" + } + }, "node_modules/void-elements": { "version": "3.1.0", "resolved": "https://registry.npmmirror.com/void-elements/-/void-elements-3.1.0.tgz", diff --git a/package.json b/package.json index 86bfa68..3155769 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "hexo-theme-landscape": "^1.0.0", "hexo-wordcount": "^6.0.1", "node-fetch": "^3.3.2", - "p-limit": "^6.2.0" + "p-limit": "^6.2.0", + "vite-plugin-require-transform": "^1.0.21" } } diff --git a/source/tags/index.md b/source/tags/index.md index b17c23a..94db5d7 100644 --- a/source/tags/index.md +++ b/source/tags/index.md @@ -4,3 +4,4 @@ date: 2025-08-09 21:33:16 type: tags comments: false --- + diff --git a/themes/butterfly/source/css/footer.css b/themes/butterfly/source/css/footer.css new file mode 100644 index 0000000..14385ea --- /dev/null +++ b/themes/butterfly/source/css/footer.css @@ -0,0 +1,321 @@ +#footer_icons{ + border-radius:12px 12px 0 0; + padding-top:2rem; + display:-webkit-box; + display:-moz-box; + display:-webkit-flex; + display:-ms-flexbox; + display:box; + display:flex; + -webkit-box-lines:multiple; + -moz-box-lines:multiple; + -o-box-lines:multiple; + -webkit-flex-wrap:wrap; + -ms-flex-wrap:wrap; + flex-wrap:wrap; + -webkit-box-pack:center; + -moz-box-pack:center; + -o-box-pack:center; + -ms-flex-pack:center; + -webkit-justify-content:center; + justify-content:center; + -webkit-box-align:center; + -moz-box-align:center; + -o-box-align:center; + -ms-flex-align:center; + -webkit-align-items:center; + align-items:center + } + #footer_icons .icon_link{ + height:40px; + width:40px; + display:-webkit-box; + display:-moz-box; + display:-webkit-flex; + display:-ms-flexbox; + display:box; + display:flex; + -webkit-box-align:center; + -moz-box-align:center; + -o-box-align:center; + -ms-flex-align:center; + -webkit-align-items:center; + align-items:center; + -webkit-box-pack:center; + -moz-box-pack:center; + -o-box-pack:center; + -ms-flex-pack:center; + -webkit-justify-content:center; + justify-content:center; + margin:1rem; + background-color:var(--font-color); + border-radius:50% + } + #footer_icons .icon_link:hover{ + background-color:var(--marcus-blue)!important + } + #footer_icons .icon_link i{ + font-size:20px; + color:#fff; + -webkit-transition:.3s; + -moz-transition:.3s; + -o-transition:.3s; + -ms-transition:.3s; + transition:.3s + } + #footer_icons>div{ + display:-webkit-box; + display:-moz-box; + display:-webkit-flex; + display:-ms-flexbox; + display:box; + display:flex + } + #footer_icons img.footer_logo{ + border-radius:50%; + width:60px; + height:60px; + margin:0 1rem; + cursor:pointer; + filter:drop-shadow(0 0 12px rgba(150,255,246,.4))!important; + -webkit-transition:all .25s; + -moz-transition:all .25s; + -o-transition:all .25s; + -ms-transition:all .25s; + transition:all .25s + } + #footer_icons img.footer_logo:hover{ + filter:drop-shadow(0 0 15px rgba(144,255,246,.7))!important + } + @media screen and (max-width:768px){ + #footer_icons img.footer_logo{ + display:none + } + #footer_icons a.icon_link{ + margin:1rem 20px + } + } + #footer_content,#footer_icons{ + margin:auto; + max-width:1220px; + width:97%; + background:rgba(245,249,255,.6) + } + @media screen and (min-width:2000px){ + #footer_content,#footer_icons{ + max-width:1720px!important + } + } + #footer_content{ + border-radius:0 0 12px 12px; + display:-webkit-box; + display:-moz-box; + display:-webkit-flex; + display:-ms-flexbox; + display:box; + display:flex; + -webkit-box-pack:justify; + -moz-box-pack:justify; + -o-box-pack:justify; + -ms-flex-pack:justify; + -webkit-justify-content:space-between; + justify-content:space-between; + -webkit-box-lines:multiple; + -moz-box-lines:multiple; + -o-box-lines:multiple; + -webkit-flex-wrap:wrap; + -ms-flex-wrap:wrap; + flex-wrap:wrap; + margin-bottom:2rem; + padding:0 2rem 2rem + } + #footer_content #friend-links-in-footer .footer-title button{ + font-size:14px; + color:var(--font-color); + margin-left:5px; + -webkit-transition:.3s; + -moz-transition:.3s; + -o-transition:.3s; + -ms-transition:.3s; + transition:.3s + } + #footer_content #friend-links-in-footer .footer-title button:hover{ + color:var(--marcus-blue) + } + @media screen and (max-width:768px){ + #footer_content #friend-links-in-footer h3{ + padding-left:10px + } + } + #footer_content .footer-group{ + min-width:120px + } + #footer_content .footer-title{ + color:var(--font-color) + } + #footer_content .footer-links{ + display:-webkit-box; + display:-moz-box; + display:-webkit-flex; + display:-ms-flexbox; + display:box; + display:flex; + -webkit-box-orient:vertical; + -moz-box-orient:vertical; + -o-box-orient:vertical; + -webkit-flex-direction:column; + -ms-flex-direction:column; + flex-direction:column + } + #footer_content .footer-item{ + font-size:1rem; + line-height:1.7; + color:var(--font-color) + } + #footer_content .footer-item:hover{ + color:var(--marcus-blue) + } + @media screen and (max-width:768px){ + #footer_content{ + padding:1rem + } + #footer_content .footer-group{ + text-align:center + } + } + #footer-bottom{ + padding:1rem; + background:var(--card-bg); + z-index:2; + border-top:1px solid #e3e8f7 + } + #footer-bottom a{ + display:-webkit-box; + display:-moz-box; + display:-webkit-flex; + display:-ms-flexbox; + display:box; + display:flex + } + #footer-bottom a:not(:first-child)::before{ + content:'|'; + font-size:14px; + line-height:2.45; + margin:0 10px + } + @media screen and (max-width:768px){ + #footer-bottom a:not(:first-child)::before{ + line-height:2.25; + margin:0 8px + } + } + #footer-bottom a:hover{ + color:var(--marcus-blue) + } + #footer-bottom a:hover::before{ + color:var(--font-color) + } + #footer-bottom .footer-bottom-content{ + display:-webkit-box; + display:-moz-box; + display:-webkit-flex; + display:-ms-flexbox; + display:box; + display:flex; + -webkit-box-pack:justify; + -moz-box-pack:justify; + -o-box-pack:justify; + -ms-flex-pack:justify; + -webkit-justify-content:space-between; + justify-content:space-between; + max-width:1220px; + width:100%; + margin:0 auto; + -webkit-box-lines:multiple; + -moz-box-lines:multiple; + -o-box-lines:multiple; + -webkit-flex-wrap:wrap; + -ms-flex-wrap:wrap; + flex-wrap:wrap + } + #footer-bottom .footer-bottom-content *{ + font-size:17px; + white-space:nowrap; + color:var(--font-color) + } + @media screen and (min-width:2000px){ + #footer-bottom .footer-bottom-content{ + max-width:1480px + } + } + #footer-bottom .footer-bottom-left,#footer-bottom .footer-bottom-right{ + display:-webkit-box; + display:-moz-box; + display:-webkit-flex; + display:-ms-flexbox; + display:box; + display:flex; + -webkit-box-orient:vertical; + -moz-box-orient:vertical; + -o-box-orient:vertical; + -webkit-flex-direction:column; + -ms-flex-direction:column; + flex-direction:column + } + #footer-bottom .footer-bottom-left div,#footer-bottom .footer-bottom-left span,#footer-bottom .footer-bottom-right div,#footer-bottom .footer-bottom-right span{ + display:-webkit-box; + display:-moz-box; + display:-webkit-flex; + display:-ms-flexbox; + display:box; + display:flex; + -webkit-box-align:center; + -moz-box-align:center; + -o-box-align:center; + -ms-flex-align:center; + -webkit-align-items:center; + align-items:center + } + #footer-bottom .footer-bottom-right div{ + -webkit-box-pack:end; + -moz-box-pack:end; + -o-box-pack:end; + -ms-flex-pack:end; + -webkit-justify-content:flex-end; + justify-content:flex-end + } + @media screen and (max-width:768px){ + #footer-bottom{ + padding:1rem 0!important + } + #footer-bottom .footer-bottom-content *{ + font-size:1rem + } + #footer-bottom .footer-bottom-left,#footer-bottom .footer-bottom-right{ + width:100%; + -webkit-box-align:center; + -moz-box-align:center; + -o-box-align:center; + -ms-flex-align:center; + -webkit-align-items:center; + align-items:center + } + } + [data-theme=dark] #footer_icons .icon_link{ + background-color:#ececec + } + [data-theme=dark] #footer_icons .icon_link i{ + color:#202020 + } + [data-theme=dark] #footer_icons .icon_link:hover i{ + color:#fff + } + [data-theme=dark] #footer_icons img.footer_logo{ + filter:drop-shadow(0 0 12px rgba(255,255,255,.3))!important + } + [data-theme=dark] #footer_icons img.footer_logo:hover{ + filter:drop-shadow(0 0 15px rgba(255,255,255,.5))!important + } + [data-theme=dark] #footer_content,[data-theme=dark] #footer_icons{ + background:rgba(24,24,30,.5) + } \ No newline at end of file diff --git a/themes/butterfly/source/js/chart.js b/themes/butterfly/source/js/chart.js new file mode 100644 index 0000000..c693704 --- /dev/null +++ b/themes/butterfly/source/js/chart.js @@ -0,0 +1,397 @@ +const cheerio = require('cheerio') +const moment = require('moment') + +hexo.extend.filter.register('after_render:html', function (locals) { + const $ = cheerio.load(locals) + const post = $('#posts-chart') + const tag = $('#tags-chart') + const category = $('#categories-chart') + const htmlEncode = false + + if (post.length > 0 || tag.length > 0 || category.length > 0) { + if (post.length > 0 && $('#postsChart').length === 0) { + if (post.attr('data-encode') === 'true') htmlEncode = true + post.after(postsChart(post.attr('data-start'))) + } + if (tag.length > 0 && $('#tagsChart').length === 0) { + if (tag.attr('data-encode') === 'true') htmlEncode = true + tag.after(tagsChart(tag.attr('data-length'))) + } + if (category.length > 0 && $('#categoriesChart').length === 0) { + if (category.attr('data-encode') === 'true') htmlEncode = true + category.after(categoriesChart(category.attr('data-parent'))) + } + + if (htmlEncode) { + return $.root().html().replace(/&#/g, '&#') + } else { + return $.root().html() + } + } else { + return locals + } +}, 15) + +function postsChart (startMonth) { + const startDate = moment(startMonth || '2020-01') + const endDate = moment() + + const monthMap = new Map() + const dayTime = 3600 * 24 * 1000 + for (let time = startDate; time <= endDate; time += dayTime) { + const month = moment(time).format('YYYY-MM') + if (!monthMap.has(month)) { + monthMap.set(month, 0) + } + } + hexo.locals.get('posts').forEach(function (post) { + const month = post.date.format('YYYY-MM') + if (monthMap.has(month)) { + monthMap.set(month, monthMap.get(month) + 1) + } + }) + const monthArr = JSON.stringify([...monthMap.keys()]) + const monthValueArr = JSON.stringify([...monthMap.values()]) + + return ` + ` +} + +function tagsChart (len) { + const tagArr = [] + hexo.locals.get('tags').map(function (tag) { + tagArr.push({ name: tag.name, value: tag.length, path: tag.path }) + }) + tagArr.sort((a, b) => { return b.value - a.value }) + + const dataLength = Math.min(tagArr.length, len) || tagArr.length + const tagNameArr = [] + for (let i = 0; i < dataLength; i++) { + tagNameArr.push(tagArr[i].name) + } + const tagNameArrJson = JSON.stringify(tagNameArr) + const tagArrJson = JSON.stringify(tagArr) + + return ` + ` +} + +function categoriesChart (dataParent) { + const categoryArr = [] + let categoryParentFlag = false + hexo.locals.get('categories').map(function (category) { + if (category.parent) categoryParentFlag = true + categoryArr.push({ + name: category.name, + value: category.length, + path: category.path, + id: category._id, + parentId: category.parent || '0' + }) + }) + categoryParentFlag = categoryParentFlag && dataParent === 'true' + categoryArr.sort((a, b) => { return b.value - a.value }) + function translateListToTree (data, parent) { + let tree = [] + let temp + data.forEach((item, index) => { + if (data[index].parentId == parent) { + let obj = data[index]; + temp = translateListToTree(data, data[index].id); + if (temp.length > 0) { + obj.children = temp + } + if (tree.indexOf()) + tree.push(obj) + } + }) + return tree + } + const categoryNameJson = JSON.stringify(categoryArr.map(function (category) { return category.name })) + const categoryArrJson = JSON.stringify(categoryArr) + const categoryArrParentJson = JSON.stringify(translateListToTree(categoryArr, '0')) + + return ` + ` +}