From 5ace2d1e40ecadf7cbc2d5a2cf53107816bdd909 Mon Sep 17 00:00:00 2001 From: anime Date: Mon, 30 Dec 2024 16:11:32 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat(=E5=88=9D=E6=AD=A5=E5=AE=9E=E7=8E=B0sp?= =?UTF-8?q?oiler=E6=8F=92=E4=BB=B6):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blog.config.js | 2 ++ components/NotionPage.js | 17 +++++++++- public/css/spoiler-text.css | 23 +++++++++++++ public/js/spoilerText.js | 66 +++++++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 public/css/spoiler-text.css create mode 100644 public/js/spoilerText.js diff --git a/blog.config.js b/blog.config.js index 3b827a95..21a3efac 100644 --- a/blog.config.js +++ b/blog.config.js @@ -298,6 +298,8 @@ const BLOG = { WIDGET_PET_SWITCH_THEME: process.env.NEXT_PUBLIC_WIDGET_PET_SWITCH_THEME || true, // 点击宠物挂件切换博客主题 + SPOILER_TEXT_TAG: process.env.NEXT_PUBLIC_SPOILER_TEXT_TAG || '', // 默认的隐藏标签 + // 音乐播放插件 MUSIC_PLAYER: process.env.NEXT_PUBLIC_MUSIC_PLAYER || false, // 是否使用音乐播放插件 MUSIC_PLAYER_VISIBLE: process.env.NEXT_PUBLIC_MUSIC_PLAYER_VISIBLE || true, // 是否在左下角显示播放和切换,如果使用播放器,打开自动播放再隐藏,就会以类似背景音乐的方式播放,无法取消和暂停 diff --git a/components/NotionPage.js b/components/NotionPage.js index a3f1a085..d7585fb1 100644 --- a/components/NotionPage.js +++ b/components/NotionPage.js @@ -1,6 +1,6 @@ import { siteConfig } from '@/lib/config' import { compressImage, mapImgUrl } from '@/lib/notion/mapImage' -import { isBrowser } from '@/lib/utils' +import { isBrowser, loadExternalResource } from '@/lib/utils' import mediumZoom from '@fisch0920/medium-zoom' import 'katex/dist/katex.min.css' import dynamic from 'next/dynamic' @@ -17,6 +17,7 @@ const NotionPage = ({ post, className }) => { // 是否关闭数据库和画册的点击跳转 const POST_DISABLE_GALLERY_CLICK = siteConfig('POST_DISABLE_GALLERY_CLICK') const POST_DISABLE_DATABASE_CLICK = siteConfig('POST_DISABLE_DATABASE_CLICK') + const SPOILER_TEXT_TAG = siteConfig('SPOILER_TEXT_TAG') const zoom = isBrowser && @@ -84,6 +85,20 @@ const NotionPage = ({ post, className }) => { } }, [post]) + useEffect(() => { + if (SPOILER_TEXT_TAG) { + import('lodash/escapeRegExp').then(escapeRegExp => { + Promise.all([ + loadExternalResource('/js/spoilerText.js', 'js'), + loadExternalResource('/css/spoiler-text.css', 'css') + ]).then(() => { + window.textToSpoiler && + window.textToSpoiler(escapeRegExp.default(SPOILER_TEXT_TAG)) + }) + }) + } + }, [post]) + return (
lastIndex) { + fragments.push( + document.createTextNode(wholeText.slice(lastIndex, match.index)) + ) + } + + // 创建 span 包裹的内容 + const span = document.createElement('span') + span.textContent = match[1] // 提取匹配的内容 + if (className) { + span.className = className + } + fragments.push(span) + // 设置lastIndex + lastIndex = regex.lastIndex + } + if (fragments.length) { + // 添加剩余未匹配的部分 + if (lastIndex < wholeText.length) { + fragments.push(document.createTextNode(wholeText.slice(lastIndex))) + } + + // 替换原节点 + fragments.forEach(fragment => { + node.parentNode.appendChild(fragment) + }) + node.remove() + } +} + +function processTextNodes(root, className, spoilerTag) { + const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null) + const waitProcessNodes = [] + while (walker.nextNode()) { + const node = walker.currentNode + waitProcessNodes.push(node) + } + for (const waitProcessNode of waitProcessNodes) { + convertTextToSpoilerSpan(waitProcessNode, className, spoilerTag) + } +} + +function textToSpoiler(spoilerTag) { + const intervalID = setInterval(() => { + const articleElement = document.querySelector( + '#article-wrapper #notion-article main' + ) + if (articleElement) { + setTimeout(() => { + processTextNodes(articleElement, 'spoiler-text', spoilerTag) + clearInterval(intervalID) + }, 300) + } + }, 1000) +} + +window.textToSpoiler = textToSpoiler From 06c41ae8f0cccbe8cfe951b3ba3992c69e69ec8c Mon Sep 17 00:00:00 2001 From: anime Date: Mon, 30 Dec 2024 16:15:57 +0800 Subject: [PATCH 2/5] =?UTF-8?q?feat(=E4=BF=AE=E5=A4=8D=E6=9F=90=E4=BA=9B?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E4=B8=8B=E6=97=A0=E6=B3=95=E6=AD=A3=E5=B8=B8?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E7=9A=84=E9=97=AE=E9=A2=98):=20=E8=BF=98?= =?UTF-8?q?=E6=98=AF=E9=80=89=E6=8B=A9span=E5=AE=8C=E5=85=A8=E5=8C=85?= =?UTF-8?q?=E8=A3=B9=EF=BC=8C=E7=AE=80=E5=8C=96=E5=A4=84=E7=90=86=E9=9A=BE?= =?UTF-8?q?=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/js/spoilerText.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/public/js/spoilerText.js b/public/js/spoilerText.js index 3276636a..7a775034 100644 --- a/public/js/spoilerText.js +++ b/public/js/spoilerText.js @@ -1,6 +1,7 @@ function convertTextToSpoilerSpan(node, className, spoilerTag) { const regex = new RegExp(`${spoilerTag}(.*?)${spoilerTag}`, 'g') const wholeText = node.wholeText + let outerSpan = document.createElement('span') const fragments = [] let lastIndex = 0 let match @@ -8,7 +9,7 @@ function convertTextToSpoilerSpan(node, className, spoilerTag) { console.log('符合要求的文字' + wholeText) // 添加前面未匹配的部分 if (match.index > lastIndex) { - fragments.push( + outerSpan.appendChild( document.createTextNode(wholeText.slice(lastIndex, match.index)) ) } @@ -19,21 +20,16 @@ function convertTextToSpoilerSpan(node, className, spoilerTag) { if (className) { span.className = className } - fragments.push(span) + outerSpan.appendChild(span) // 设置lastIndex lastIndex = regex.lastIndex } - if (fragments.length) { + if (outerSpan.childNodes.length) { // 添加剩余未匹配的部分 if (lastIndex < wholeText.length) { - fragments.push(document.createTextNode(wholeText.slice(lastIndex))) + outerSpan.appendChild(document.createTextNode(wholeText.slice(lastIndex))) } - - // 替换原节点 - fragments.forEach(fragment => { - node.parentNode.appendChild(fragment) - }) - node.remove() + node.replaceWith(outerSpan) } } From 177c4508853f3b1b7bbb75200c8943cf287d98e6 Mon Sep 17 00:00:00 2001 From: anime Date: Mon, 30 Dec 2024 16:25:42 +0800 Subject: [PATCH 3/5] =?UTF-8?q?feat(spoiler=20text=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=96=87=E6=A1=A3):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/js/spoilerText.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/public/js/spoilerText.js b/public/js/spoilerText.js index 7a775034..afaf3a0a 100644 --- a/public/js/spoilerText.js +++ b/public/js/spoilerText.js @@ -1,3 +1,9 @@ +/** + * 将Node文本中的指定标签内容转换为带有指定类名的span + * @param node + * @param className + * @param spoilerTag + */ function convertTextToSpoilerSpan(node, className, spoilerTag) { const regex = new RegExp(`${spoilerTag}(.*?)${spoilerTag}`, 'g') const wholeText = node.wholeText @@ -33,6 +39,12 @@ function convertTextToSpoilerSpan(node, className, spoilerTag) { } } +/** + * 收集并处理指定节点下的所有文本节点 + * @param root + * @param className + * @param spoilerTag + */ function processTextNodes(root, className, spoilerTag) { const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null) const waitProcessNodes = [] @@ -45,6 +57,10 @@ function processTextNodes(root, className, spoilerTag) { } } +/** + * 定位到目标处理位置,开始进行文本到spoiler的转换 + * @param spoilerTag + */ function textToSpoiler(spoilerTag) { const intervalID = setInterval(() => { const articleElement = document.querySelector( From 65097c64b24f8bc083ed2e6771414f4912af3602 Mon Sep 17 00:00:00 2001 From: anime Date: Mon, 30 Dec 2024 16:28:15 +0800 Subject: [PATCH 4/5] =?UTF-8?q?perf(=E4=BC=98=E5=8C=96spoiler=20text?= =?UTF-8?q?=E5=A4=84=E7=90=86=E8=BF=87=E7=A8=8B):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/js/spoilerText.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/public/js/spoilerText.js b/public/js/spoilerText.js index afaf3a0a..c0b12895 100644 --- a/public/js/spoilerText.js +++ b/public/js/spoilerText.js @@ -1,11 +1,10 @@ /** * 将Node文本中的指定标签内容转换为带有指定类名的span + * @param regex * @param node * @param className - * @param spoilerTag */ -function convertTextToSpoilerSpan(node, className, spoilerTag) { - const regex = new RegExp(`${spoilerTag}(.*?)${spoilerTag}`, 'g') +function convertTextToSpoilerSpan(regex, node, className) { const wholeText = node.wholeText let outerSpan = document.createElement('span') const fragments = [] @@ -46,14 +45,21 @@ function convertTextToSpoilerSpan(node, className, spoilerTag) { * @param spoilerTag */ function processTextNodes(root, className, spoilerTag) { - const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null) + const regex = new RegExp(`${spoilerTag}(.*?)${spoilerTag}`, 'g') + const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { + acceptNode: function (node) { + return regex.test(node.wholeText) + ? NodeFilter.FILTER_ACCEPT + : NodeFilter.FILTER_REJECT + } + }) const waitProcessNodes = [] while (walker.nextNode()) { const node = walker.currentNode waitProcessNodes.push(node) } for (const waitProcessNode of waitProcessNodes) { - convertTextToSpoilerSpan(waitProcessNode, className, spoilerTag) + convertTextToSpoilerSpan(regex, waitProcessNode, className) } } From adccf622fdbc6d704d3bd928a10d56c0ac0e209a Mon Sep 17 00:00:00 2001 From: anime Date: Mon, 30 Dec 2024 19:43:18 +0800 Subject: [PATCH 5/5] =?UTF-8?q?feat(spoiler=20text=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=96=87=E6=A1=A3):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blog.config.js | 2 +- components/NotionPage.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/blog.config.js b/blog.config.js index 21a3efac..6d575870 100644 --- a/blog.config.js +++ b/blog.config.js @@ -298,7 +298,7 @@ const BLOG = { WIDGET_PET_SWITCH_THEME: process.env.NEXT_PUBLIC_WIDGET_PET_SWITCH_THEME || true, // 点击宠物挂件切换博客主题 - SPOILER_TEXT_TAG: process.env.NEXT_PUBLIC_SPOILER_TEXT_TAG || '', // 默认的隐藏标签 + SPOILER_TEXT_TAG: process.env.NEXT_PUBLIC_SPOILER_TEXT_TAG || '', // Spoiler文本隐藏功能,如Notion中 [sp]希望被spoiler的文字[sp],填入[sp] 即可 // 音乐播放插件 MUSIC_PLAYER: process.env.NEXT_PUBLIC_MUSIC_PLAYER || false, // 是否使用音乐播放插件 diff --git a/components/NotionPage.js b/components/NotionPage.js index d7585fb1..4baf099c 100644 --- a/components/NotionPage.js +++ b/components/NotionPage.js @@ -86,6 +86,7 @@ const NotionPage = ({ post, className }) => { }, [post]) useEffect(() => { + // Spoiler文本功能 if (SPOILER_TEXT_TAG) { import('lodash/escapeRegExp').then(escapeRegExp => { Promise.all([