From ffeab5e20cb76cde25b704df7f12ff0106a41bec Mon Sep 17 00:00:00 2001 From: Jerry Date: Sun, 14 Nov 2021 17:50:11 +0800 Subject: [PATCH] =?UTF-8?q?breaking=20changes:=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=8D=9A=E5=A4=A9api=20breaking=20changes:=20=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=20waline=20=E7=9A=84=20avatar=20=E5=92=8C=20avatar=20?= =?UTF-8?q?cdn=20=E9=85=8D=E7=BD=AE=20feat:=20anchor=20=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E9=99=90=E5=88=B6=20post=20=E9=A0=81=E9=96=8B=E5=95=9F?= =?UTF-8?q?=EF=BC=8C=E5=8F=AF=E4=BB=A5=E5=9C=A8=E4=BB=BB=E4=BD=95=E9=A0=81?= =?UTF-8?q?=E9=9D=A2=E9=96=8B=E5=95=9F=20feat:=20=E6=96=87=E7=AB=A0?= =?UTF-8?q?=E6=A8=99=E9=A1=8C=E6=94=AF=E6=8C=81=E9=BB=9E=E6=93=8A=E8=B7=B3?= =?UTF-8?q?=E8=BD=89=E5=88=B0=E6=AD=A4=E6=A8=99=E9=A1=8C=E9=96=8B=E5=A7=8B?= =?UTF-8?q?=E9=96=B2=E8=AE=80=20closed=20#653=20feat:=20toc=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E8=A8=AD=E7=BD=AE=E5=85=A8=E9=83=A8=E5=B1=95=E9=96=8B?= =?UTF-8?q?=20closed=20#709=20feat:=20=E5=A2=9E=E5=8A=A0=20=E6=96=B0?= =?UTF-8?q?=E7=9A=84=E8=A9=95=E8=AB=96=E7=B3=BB=E7=B5=B1=20giscus=20feat:?= =?UTF-8?q?=20=E6=94=AF=E6=8C=81=E6=96=B0=E7=9A=84=E8=A9=95=E8=AB=96?= =?UTF-8?q?=E5=90=8D=E5=AF=AB=E6=B3=95=EF=BC=8C=E4=B8=BB=E9=A1=8C=E6=9C=83?= =?UTF-8?q?=E8=99=95=E7=90=86=E8=A9=95=E8=AB=96=E5=90=8D=E5=AD=97=E5=A4=A7?= =?UTF-8?q?=E5=B0=8F=E5=AF=AB=EF=BC=8C=E8=88=8A=E7=9A=84=E6=9C=83=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=20feat:=20=E5=8F=8B=E6=83=85=E9=8F=88=E6=8E=A5?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E5=A2=9E=E5=8A=A0=20fetch=20url=20=E7=8D=B2?= =?UTF-8?q?=E5=8F=96=20improvement:=20=E9=BC=A0=E6=A8=99=E7=A7=BB=E5=88=B0?= =?UTF-8?q?=E6=9C=80=E6=96=B0=E8=A9=95=E8=AB=96=E5=85=A7=E5=AE=B9=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20title=20=E9=A1=AF=E7=A4=BA=20fix:=20?= =?UTF-8?q?=E4=BF=AE=E5=BE=A9=20rightside=20=E9=81=AE=E6=93=8B=E5=85=A7?= =?UTF-8?q?=E5=AE=B9=EF=BC=8C=E5=B0=8E=E8=87=B4=E5=85=A7=E5=AE=B9=E7=84=A1?= =?UTF-8?q?=E6=B3=95=E9=BB=9E=E6=93=8A=E7=9A=84=20bug=20fix:=20=E4=BF=AE?= =?UTF-8?q?=E5=BE=A9=20mermaid=20=E5=9C=A8=E6=9F=90=E4=BA=9B=E9=A0=81?= =?UTF-8?q?=E9=9D=A2=EF=BC=88=E6=9C=89=E5=85=83=E7=B4=A0=20id=20=E7=82=BA?= =?UTF-8?q?=20mermaid=20=E6=99=82=EF=BC=89=20=E6=9C=83=E7=84=A1=E6=B3=95?= =?UTF-8?q?=E5=8A=A0=E8=BC=89=E7=9A=84=20bug=20fix:=20=E4=BF=AE=E5=BE=A9?= =?UTF-8?q?=20=E6=90=9C=E7=B4=A2=E6=A1=86=E4=B8=8D=E6=9C=83=E8=87=AA?= =?UTF-8?q?=E5=8B=95=20focus=20=E7=9A=84=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- README_CN.md | 4 +- _config.yml | 29 +-- layout/includes/head/config.pug | 2 +- layout/includes/page/flink.pug | 67 +++++-- .../includes/third-party/comments/giscus.pug | 49 +++++ .../includes/third-party/comments/index.pug | 2 + layout/includes/third-party/comments/js.pug | 2 + .../includes/third-party/comments/waline.pug | 6 +- layout/includes/third-party/math/mermaid.pug | 3 +- .../newest-comments/disqus-comment.pug | 2 +- .../newest-comments/github-issues.pug | 2 +- .../newest-comments/twikoo-comment.pug | 2 +- .../third-party/newest-comments/valine.pug | 2 +- .../third-party/newest-comments/waline.pug | 11 +- layout/includes/third-party/subtitle.pug | 36 +--- layout/includes/widget/card_post_toc.pug | 6 +- package.json | 6 +- scripts/events/{cdn.js => config.js} | 23 ++- scripts/tag/timeline.js | 2 - source/css/_global/function.styl | 4 +- source/css/_global/index.styl | 1 + source/css/_layout/aside.styl | 17 +- source/css/_layout/post.styl | 28 ++- source/css/_layout/rightside.styl | 22 ++- source/css/_search/index.styl | 1 - source/js/main.js | 182 +++++++++--------- source/js/search/algolia.js | 13 +- source/js/search/local-search.js | 24 +-- source/js/utils.js | 36 ++-- 30 files changed, 363 insertions(+), 225 deletions(-) create mode 100644 layout/includes/third-party/comments/giscus.pug rename scripts/events/{cdn.js => config.js} (91%) diff --git a/README.md b/README.md index 9179f3a..ef8372c 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ ![](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/theme-butterfly-readme.png) -Demo: 👍 [Butterfly](https://butterfly.js.org/) || 🤞 [MYW](https://immyw.com/) +Demo: 👍 [Butterfly](https://butterfly.js.org/) || 🤞 [CrazyWong](https://crazywong.com/) Docs: 📖 [Butterfly Docs](https://butterfly.js.org/posts/21cfbf15/) @@ -79,7 +79,7 @@ npm i hexo-theme-butterfly - [x] Related articles - [x] Displays outdated notice for a post - [x] Share (AddThis/Sharejs/Addtoany) -- [X] Comment (Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo) +- [X] Comment (Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus) - [x] Multiple Comment System Support - [x] Online Chats (Chatra/Tidio/Daovoice/Gitter/Crisp) - [x] Web analytics diff --git a/README_CN.md b/README_CN.md index e680844..5d7e0bb 100644 --- a/README_CN.md +++ b/README_CN.md @@ -14,7 +14,7 @@ ![](https://cdn.jsdelivr.net/gh/jerryc127/CDN@m2/img/theme-butterfly-readme.png) -預覽: 👍 [Butterfly](https://butterfly.js.org/) || 🤞 [MYW](https://immyw.com/) +預覽: 👍 [Butterfly](https://butterfly.js.org/) || 🤞 [CrazyWong](https://crazywong.com/) 文檔: 📖 [Butterfly Docs](https://butterfly.js.org/posts/21cfbf15/) @@ -79,7 +79,7 @@ theme: butterfly - [x] 顯示相關文章 - [x] 過期文章提醒 - [x] 多種分享系統(AddThis/Sharejs/Addtoany) -- [X] 多種評論系統(Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo) +- [X] 多種評論系統(Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus) - [x] 支持雙評論部署 - [x] 多種在線聊天(Chatra/Tidio/Daovoice/Gitter/Crisp) - [x] 多種分析系統 diff --git a/_config.yml b/_config.yml index cb4e7ea..33580e0 100644 --- a/_config.yml +++ b/_config.yml @@ -171,6 +171,7 @@ toc: post: true page: false number: true + expand: false style_simple: false # for post post_copyright: @@ -253,10 +254,7 @@ addtoany: comments: # Up to two comments system, the first will be shown as default - # Choose: Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo - use: - # - Valine - # - Disqus + use: # Valine,Disqus text: true # Display the comment name next to the button # lazyload: The comment system will be load when comment element enters the browser's viewport. # If you set it to true, the comment count will be invalid @@ -308,8 +306,6 @@ valine: # https://waline.js.org/ waline: serverURL: # Waline server address url - avatar: monsterid # gravatar style https://zh-tw.gravatar.com/site/implement/images/#default-image - avatarCDN: # Gravatar CDN baseURL bg: # waline background visitor: false option: @@ -341,6 +337,17 @@ twikoo: visitor: false option: +# Giscus +# https://giscus.app/ +giscus: + repo: + repo_id: + category_id: + theme: + light: light + dark: dark + option: + # Chat Services # -------------------------------------- @@ -583,12 +590,11 @@ subtitle: effect: true # loop (循環打字) loop: true - # source調用第三方服務 + # source 調用第三方服務 # source: false 關閉調用 - # source: 1 調用搏天 api 的隨機語錄(簡體) - # source: 2 調用一言網的一句話(簡體) - # source: 3 調用一句網(簡體) - # source: 4 調用今日詩詞(簡體) + # source: 1 調用一言網的一句話(簡體) https://hitokoto.cn/ + # source: 2 調用一句網(簡體) http://yijuzhan.com/ + # source: 3 調用今日詩詞(簡體) https://www.jinrishici.com/ # subtitle 會先顯示 source , 再顯示 sub 的內容 source: false # 如果關閉打字效果,subtitle 只會顯示 sub 的第一行文字 @@ -836,6 +842,7 @@ CDN: utterances: twikoo: waline: + giscus: # share addtoany: diff --git a/layout/includes/head/config.pug b/layout/includes/head/config.pug index 5e15f0e..49fe4fe 100644 --- a/layout/includes/head/config.pug +++ b/layout/includes/head/config.pug @@ -119,5 +119,5 @@ script. }, isPhotoFigcaption: !{theme.photofigcaption}, islazyload: !{theme.lazyload.enable}, - isanchor: !{theme.anchor} + isAnchor: !{theme.anchor} } diff --git a/layout/includes/page/flink.pug b/layout/includes/page/flink.pug index f6c1063..e82186d 100644 --- a/layout/includes/page/flink.pug +++ b/layout/includes/page/flink.pug @@ -1,18 +1,57 @@ #article-container .flink - if site.data.link - each i in site.data.link - if i.class_name - h2!= i.class_name - if i.class_desc - .flink-desc!=i.class_desc - .flink-list - each item in i.link_list - .flink-list-item - a(href=url_for(item.link) title=item.name target="_blank") - .flink-item-icon - img.no-lightbox(src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt=item.name ) - .flink-item-name= item.name - .flink-item-desc(title=item.descr)= item.descr + if page.flink_url + script. + (()=>{ + const replaceSymbol = (str) => { + return str.replace(/[\p{P}\p{S}]/gu, '-') + } + + let result = '' + fetch('!{url_for(page.flink_url)}') + .then(response => response.json()) + .then(str => { + for(let i = 0; i < str.length; i++){ + const replaceClassName = replaceSymbol(str[i].class_name) + const className = str[i].class_name ? `

${str[i].class_name}

` : '' + const classDesc = str[i].class_desc ? `` : '' + + let listResult = '' + const lists = str[i].link_list + for(let j = 0; j < lists.length; j++){ + listResult += ` + ` + } + + result += `${className}${classDesc} ` + } + + document.querySelector('.flink').insertAdjacentHTML('afterbegin', result) + }) + })() + + else + if site.data.link + each i in site.data.link + if i.class_name + !=markdown(`## ${i.class_name}`) + if i.class_desc + .flink-desc!=i.class_desc + .flink-list + each item in i.link_list + .flink-list-item + a(href=url_for(item.link) title=item.name target="_blank") + .flink-item-icon + img.no-lightbox(src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt=item.name ) + .flink-item-name= item.name + .flink-item-desc(title=item.descr)= item.descr != page.content diff --git a/layout/includes/third-party/comments/giscus.pug b/layout/includes/third-party/comments/giscus.pug new file mode 100644 index 0000000..b09bf5a --- /dev/null +++ b/layout/includes/third-party/comments/giscus.pug @@ -0,0 +1,49 @@ +- const { repo, repo_id, category_id, option } = theme.giscus +- const themes = theme.giscus.theme +script. + function loadGiscus () { + let nowTheme = document.documentElement.getAttribute('data-theme') === 'dark' ? '!{themes.dark}' : '!{themes.light}' + + const config = Object.assign({ + src: '!{theme.CDN.giscus}', + 'data-repo': '!{repo}', + 'data-repo-id': '!{repo_id}', + 'data-category-id': '!{category_id}', + 'data-mapping': 'pathname', + 'data-theme': nowTheme, + 'data-reactions-enabled': '1', + crossorigin: 'anonymous', + async: true + },!{JSON.stringify(option)}) + + let ele = document.createElement('script') + for (let key in config) { + ele.setAttribute(key, config[key]) + } + document.getElementById('giscus-wrap').insertAdjacentElement('afterbegin',ele) + } + + function changeGiscusTheme () { + const theme = document.documentElement.getAttribute('data-theme') === 'dark' ? '!{themes.dark}' : '!{themes.light}' + + function sendMessage(message) { + const iframe = document.querySelector('iframe.giscus-frame'); + if (!iframe) return; + iframe.contentWindow.postMessage({ giscus: message }, 'https://giscus.app'); + } + + sendMessage({ + setConfig: { + theme: theme + } + }); + } + + if ('!{theme.comments.use[0]}' === 'Giscus' || !!{theme.comments.lazyload}) { + if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('giscus-wrap'), loadGiscus) + else loadGiscus() + } else { + function loadOtherComment () { + loadGiscus() + } + } diff --git a/layout/includes/third-party/comments/index.pug b/layout/includes/third-party/comments/index.pug index 3bab3bb..6c725d6 100644 --- a/layout/includes/third-party/comments/index.pug +++ b/layout/includes/third-party/comments/index.pug @@ -33,6 +33,8 @@ hr #twikoo-wrap when 'Waline' #waline-wrap + when 'Giscus' + #giscus-wrap when 'Facebook Comments' .fb-comments(data-colorscheme = theme.display_mode === 'dark' ? 'dark' : 'light' data-numposts= theme.facebook_comments.pageSize || 10 diff --git a/layout/includes/third-party/comments/js.pug b/layout/includes/third-party/comments/js.pug index 0b04451..1acd8d3 100644 --- a/layout/includes/third-party/comments/js.pug +++ b/layout/includes/third-party/comments/js.pug @@ -16,5 +16,7 @@ each name in theme.comments.use !=partial('includes/third-party/comments/twikoo', {}, {cache: true}) when 'Waline' !=partial('includes/third-party/comments/waline', {}, {cache: true}) + when 'Giscus' + !=partial('includes/third-party/comments/giscus', {}, {cache: true}) when 'Facebook Comments' !=partial('includes/third-party/comments/facebook_comments', {}, {cache: true}) diff --git a/layout/includes/third-party/comments/waline.pug b/layout/includes/third-party/comments/waline.pug index 2407e4a..cfd05c2 100644 --- a/layout/includes/third-party/comments/waline.pug +++ b/layout/includes/third-party/comments/waline.pug @@ -1,4 +1,4 @@ -- const { serverURL, avatar, avatarCDN, visitor, option } = theme.waline +- const { serverURL, visitor, option } = theme.waline script. function loadWaline () { @@ -6,15 +6,13 @@ script. const waline = new Waline(Object.assign({ el: '#waline-wrap', serverURL: '!{serverURL}', - avatar: '#{avatar}', - avatarCDN: '!{avatarCDN || "https://sdn.geekzu.org/avatar/"}', path: location.pathname, visitor: !{visitor}, dark: 'html[data-theme="dark"]' }, !{JSON.stringify(option)})) } - if (typeof Waline === 'function') initWaline() + if (typeof Waline === 'function') initWaline() else getScript('!{url_for(theme.CDN.waline)}').then(initWaline) } diff --git a/layout/includes/third-party/math/mermaid.pug b/layout/includes/third-party/math/mermaid.pug index 5716b30..31dda5e 100644 --- a/layout/includes/third-party/math/mermaid.pug +++ b/layout/includes/third-party/math/mermaid.pug @@ -3,6 +3,7 @@ script. const $mermaidWrap = document.querySelectorAll('#article-container .mermaid-wrap') if ($mermaidWrap.length) { window.runMermaid = () => { + window.loadMermaid = true const theme = document.documentElement.getAttribute('data-theme') === 'dark' ? '!{theme.mermaid.theme.dark}' : '!{theme.mermaid.theme.light}' Array.from($mermaidWrap).forEach((item, index) => { @@ -17,7 +18,7 @@ script. } const loadMermaid = () => { - window.mermaid ? runMermaid() : getScript('!{theme.CDN.mermaid}').then(runMermaid) + window.loadMermaid ? runMermaid() : getScript('!{theme.CDN.mermaid}').then(runMermaid) } window.pjax ? loadMermaid() : document.addEventListener('DOMContentLoaded', loadMermaid) diff --git a/layout/includes/third-party/newest-comments/disqus-comment.pug b/layout/includes/third-party/newest-comments/disqus-comment.pug index 896faab..5b95e74 100644 --- a/layout/includes/third-party/newest-comments/disqus-comment.pug +++ b/layout/includes/third-party/newest-comments/disqus-comment.pug @@ -49,7 +49,7 @@ script. } result += `
- ${array[i].content} + ${array[i].content}
${array[i].nick}
` } diff --git a/layout/includes/third-party/newest-comments/github-issues.pug b/layout/includes/third-party/newest-comments/github-issues.pug index 04fa27c..2153b11 100644 --- a/layout/includes/third-party/newest-comments/github-issues.pug +++ b/layout/includes/third-party/newest-comments/github-issues.pug @@ -75,7 +75,7 @@ script. } result += `
- ${array[i].content} + ${array[i].content}
${array[i].nick} /
` } diff --git a/layout/includes/third-party/newest-comments/twikoo-comment.pug b/layout/includes/third-party/newest-comments/twikoo-comment.pug index d8cea8c..71b2803 100644 --- a/layout/includes/third-party/newest-comments/twikoo-comment.pug +++ b/layout/includes/third-party/newest-comments/twikoo-comment.pug @@ -60,7 +60,7 @@ script. } result += `
- ${array[i].content} + ${array[i].content}
${array[i].nick} /
` } diff --git a/layout/includes/third-party/newest-comments/valine.pug b/layout/includes/third-party/newest-comments/valine.pug index 809da74..2aa758d 100644 --- a/layout/includes/third-party/newest-comments/valine.pug +++ b/layout/includes/third-party/newest-comments/valine.pug @@ -37,7 +37,7 @@ script. } result += `
- ${array[i].content} + ${array[i].content}
${array[i].nick} /
` } diff --git a/layout/includes/third-party/newest-comments/waline.pug b/layout/includes/third-party/newest-comments/waline.pug index 2b9b630..e412c37 100644 --- a/layout/includes/third-party/newest-comments/waline.pug +++ b/layout/includes/third-party/newest-comments/waline.pug @@ -16,13 +16,6 @@ script. return content } - const getIcon = (ava,mail) => { - if (ava) return ava - let defaultIcon = '!{ avatar ? `?d=${avatar}` : ''}' - let iconUrl = "!{avatarCDN ? avatarCDN : 'https://gravatar.loli.net/avatar/'}" + mail + defaultIcon - return iconUrl - } - const generateHtml = array => { let result = '' @@ -36,7 +29,7 @@ script. } result += `
- ${array[i].content} + ${array[i].content}
${array[i].nick} /
` } @@ -59,7 +52,7 @@ script. const walineArray = res.comments.map(e => { return { 'content': changeContent(e.comment), - 'avatar': getIcon(e.QQAvatar,e.mail), + 'avatar': e.avatar, 'nick': e.nick, 'url': e.url + '#' + e.objectId, 'date': e.updatedAt, diff --git a/layout/includes/third-party/subtitle.pug b/layout/includes/third-party/subtitle.pug index 1b2720c..21d705a 100644 --- a/layout/includes/third-party/subtitle.pug +++ b/layout/includes/third-party/subtitle.pug @@ -3,38 +3,6 @@ case source when 1 - script. - function subtitleType () { - fetch('https://api.btstu.cn/yan/api.php?charset=utf-8&encode=json') - .then(response => response.json()) - .then(data => { - if (!{effect}) { - const sub = !{JSON.stringify(subContent)} - sub.unshift(data.text) - window.typed = new Typed('#subtitle', { - strings: sub, - startDelay: 300, - typeSpeed: 150, - loop: !{loop}, - backSpeed: 50, - }) - } else { - document.getElementById('subtitle').innerHTML = data.text - } - }) - } - - if (!{effect}) { - if (typeof Typed === 'function') { - subtitleType() - } else { - getScript('!{url_for(theme.CDN.typed)}').then(subtitleType) - } - } else { - subtitleType() - } - - when 2 script. function subtitleType () { fetch('https://v1.hitokoto.cn') @@ -67,7 +35,7 @@ case source subtitleType() } - when 3 + when 2 script. function subtitleType () { getScript('https://yijuzhan.com/api/word.php?m=js').then(() => { @@ -99,7 +67,7 @@ case source subtitleType() } - when 4 + when 3 script. function subtitleType () { getScript('https://sdk.jinrishici.com/v2/browser/jinrishici.js').then(() => { diff --git a/layout/includes/widget/card_post_toc.pug b/layout/includes/widget/card_post_toc.pug index ec80335..4154221 100644 --- a/layout/includes/widget/card_post_toc.pug +++ b/layout/includes/widget/card_post_toc.pug @@ -1,4 +1,6 @@ - let tocNumber = page.toc_number !== undefined ? page.toc_number : theme.toc.number +- let tocExpand = page.toc_expand !== undefined ? page.toc_expand : theme.toc.expand +- let tocExpandClass = tocExpand ? 'is-expand' : '' #card-toc.card-widget .item-headline @@ -7,7 +9,7 @@ span.toc-percentage if (page.encrypt == true) - .toc-content.toc-div-class(style="display:none")!=toc(page.origin, {list_number: tocNumber}) + .toc-content.toc-div-class(class=tocExpandClass style="display:none")!=toc(page.origin, {list_number: tocNumber}) else - .toc-content!=toc(page.content, {list_number: tocNumber}) + .toc-content(class=tocExpandClass)!=toc(page.content, {list_number: tocNumber}) \ No newline at end of file diff --git a/package.json b/package.json index 1a32584..7e5536e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hexo-theme-butterfly", - "version": "4.0.0-b9", + "version": "4.0.0-b10", "description": "A Simple and Card UI Design theme for Hexo", "main": "package.json", "scripts": { @@ -20,13 +20,13 @@ }, "bugs": { "url": "https://github.com/jerryc127/hexo-theme-butterfly/issues", - "email": "i@immyw.com" + "email": "my@crazywong.com" }, "dependencies": { "hexo-renderer-stylus": "^2.0.1", "hexo-renderer-pug": "^2.0.0" }, "homepage": "https://butterfly.js.org/", - "author": "Jerry ", + "author": "Jerry ", "license": "Apache-2.0" } diff --git a/scripts/events/cdn.js b/scripts/events/config.js similarity index 91% rename from scripts/events/cdn.js rename to scripts/events/config.js index ea564b2..8d4d074 100644 --- a/scripts/events/cdn.js +++ b/scripts/events/config.js @@ -1,12 +1,18 @@ /** * Butterfly - * CDN + * 1. Merge CDN + * 2. Capitalize the first letter of comment name */ 'use strict' hexo.extend.filter.register('before_generate', () => { const themeConfig = hexo.theme.config + + /** + * Merge CDN + */ + const defaultCDN = { main_css: '/css/index.css', main: '/js/main.js', @@ -24,6 +30,7 @@ hexo.extend.filter.register('before_generate', () => { utterances: 'https://utteranc.es/client.js', twikoo: 'https://cdn.jsdelivr.net/npm/twikoo/dist/twikoo.all.min.js', waline: 'https://cdn.jsdelivr.net/npm/@waline/client/dist/Waline.min.js', + giscus: 'https://giscus.app/client.js', // share addtoany: 'https://static.addtoany.com/menu/page.js', @@ -101,4 +108,18 @@ hexo.extend.filter.register('before_generate', () => { } themeConfig.CDN = Object.assign(defaultCDN, deleteNullValue(themeConfig.CDN)) + + /** + * Capitalize the first letter of comment name + */ + + let { use } = themeConfig.comments + + if (typeof use === 'string') { + use = use.split(',') + } + + const newArray = use.map(item => item.toLowerCase().replace(/^\S/, s => s.toUpperCase())) + + themeConfig.comments.use = newArray }) diff --git a/scripts/tag/timeline.js b/scripts/tag/timeline.js index 1653e6f..43c1333 100644 --- a/scripts/tag/timeline.js +++ b/scripts/tag/timeline.js @@ -22,8 +22,6 @@ function timeLineFn (args, content) { matches.push(match[2]) } - console.log(matches) - for (let i = 0; i < matches.length; i += 2) { const tlChildTitle = hexo.render.renderSync({ text: matches[i], engine: 'markdown' }) const tlChildContent = hexo.render.renderSync({ text: matches[i + 1], engine: 'markdown' }) diff --git a/source/css/_global/function.styl b/source/css/_global/function.styl index 741b35f..771196d 100644 --- a/source/css/_global/function.styl +++ b/source/css/_global/function.styl @@ -117,7 +117,7 @@ if hexo-config('enter_transitions') #site-title, #site-subtitle - animation: titlescale 1s + animation: titleScale 1s #nav.show animation: headerNoOpacity 1s @@ -187,7 +187,7 @@ if hexo-config('avatar.effect') == true margin-top: 0 opacity: 1 -@keyframes titlescale +@keyframes titleScale 0% opacity: 0 transform: scale(.7) diff --git a/source/css/_global/index.styl b/source/css/_global/index.styl index 141ee9c..0d3c415 100644 --- a/source/css/_global/index.styl +++ b/source/css/_global/index.styl @@ -32,6 +32,7 @@ --timeline-bg: $timeline-content-bg --timeline-border-color: $timeline-border-color --pseudo-hover: $pseudo-hover + --headline-presudo: #a0a0a0 body position: relative diff --git a/source/css/_layout/aside.styl b/source/css/_layout/aside.styl index ba6e0ff..0696274 100644 --- a/source/css/_layout/aside.styl +++ b/source/css/_layout/aside.styl @@ -265,16 +265,17 @@ +maxWidth900() max-height: calc(100vh - 140px) - .toc-child - display: none + &:not(.is-expand) + .toc-child + display: none - +maxWidth900() - display: block !important + +maxWidth900() + display: block !important - .toc-item - &.active - .toc-child - display: block + .toc-item + &.active + .toc-child + display: block ol, li diff --git a/source/css/_layout/post.styl b/source/css/_layout/post.styl index a082f33..83c6cec 100644 --- a/source/css/_layout/post.styl +++ b/source/css/_layout/post.styl @@ -7,7 +7,7 @@ beautify() font-size: unit(fontsize, 'px') &:hover - padding-left: unit(fontsize + 12, 'px') + padding-left: unit(fontsize + 18, 'px') h1, h2, @@ -102,6 +102,32 @@ beautify() font-family: Monaco, 'Ubuntu Mono', monospace line-height: 1em + if hexo-config('anchor') + a.headerlink + &:after + @extend .fontawesomeIcon + float: right + color: var(--headline-presudo) + content: '\f0c1' + font-size: .95em + opacity: 0 + transition: all .3s + + &:hover + &:after + color: var(--pseudo-hover) + + h1, + h2, + h3, + h4, + h5, + h6 + &:hover + a.headerlink + &:after + opacity: 1 + ol, ul ol, diff --git a/source/css/_layout/rightside.styl b/source/css/_layout/rightside.styl index d9e338c..bb3406b 100644 --- a/source/css/_layout/rightside.styl +++ b/source/css/_layout/rightside.styl @@ -7,11 +7,7 @@ transition: all .5s #rightside-config-hide - transition: transform .4s - transform: translate(48px, 0) - - &.show - transform: translate(0, 0) !important + display: none & > div & > button, @@ -39,3 +35,19 @@ +maxWidth900() #hide-aside-btn display: none + +@keyframes rightside-item-in + 0% + transform: translate(48px, 0) + + 100% + transform: translate(0, 0) + +@keyframes rightside-item-out + 0% + display: block + transform: translate(0, 0) + + 100% + display: none + transform: translate(48px, 0) diff --git a/source/css/_search/index.styl b/source/css/_search/index.styl index cac6278..2e4e874 100644 --- a/source/css/_search/index.styl +++ b/source/css/_search/index.styl @@ -9,7 +9,6 @@ width: 600px border-radius: 8px background: var(--search-bg) - animation: titlescale .5s +maxWidth768() top: 0 diff --git a/source/js/main.js b/source/js/main.js index e0625b4..e292a12 100644 --- a/source/js/main.js +++ b/source/js/main.js @@ -34,7 +34,7 @@ document.addEventListener('DOMContentLoaded', function () { open: () => { btf.sidebarPaddingR() document.body.style.overflow = 'hidden' - btf.fadeIn(document.getElementById('menu-mask'), 0.5) + btf.animateIn(document.getElementById('menu-mask'), 'to_show 0.5s') document.getElementById('sidebar-menus').classList.add('open') mobileSidebarOpen = true }, @@ -42,7 +42,7 @@ document.addEventListener('DOMContentLoaded', function () { const $body = document.body $body.style.overflow = '' $body.style.paddingRight = '' - btf.fadeOut(document.getElementById('menu-mask'), 0.5) + btf.animateOut(document.getElementById('menu-mask'), 'to_hide 0.5s') document.getElementById('sidebar-menus').classList.remove('open') mobileSidebarOpen = false } @@ -315,82 +315,69 @@ document.addEventListener('DOMContentLoaded', function () { } /** - * toc + * toc,anchor */ - const tocFn = function () { - const $cardTocLayout = document.getElementById('card-toc') - const $cardToc = $cardTocLayout.getElementsByClassName('toc-content')[0] - const $tocLink = $cardToc.querySelectorAll('.toc-link') + const scrollFnToDo = function () { + const isToc = GLOBAL_CONFIG_SITE.isToc + const isAnchor = GLOBAL_CONFIG.isAnchor const $article = document.getElementById('article-container') - const $tocPercentage = $cardTocLayout.querySelector('.toc-percentage') - // main of scroll - window.tocScrollFn = function () { - return btf.throttle(function () { - const currentTop = window.scrollY || document.documentElement.scrollTop - scrollPercent(currentTop) - findHeadPosition(currentTop) - }, 100)() - } - window.addEventListener('scroll', tocScrollFn) + if (!($article && (isToc || isAnchor))) return - const scrollPercent = function (currentTop) { - const docHeight = $article.clientHeight - const winHeight = document.documentElement.clientHeight - const headerHeight = $article.offsetTop - const contentMath = (docHeight > winHeight) ? (docHeight - winHeight) : (document.documentElement.scrollHeight - winHeight) - const scrollPercent = (currentTop - headerHeight) / (contentMath) - const scrollPercentRounded = Math.round(scrollPercent * 100) - const percentage = (scrollPercentRounded > 100) ? 100 : (scrollPercentRounded <= 0) ? 0 : scrollPercentRounded - $tocPercentage.textContent = percentage - } + let $tocLink, $cardToc, scrollPercent, autoScrollToc, isExpand - // anchor - const isAnchor = GLOBAL_CONFIG.isanchor - const updateAnchor = function (anchor) { - if (window.history.replaceState && anchor !== window.location.hash) { - if (!anchor) anchor = location.pathname - const title = GLOBAL_CONFIG_SITE.title - window.history.replaceState({ - url: location.href, - title: title - }, title, anchor) + if (isToc) { + const $cardTocLayout = document.getElementById('card-toc') + $cardToc = $cardTocLayout.getElementsByClassName('toc-content')[0] + $tocLink = $cardToc.querySelectorAll('.toc-link') + const $tocPercentage = $cardTocLayout.querySelector('.toc-percentage') + isExpand = $cardToc.classList.contains('is-expand') + + scrollPercent = currentTop => { + const docHeight = $article.clientHeight + const winHeight = document.documentElement.clientHeight + const headerHeight = $article.offsetTop + const contentMath = (docHeight > winHeight) ? (docHeight - winHeight) : (document.documentElement.scrollHeight - winHeight) + const scrollPercent = (currentTop - headerHeight) / (contentMath) + const scrollPercentRounded = Math.round(scrollPercent * 100) + const percentage = (scrollPercentRounded > 100) ? 100 : (scrollPercentRounded <= 0) ? 0 : scrollPercentRounded + $tocPercentage.textContent = percentage } - } - window.mobileToc = { - open: () => { - $cardTocLayout.style.cssText = 'animation: toc-open .3s; opacity: 1; right: 55px' - }, + window.mobileToc = { + open: () => { + $cardTocLayout.style.cssText = 'animation: toc-open .3s; opacity: 1; right: 55px' + }, - close: () => { - $cardTocLayout.style.animation = 'toc-close .2s' - setTimeout(() => { - $cardTocLayout.style.cssText = "opacity:''; animation: ''; right: ''" - }, 100) + close: () => { + $cardTocLayout.style.animation = 'toc-close .2s' + setTimeout(() => { + $cardTocLayout.style.cssText = "opacity:''; animation: ''; right: ''" + }, 100) + } } - } - // toc元素點擊 - $cardToc.addEventListener('click', (e) => { - e.preventDefault() - const $target = e.target.classList.contains('toc-link') - ? e.target - : e.target.parentElement - btf.scrollToDest(btf.getEleTop(document.getElementById(decodeURI($target.getAttribute('href')).replace('#', ''))), 300) - if (window.innerWidth < 900) { - window.mobileToc.close() - } - }) + // toc元素點擊 + $cardToc.addEventListener('click', e => { + e.preventDefault() + const $target = e.target.classList.contains('toc-link') + ? e.target + : e.target.parentElement + btf.scrollToDest(btf.getEleTop(document.getElementById(decodeURI($target.getAttribute('href')).replace('#', ''))), 300) + if (window.innerWidth < 900) { + window.mobileToc.close() + } + }) - const autoScrollToc = function (item) { - const activePosition = item.getBoundingClientRect().top - const sidebarScrollTop = $cardToc.scrollTop - if (activePosition > (document.documentElement.clientHeight - 100)) { - $cardToc.scrollTop = sidebarScrollTop + 150 - } - if (activePosition < 100) { - $cardToc.scrollTop = sidebarScrollTop - 150 + autoScrollToc = item => { + const activePosition = item.getBoundingClientRect().top + const sidebarScrollTop = $cardToc.scrollTop + if (activePosition > (document.documentElement.clientHeight - 100)) { + $cardToc.scrollTop = sidebarScrollTop + 150 + } + if (activePosition < 100) { + $cardToc.scrollTop = sidebarScrollTop - 150 + } } } @@ -398,7 +385,7 @@ document.addEventListener('DOMContentLoaded', function () { const list = $article.querySelectorAll('h1,h2,h3,h4,h5,h6') let detectItem = '' const findHeadPosition = function (top) { - if ($tocLink.length === 0 || top === 0) { + if (top === 0) { return false } @@ -407,37 +394,50 @@ document.addEventListener('DOMContentLoaded', function () { list.forEach(function (ele, index) { if (top > btf.getEleTop(ele) - 80) { - currentId = '#' + encodeURI(ele.getAttribute('id')) + const id = ele.id + currentId = id ? '#' + encodeURI(id) : '' currentIndex = index } }) if (detectItem === currentIndex) return - if (isAnchor) updateAnchor(currentId) - - if (currentId === '') { - $cardToc.querySelectorAll('.active').forEach(i => { i.classList.remove('active') }) - detectItem = currentIndex - return - } + if (isAnchor) btf.updateAnchor(currentId) detectItem = currentIndex - $cardToc.querySelectorAll('.active').forEach(item => { item.classList.remove('active') }) - const currentActive = $tocLink[currentIndex] - currentActive.classList.add('active') + if (isToc) { + $cardToc.querySelectorAll('.active').forEach(i => { i.classList.remove('active') }) - setTimeout(() => { - autoScrollToc(currentActive) - }, 0) + if (currentId === '') { + return + } - let parent = currentActive.parentNode + const currentActive = $tocLink[currentIndex] + currentActive.classList.add('active') - for (; !parent.matches('.toc'); parent = parent.parentNode) { - if (parent.matches('li')) parent.classList.add('active') + setTimeout(() => { + autoScrollToc(currentActive) + }, 0) + + if (isExpand) return + let parent = currentActive.parentNode + + for (; !parent.matches('.toc'); parent = parent.parentNode) { + if (parent.matches('li')) parent.classList.add('active') + } } } + + // main of scroll + window.tocScrollFn = function () { + return btf.throttle(function () { + const currentTop = window.scrollY || document.documentElement.scrollTop + isToc && scrollPercent(currentTop) + findHeadPosition(currentTop) + }, 100)() + } + window.addEventListener('scroll', tocScrollFn) } /** @@ -473,12 +473,20 @@ document.addEventListener('DOMContentLoaded', function () { } // handle some cases typeof utterancesTheme === 'function' && utterancesTheme() + typeof changeGiscusTheme === 'function' && changeGiscusTheme() typeof FB === 'object' && window.loadFBComment() window.DISQUS && document.getElementById('disqus_thread').children.length && setTimeout(() => window.disqusReset(), 200) typeof runMermaid === 'function' && window.runMermaid() }, showOrHideBtn: () => { // rightside 點擊設置 按鈕 展開 - document.getElementById('rightside-config-hide').classList.toggle('show') + const target = document.getElementById('rightside-config-hide') + if (window.rightSideIn) { + window.rightSideIn = false + btf.animateOut(target, 'rightside-item-out 0.5s') + } else { + window.rightSideIn = true + btf.animateIn(target, 'rightside-item-in 0.5s') + } }, scrollToTop: () => { // Back to top btf.scrollToDest(0, 500) @@ -749,7 +757,7 @@ document.addEventListener('DOMContentLoaded', function () { toggleCardCategory() } - GLOBAL_CONFIG_SITE.isToc && tocFn() + scrollFnToDo() GLOBAL_CONFIG_SITE.isHome && scrollDownInIndex() addHighlightTool() GLOBAL_CONFIG.isPhotoFigcaption && addPhotoFigcaption() diff --git a/source/js/search/algolia.js b/source/js/search/algolia.js index 83353a7..13373e9 100644 --- a/source/js/search/algolia.js +++ b/source/js/search/algolia.js @@ -3,9 +3,10 @@ window.addEventListener('load', () => { const bodyStyle = document.body.style bodyStyle.width = '100%' bodyStyle.overflow = 'hidden' - document.querySelector('#algolia-search .search-dialog').style.display = 'block' - document.querySelector('#algolia-search .ais-SearchBox-input').focus() - btf.fadeIn(document.getElementById('search-mask'), 0.5) + btf.animateIn(document.getElementById('search-mask'), 'to_show 0.5s') + btf.animateIn(document.querySelector('#algolia-search .search-dialog'), 'titleScale 0.5s') + setTimeout(() => { document.querySelector('#algolia-search .ais-SearchBox-input').focus() }, 100) + // shortcut: ESC document.addEventListener('keydown', function f (event) { if (event.code === 'Escape') { @@ -19,10 +20,8 @@ window.addEventListener('load', () => { const bodyStyle = document.body.style bodyStyle.width = '' bodyStyle.overflow = '' - const $searchDialog = document.querySelector('#algolia-search .search-dialog') - $searchDialog.style.animation = 'search_close .5s' - setTimeout(() => { $searchDialog.style.cssText = "display: none; animation: ''" }, 500) - btf.fadeOut(document.getElementById('search-mask'), 0.5) + btf.animateOut(document.querySelector('#algolia-search .search-dialog'), 'search_close .5s') + btf.animateOut(document.getElementById('search-mask'), 'to_hide 0.5s') } const searchClickFn = () => { diff --git a/source/js/search/local-search.js b/source/js/search/local-search.js index d1df43d..de2db62 100644 --- a/source/js/search/local-search.js +++ b/source/js/search/local-search.js @@ -1,10 +1,12 @@ window.addEventListener('load', () => { let loadFlag = false - const openSearch = function () { - document.body.style.cssText = 'width: 100%;overflow: hidden' - document.querySelector('#local-search .search-dialog').style.display = 'block' - document.querySelector('#local-search-input input').focus() - btf.fadeIn(document.getElementById('search-mask'), 0.5) + const openSearch = () => { + const bodyStyle = document.body.style + bodyStyle.width = '100%' + bodyStyle.overflow = 'hidden' + btf.animateIn(document.getElementById('search-mask'), 'to_show 0.5s') + btf.animateIn(document.querySelector('#local-search .search-dialog'), 'titleScale 0.5s') + setTimeout(() => { document.querySelector('#local-search-input input').focus() }, 100) if (!loadFlag) { search(GLOBAL_CONFIG.localSearch.path) loadFlag = true @@ -18,12 +20,12 @@ window.addEventListener('load', () => { }) } - const closeSearch = function () { - document.body.style.cssText = "width: '';overflow: ''" - const $searchDialog = document.querySelector('#local-search .search-dialog') - $searchDialog.style.animation = 'search_close .5s' - setTimeout(() => { $searchDialog.style.cssText = "display: none; animation: ''" }, 500) - btf.fadeOut(document.getElementById('search-mask'), 0.5) + const closeSearch = () => { + const bodyStyle = document.body.style + bodyStyle.width = '' + bodyStyle.overflow = '' + btf.animateOut(document.querySelector('#local-search .search-dialog'), 'search_close .5s') + btf.animateOut(document.getElementById('search-mask'), 'to_hide 0.5s') } // click function diff --git a/source/js/utils.js b/source/js/utils.js index 5b38b5f..b3e5df0 100644 --- a/source/js/utils.js +++ b/source/js/utils.js @@ -58,16 +58,14 @@ const btf = { } }, - snackbarShow: (text, showAction, duration) => { - const sa = (typeof showAction !== 'undefined') ? showAction : false - const dur = (typeof duration !== 'undefined') ? duration : 2000 - const position = GLOBAL_CONFIG.Snackbar.position - const bg = document.documentElement.getAttribute('data-theme') === 'light' ? GLOBAL_CONFIG.Snackbar.bgLight : GLOBAL_CONFIG.Snackbar.bgDark + snackbarShow: (text, showAction = false, duration = 2000) => { + const { position, bgLight, bgDark } = GLOBAL_CONFIG.Snackbar + const bg = document.documentElement.getAttribute('data-theme') === 'light' ? bgLight : bgDark Snackbar.show({ text: text, backgroundColor: bg, - showAction: sa, - duration: dur, + showAction: showAction, + duration: duration, pos: position }) }, @@ -151,16 +149,18 @@ const btf = { }) }, - fadeIn: (ele, time) => { - ele.style.cssText = `display:block;animation: to_show ${time}s` + animateIn: (ele, text) => { + ele.style.display = 'block' + ele.style.animation = text }, - fadeOut: (ele, time) => { + animateOut: (ele, text) => { ele.addEventListener('animationend', function f () { - ele.style.cssText = "display: none; animation: '' " + ele.style.display = '' + ele.style.animation = '' ele.removeEventListener('animationend', f) }) - ele.style.animation = `to_hide ${time}s` + ele.style.animation = text }, getParents: (elem, selector) => { @@ -180,7 +180,6 @@ const btf = { }, /** - * * @param {*} selector * @param {*} eleType the type of create element * @param {*} options object key: value @@ -263,5 +262,16 @@ const btf = { }) } }) + }, + + updateAnchor: (anchor) => { + if (anchor !== window.location.hash) { + if (!anchor) anchor = location.pathname + const title = GLOBAL_CONFIG_SITE.title + window.history.replaceState({ + url: location.href, + title: title + }, title, anchor) + } } }