From 5eb390bc38ee9748d3da5ed2f9659f354596bfe9 Mon Sep 17 00:00:00 2001 From: anime Date: Mon, 23 Dec 2024 03:01:35 +0800 Subject: [PATCH 1/8] =?UTF-8?q?feat(=E5=8E=9F=E7=94=9F=E6=94=AF=E6=8C=81AI?= =?UTF-8?q?=E6=91=98=E8=A6=81=E5=8A=9F=E8=83=BD):=20=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E4=B8=80=E5=B1=82API=E4=BD=9C=E4=B8=BA=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E8=80=8C=E9=9D=9E=E7=9B=B4=E6=8E=A5=E8=AF=B7=E6=B1=82AI?= =?UTF-8?q?=EF=BC=8C=E5=8F=AF=E4=BB=A5=E5=AE=9E=E7=8E=B0=E7=BC=93=E5=AD=98?= =?UTF-8?q?/=E5=90=8E=E7=AB=AF=E4=BF=9D=E5=AF=86=E5=92=8C=E9=A2=84?= =?UTF-8?q?=E6=B8=B2=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 611a7d1d5dc7bc200d4390e29217ab8c8f76b0f0) --- blog.config.js | 10 ++++ components/AISummary.js | 94 +++++++++++++++++++++++++++++++++ components/AISummary.module.css | 53 +++++++++++++++++++ lib/plugins/aiSummary.js | 31 +++++++++++ pages/[prefix]/[slug]/index.js | 22 ++++++++ themes/heo/index.js | 2 + 6 files changed, 212 insertions(+) create mode 100644 components/AISummary.js create mode 100644 components/AISummary.module.css create mode 100644 lib/plugins/aiSummary.js diff --git a/blog.config.js b/blog.config.js index 3b827a95..161f7715 100644 --- a/blog.config.js +++ b/blog.config.js @@ -270,6 +270,16 @@ const BLOG = { // 星空雨特效 黑夜模式才会生效 STARRY_SKY: process.env.NEXT_PUBLIC_STARRY_SKY || false, // 开关 + // AI 文章摘要生成 + AI_SUMMARY_API: + process.env.AI_SUMMARY_API|| + '', + AI_SUMMARY_KEY: + process.env.AI_SUMMARY_KEY || + '', + AI_SUMMARY_WORD_LIMIT: process.env.AI_SUMMARY_WORD_LIMIT || 1000, + + // ********挂件组件相关******** // AI 文章摘要生成 @see https://docs_s.tianli0.top/ TianliGPT_CSS: diff --git a/components/AISummary.js b/components/AISummary.js new file mode 100644 index 00000000..055b4fc5 --- /dev/null +++ b/components/AISummary.js @@ -0,0 +1,94 @@ +import styles from './AISummary.module.css' +import { useEffect, useState } from 'react' + +const AISummary = ({ aiSummary }) => { + const [summary, setSummary] = useState('生成中...') + + useEffect(() => { + showAiSummaryAnimation(aiSummary, setSummary) + }, []) + + return ( +
+
+
+
+ + + +
+
AI智能摘要
+
GPT
+
+
+
+ {summary} + {summary !== aiSummary && ( + + )} +
+
+
+
+ ) +} + +const showAiSummaryAnimation = (rawSummary, setSummary) => { + if (!rawSummary) return + let currentIndex = 0 + const typingDelay = 20 + const punctuationDelayMultiplier = 6 + let animationRunning = true + let lastUpdateTime = performance.now() + const animate = () => { + if (currentIndex < rawSummary.length && animationRunning) { + const currentTime = performance.now() + const timeDiff = currentTime - lastUpdateTime + + const letter = rawSummary.slice(currentIndex, currentIndex + 1) + const isPunctuation = /[,。!、?,.!?]/.test(letter) + const delay = isPunctuation + ? typingDelay * punctuationDelayMultiplier + : typingDelay + + if (timeDiff >= delay) { + setSummary(rawSummary.slice(0, currentIndex + 1)) + lastUpdateTime = currentTime + currentIndex++ + + if (currentIndex < rawSummary.length) { + setSummary(rawSummary.slice(0, currentIndex)) + } else { + setSummary(rawSummary) + observer.disconnect() + } + } + requestAnimationFrame(animate) + } + } + animate(rawSummary) + const observer = new IntersectionObserver( + entries => { + animationRunning = entries[0].isIntersecting + if (animationRunning && currentIndex === 0) { + setTimeout(() => { + requestAnimationFrame(animate) + }, 200) + } + }, + { threshold: 0 } + ) + let post_ai = document.querySelector('.post-ai') + if (post_ai) { + observer.observe(post_ai) + } +} + +export default AISummary diff --git a/components/AISummary.module.css b/components/AISummary.module.css new file mode 100644 index 00000000..b7e89f2a --- /dev/null +++ b/components/AISummary.module.css @@ -0,0 +1,53 @@ +.post-ai { + font-family: 'Noto Sans SC', sans-serif; + margin-bottom: 20px; +} +.ai-container { + background: linear-gradient(135deg, #f9f9f9 0%, #f5f5f5 100%); + border: 1px solid #e8e8e8; + border-radius: 10px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + overflow: hidden; +} +.ai-header { + background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%); + color: white; + padding: 12px 20px; + display: flex; + align-items: center; +} +.ai-icon { + margin-right: 10px; +} +.ai-title { + font-size: 18px; + font-weight: bold; + flex-grow: 1; +} +.ai-tag { + background-color: rgba(255, 255, 255, 0.2); + padding: 3px 8px; + border-radius: 12px; + font-size: 12px; +} +.ai-content { + padding: 20px; +} +.ai-explanation { + font-size: 16px; + line-height: 1.6; + color: #333; +} +.blinking-cursor { + display: inline-block; + width: 2px; + height: 20px; + background-color: #333; + animation: blink 0.7s infinite; + margin-left: 5px; +} +@keyframes blink { + 0% { opacity: 0; } + 50% { opacity: 1; } + 100% { opacity: 0; } +} diff --git a/lib/plugins/aiSummary.js b/lib/plugins/aiSummary.js new file mode 100644 index 00000000..5b9c1d4e --- /dev/null +++ b/lib/plugins/aiSummary.js @@ -0,0 +1,31 @@ +/** + * get Ai summary + * @returns {Promise} + * @param aiSummaryAPI + * @param aiSummaryKey + * @param truncatedText + */ +export async function getAiSummary(aiSummaryAPI, aiSummaryKey, truncatedText) { + try { + const response = await fetch(aiSummaryAPI, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + token: aiSummaryKey, + content: truncatedText + }) + }) + + if (response.ok) { + const data = await response.json() + return data.summary + } else { + throw new Error('Response not ok') + } + } catch (error) { + console.error('ChucklePostAI:请求失败', error) + return '获取文章摘要失败,请稍后再试。' + } +} diff --git a/pages/[prefix]/[slug]/index.js b/pages/[prefix]/[slug]/index.js index cdf99afc..6d30e227 100644 --- a/pages/[prefix]/[slug]/index.js +++ b/pages/[prefix]/[slug]/index.js @@ -6,6 +6,8 @@ import { uploadDataToAlgolia } from '@/lib/plugins/algolia' import { checkSlugHasOneSlash, getRecommendPost } from '@/lib/utils/post' import { idToUuid } from 'notion-utils' import Slug from '..' +import { getPageContentText } from '@/pages/search/[keyword]' +import { getAiSummary } from '@/lib/plugins/aiSummary' /** * 根据notion的slug访问页面 @@ -94,6 +96,26 @@ export async function getStaticProps({ params: { prefix, slug }, locale }) { key => props.post.blockMap.block[key]?.value?.parent_id === props.post.id ) props.post.toc = getPageTableOfContents(props.post, props.post.blockMap) + + const aiSummaryAPI = siteConfig('AI_SUMMARY_API') + if (aiSummaryAPI) { + const aiSummaryKey = siteConfig('AI_SUMMARY_KEY') + const wordLimit = siteConfig('AI_SUMMARY_WORD_LIMIT', '1000') + const post = props.post + let content = '' + for (let heading of post.toc) { + content += heading.text + ' ' + } + content += getPageContentText(post, post.blockMap) + const combinedText = post.title + ' ' + content + const truncatedText = combinedText.slice(0, wordLimit) + + props.post.aiSummary = await getAiSummary( + aiSummaryAPI, + aiSummaryKey, + truncatedText + ) + } } // 生成全文索引 && JSON.parse(BLOG.ALGOLIA_RECREATE_DATA) diff --git a/themes/heo/index.js b/themes/heo/index.js index 93a7dabb..6fa85552 100644 --- a/themes/heo/index.js +++ b/themes/heo/index.js @@ -42,6 +42,7 @@ import SearchNav from './components/SearchNav' import SideRight from './components/SideRight' import CONFIG from './config' import { Style } from './style' +import AISummary from '@/components/AISummary' /** * 基础布局 采用上中下布局,移动端使用顶部侧边导航栏 @@ -306,6 +307,7 @@ const LayoutSlug = props => {
+ {post && } From 46527958d0a3ab68cc2c9b53b7119e0cfedb4320 Mon Sep 17 00:00:00 2001 From: anime Date: Mon, 23 Dec 2024 03:23:47 +0800 Subject: [PATCH 2/8] =?UTF-8?q?feat(AI=E6=91=98=E8=A6=81=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=A4=9A=E8=AF=AD=E8=A8=80):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit fcf0ca4ebb241321b3210a0465f9913f7e48bb5b) --- components/AISummary.js | 6 ++++-- lib/lang/en-US.js | 4 ++++ lib/lang/fr-FR.js | 9 +++++++-- lib/lang/ja-JP.js | 4 ++++ lib/lang/tr-TR.js | 4 ++++ lib/lang/zh-CN.js | 4 ++++ lib/lang/zh-HK.js | 4 ++++ lib/lang/zh-TW.js | 4 ++++ 8 files changed, 35 insertions(+), 4 deletions(-) diff --git a/components/AISummary.js b/components/AISummary.js index 055b4fc5..058d4214 100644 --- a/components/AISummary.js +++ b/components/AISummary.js @@ -1,8 +1,10 @@ import styles from './AISummary.module.css' import { useEffect, useState } from 'react' +import { useGlobal } from '@/lib/global' const AISummary = ({ aiSummary }) => { - const [summary, setSummary] = useState('生成中...') + const { locale } = useGlobal() + const [summary, setSummary] = useState(locale.AI_SUMMARY.GENERATING + '...') useEffect(() => { showAiSummaryAnimation(aiSummary, setSummary) @@ -24,7 +26,7 @@ const AISummary = ({ aiSummary }) => { /> -
AI智能摘要
+
{locale.AI_SUMMARY.NAME}
GPT
diff --git a/lib/lang/en-US.js b/lib/lang/en-US.js index 17be1575..af5ec370 100644 --- a/lib/lang/en-US.js +++ b/lib/lang/en-US.js @@ -87,5 +87,9 @@ export default { SUBSCRIBE: 'Subscribe', MSG: 'Get the latest news and articles to your inbox every month.', EMAIL: 'Email' + }, + AI_SUMMARY: { + NAME: 'AI intelligent summary', + GENERATING: 'Generating' } } diff --git a/lib/lang/fr-FR.js b/lib/lang/fr-FR.js index 2bc40057..efc775c4 100644 --- a/lib/lang/fr-FR.js +++ b/lib/lang/fr-FR.js @@ -21,7 +21,7 @@ export default { URL_COPIED: "L'URL est copé!", TABLE_OF_CONTENTS: 'Sommaire', RELATE_POSTS: 'Article similaire', - COPYRIGHT: 'Droit d\'auteur', + COPYRIGHT: "Droit d'auteur", AUTHOR: 'Auteur', URL: 'Link', ANALYTICS: 'Analytique', @@ -29,7 +29,8 @@ export default { ARTICLE: 'Article(s)', VISITORS: 'Visiteurs', VIEWS: 'Views', - COPYRIGHT_NOTICE: 'Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International (CC BY-NC-SA 4.0)', + COPYRIGHT_NOTICE: + 'Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International (CC BY-NC-SA 4.0)', RESULT_OF_SEARCH: 'Résultats', ARTICLE_DETAIL: 'Plus de détails', PASSWORD_ERROR: 'Mot de passe est incorrect!', @@ -50,5 +51,9 @@ export default { POST: { BACK: 'Page precedente', TOP: 'Haut' + }, + AI_SUMMARY: { + NAME: "Résumé intelligent par l'IA", + GENERATING: 'Génération en cours' } } diff --git a/lib/lang/ja-JP.js b/lib/lang/ja-JP.js index 628b28b3..cdb57e23 100644 --- a/lib/lang/ja-JP.js +++ b/lib/lang/ja-JP.js @@ -58,5 +58,9 @@ export default { POST: { BACK: '前のページに戻る', TOP: '上に戻る' + }, + AI_SUMMARY: { + NAME: 'AIインテリジェントサマリー', + GENERATING: '生成中' } } diff --git a/lib/lang/tr-TR.js b/lib/lang/tr-TR.js index 5be7a520..69bcbf6a 100644 --- a/lib/lang/tr-TR.js +++ b/lib/lang/tr-TR.js @@ -53,5 +53,9 @@ export default { POST: { BACK: 'Geri', TOP: 'Yukarı' + }, + AI_SUMMARY: { + NAME: 'Yapay Zeka Akıllı Özet', + GENERATING: 'Oluşturuluyor' } } diff --git a/lib/lang/zh-CN.js b/lib/lang/zh-CN.js index f17e0f0f..82477e9f 100644 --- a/lib/lang/zh-CN.js +++ b/lib/lang/zh-CN.js @@ -87,5 +87,9 @@ export default { SUBSCRIBE: '邮件订阅', MSG: '订阅以获取每月更新的新闻和文章,直接发送至您的邮箱。', EMAIL: '邮箱' + }, + AI_SUMMARY: { + NAME: 'AI智能摘要', + GENERATING: '生成中' } } diff --git a/lib/lang/zh-HK.js b/lib/lang/zh-HK.js index e4de0f4e..e72c7a8c 100644 --- a/lib/lang/zh-HK.js +++ b/lib/lang/zh-HK.js @@ -52,5 +52,9 @@ export default { POST: { BACK: '返回', TOP: '回到頂端' + }, + AI_SUMMARY: { + NAME: 'AI智能摘要', + GENERATING: '生成中' } } diff --git a/lib/lang/zh-TW.js b/lib/lang/zh-TW.js index 02c3ed2f..550aee95 100644 --- a/lib/lang/zh-TW.js +++ b/lib/lang/zh-TW.js @@ -52,5 +52,9 @@ export default { POST: { BACK: '返回', TOP: '回到頂端' + }, + AI_SUMMARY: { + NAME: 'AI智能摘要', + GENERATING: '生成中' } } From 148545cea952b948508d005a95e8aebdc01c0b7a Mon Sep 17 00:00:00 2001 From: anime Date: Mon, 23 Dec 2024 17:39:48 +0800 Subject: [PATCH 3/8] =?UTF-8?q?feat(=E5=8E=9F=E7=94=9FAI=E6=91=98=E8=A6=81?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E6=94=AF=E6=8C=81=E7=BC=93=E5=AD=98):=20?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E7=BC=93=E5=AD=98=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=8F=AA=E6=94=AF=E6=8C=81memory=EF=BC=8C=E4=B8=8D=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=96=87=E4=BB=B6=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit dd56dae44f7f555d9004e7d1f872085bded2cb86) --- blog.config.js | 1 + lib/cache/cache_manager.js | 4 ++-- lib/cache/memory_cache.js | 4 ++-- lib/plugins/aiSummary.js | 1 + pages/[prefix]/[slug]/index.js | 37 +++++++++++++++++++++------------- 5 files changed, 29 insertions(+), 18 deletions(-) diff --git a/blog.config.js b/blog.config.js index 161f7715..89567a8f 100644 --- a/blog.config.js +++ b/blog.config.js @@ -277,6 +277,7 @@ const BLOG = { AI_SUMMARY_KEY: process.env.AI_SUMMARY_KEY || '', + AI_SUMMARY_CACHE_TIME: process.env.AI_SUMMARY_CACHE_TIME || 1800, // 缓存时间,单位秒 AI_SUMMARY_WORD_LIMIT: process.env.AI_SUMMARY_WORD_LIMIT || 1000, diff --git a/lib/cache/cache_manager.js b/lib/cache/cache_manager.js index 1d665ca1..f35a1fe1 100644 --- a/lib/cache/cache_manager.js +++ b/lib/cache/cache_manager.js @@ -20,12 +20,12 @@ export async function getDataFromCache(key, force) { } } -export async function setDataToCache(key, data) { +export async function setDataToCache(key, data, customCacheTime) { if (!data) { return } // console.trace('[API-->>缓存写入]:', key) - await getApi().setCache(key, data) + await getApi().setCache(key, data, customCacheTime) } export async function delCacheData(key) { diff --git a/lib/cache/memory_cache.js b/lib/cache/memory_cache.js index 1a7012f9..001666c3 100644 --- a/lib/cache/memory_cache.js +++ b/lib/cache/memory_cache.js @@ -7,8 +7,8 @@ export async function getCache(key, options) { return await cache.get(key) } -export async function setCache(key, data) { - await cache.put(key, data, cacheTime * 1000) +export async function setCache(key, data, customCacheTime) { + await cache.put(key, data, (customCacheTime || cacheTime) * 1000) } export async function delCache(key) { diff --git a/lib/plugins/aiSummary.js b/lib/plugins/aiSummary.js index 5b9c1d4e..336b9954 100644 --- a/lib/plugins/aiSummary.js +++ b/lib/plugins/aiSummary.js @@ -7,6 +7,7 @@ */ export async function getAiSummary(aiSummaryAPI, aiSummaryKey, truncatedText) { try { + console.log('请求文章摘要', truncatedText.slice(0, 100)) const response = await fetch(aiSummaryAPI, { method: 'POST', headers: { diff --git a/pages/[prefix]/[slug]/index.js b/pages/[prefix]/[slug]/index.js index 6d30e227..fa9ced8e 100644 --- a/pages/[prefix]/[slug]/index.js +++ b/pages/[prefix]/[slug]/index.js @@ -8,6 +8,7 @@ import { idToUuid } from 'notion-utils' import Slug from '..' import { getPageContentText } from '@/pages/search/[keyword]' import { getAiSummary } from '@/lib/plugins/aiSummary' +import { getDataFromCache, setDataToCache } from '@/lib/cache/cache_manager' /** * 根据notion的slug访问页面 @@ -99,22 +100,30 @@ export async function getStaticProps({ params: { prefix, slug }, locale }) { const aiSummaryAPI = siteConfig('AI_SUMMARY_API') if (aiSummaryAPI) { - const aiSummaryKey = siteConfig('AI_SUMMARY_KEY') - const wordLimit = siteConfig('AI_SUMMARY_WORD_LIMIT', '1000') const post = props.post - let content = '' - for (let heading of post.toc) { - content += heading.text + ' ' + const cacheKey = `ai_summary_${post.id}` + let aiSummary = await getDataFromCache(cacheKey) + if (aiSummary) { + props.post.aiSummary = aiSummary + } else { + const aiSummaryKey = siteConfig('AI_SUMMARY_KEY') + const aiSummaryCacheTime = siteConfig('AI_SUMMARY_CACHE_TIME') + const wordLimit = siteConfig('AI_SUMMARY_WORD_LIMIT', '1000') + let content = '' + for (let heading of post.toc) { + content += heading.text + ' ' + } + content += getPageContentText(post, post.blockMap) + const combinedText = post.title + ' ' + content + const truncatedText = combinedText.slice(0, wordLimit) + aiSummary = await getAiSummary( + aiSummaryAPI, + aiSummaryKey, + truncatedText + ) + await setDataToCache(cacheKey, aiSummary, aiSummaryCacheTime) + props.post.aiSummary = aiSummary } - content += getPageContentText(post, post.blockMap) - const combinedText = post.title + ' ' + content - const truncatedText = combinedText.slice(0, wordLimit) - - props.post.aiSummary = await getAiSummary( - aiSummaryAPI, - aiSummaryKey, - truncatedText - ) } } From 2b2d5b2d3399df0465828b2a0e76fce2487a67d1 Mon Sep 17 00:00:00 2001 From: anime Date: Mon, 23 Dec 2024 18:12:12 +0800 Subject: [PATCH 4/8] =?UTF-8?q?feat(=E4=BC=98=E5=8C=96=E5=8E=9F=E7=94=9FAI?= =?UTF-8?q?=E6=91=98=E8=A6=81=E5=8A=9F=E8=83=BD=E6=98=BE=E7=A4=BA=E6=83=85?= =?UTF-8?q?=E5=86=B5):=20=E6=B2=A1=E6=9C=89=E6=95=B0=E6=8D=AE=E4=B8=8D?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=EF=BC=8C=E9=BB=98=E8=AE=A4=E6=B8=B2=E6=9F=93?= =?UTF-8?q?=E5=AE=8C=E6=95=B4=E6=91=98=E8=A6=81=EF=BC=8C=E5=88=A9=E4=BA=8E?= =?UTF-8?q?SEO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 1964bc96fdce688129287594103e6555fb42c779) --- components/AISummary.js | 52 +++++++++++++++++++++-------------------- lib/lang/en-US.js | 1 - lib/lang/fr-FR.js | 1 - lib/lang/ja-JP.js | 1 - lib/lang/tr-TR.js | 1 - lib/lang/zh-CN.js | 1 - lib/lang/zh-HK.js | 1 - lib/lang/zh-TW.js | 1 - 8 files changed, 27 insertions(+), 32 deletions(-) diff --git a/components/AISummary.js b/components/AISummary.js index 058d4214..29627c92 100644 --- a/components/AISummary.js +++ b/components/AISummary.js @@ -4,41 +4,43 @@ import { useGlobal } from '@/lib/global' const AISummary = ({ aiSummary }) => { const { locale } = useGlobal() - const [summary, setSummary] = useState(locale.AI_SUMMARY.GENERATING + '...') + const [summary, setSummary] = useState(aiSummary) useEffect(() => { showAiSummaryAnimation(aiSummary, setSummary) }, []) return ( -
-
-
-
- - - + aiSummary && ( +
+
+
+
+ + + +
+
{locale.AI_SUMMARY.NAME}
+
GPT
-
{locale.AI_SUMMARY.NAME}
-
GPT
-
-
-
- {summary} - {summary !== aiSummary && ( - - )} +
+
+ {summary} + {summary !== aiSummary && ( + + )} +
-
+ ) ) } diff --git a/lib/lang/en-US.js b/lib/lang/en-US.js index af5ec370..1649c6d3 100644 --- a/lib/lang/en-US.js +++ b/lib/lang/en-US.js @@ -90,6 +90,5 @@ export default { }, AI_SUMMARY: { NAME: 'AI intelligent summary', - GENERATING: 'Generating' } } diff --git a/lib/lang/fr-FR.js b/lib/lang/fr-FR.js index efc775c4..e507dc4b 100644 --- a/lib/lang/fr-FR.js +++ b/lib/lang/fr-FR.js @@ -54,6 +54,5 @@ export default { }, AI_SUMMARY: { NAME: "Résumé intelligent par l'IA", - GENERATING: 'Génération en cours' } } diff --git a/lib/lang/ja-JP.js b/lib/lang/ja-JP.js index cdb57e23..7ce1270c 100644 --- a/lib/lang/ja-JP.js +++ b/lib/lang/ja-JP.js @@ -61,6 +61,5 @@ export default { }, AI_SUMMARY: { NAME: 'AIインテリジェントサマリー', - GENERATING: '生成中' } } diff --git a/lib/lang/tr-TR.js b/lib/lang/tr-TR.js index 69bcbf6a..7b5ac6f7 100644 --- a/lib/lang/tr-TR.js +++ b/lib/lang/tr-TR.js @@ -56,6 +56,5 @@ export default { }, AI_SUMMARY: { NAME: 'Yapay Zeka Akıllı Özet', - GENERATING: 'Oluşturuluyor' } } diff --git a/lib/lang/zh-CN.js b/lib/lang/zh-CN.js index 82477e9f..265bb0c2 100644 --- a/lib/lang/zh-CN.js +++ b/lib/lang/zh-CN.js @@ -90,6 +90,5 @@ export default { }, AI_SUMMARY: { NAME: 'AI智能摘要', - GENERATING: '生成中' } } diff --git a/lib/lang/zh-HK.js b/lib/lang/zh-HK.js index e72c7a8c..a899fdac 100644 --- a/lib/lang/zh-HK.js +++ b/lib/lang/zh-HK.js @@ -55,6 +55,5 @@ export default { }, AI_SUMMARY: { NAME: 'AI智能摘要', - GENERATING: '生成中' } } diff --git a/lib/lang/zh-TW.js b/lib/lang/zh-TW.js index 550aee95..e6c4f061 100644 --- a/lib/lang/zh-TW.js +++ b/lib/lang/zh-TW.js @@ -55,6 +55,5 @@ export default { }, AI_SUMMARY: { NAME: 'AI智能摘要', - GENERATING: '生成中' } } From 09c092aefe462ea24bbe15030fdd737a8cdfbf63 Mon Sep 17 00:00:00 2001 From: anime Date: Mon, 23 Dec 2024 19:35:58 +0800 Subject: [PATCH 5/8] =?UTF-8?q?feat(=E7=BB=9F=E4=B8=80=E6=96=87=E7=AB=A0?= =?UTF-8?q?=E4=B8=8D=E5=90=8C=E8=B7=AF=E5=BE=84=E7=BB=93=E6=9E=84=E4=B8=8B?= =?UTF-8?q?=E7=9A=84=E6=95=B0=E6=8D=AE=E5=A4=84=E7=90=86=E6=96=B9=E5=BC=8F?= =?UTF-8?q?):=20=E4=BD=BF=E5=BE=97=E5=85=B6=E4=BB=96=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E4=B8=8B=E7=9A=84=E6=96=87=E7=AB=A0=E4=B9=9F?= =?UTF-8?q?=E8=83=BD=E8=BF=9B=E8=A1=8C=E5=8E=9F=E7=94=9FAI=E6=91=98?= =?UTF-8?q?=E8=A6=81=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit f427696b37872db96de53a071818a3fa98c691cd) --- lib/utils/post.js | 83 ++++++++++++++++++++++++++ pages/[prefix]/[slug]/[...suffix].js | 57 ++---------------- pages/[prefix]/[slug]/index.js | 88 ++-------------------------- pages/[prefix]/index.js | 56 ++---------------- 4 files changed, 95 insertions(+), 189 deletions(-) diff --git a/lib/utils/post.js b/lib/utils/post.js index e6402a02..efcb2904 100644 --- a/lib/utils/post.js +++ b/lib/utils/post.js @@ -2,6 +2,14 @@ * 文章相关工具 */ import { checkStartWithHttp } from '.' +import { getPostBlocks } from '@/lib/db/getSiteData' +import { getPageTableOfContents } from '@/lib/notion/getPageTableOfContents' +import { siteConfig } from '@/lib/config' +import { getDataFromCache, setDataToCache } from '@/lib/cache/cache_manager' +import { getPageContentText } from '@/pages/search/[keyword]' +import { getAiSummary } from '@/lib/plugins/aiSummary' +import BLOG from '@/blog.config' +import { uploadDataToAlgolia } from '@/lib/plugins/algolia' /** * 获取文章的关联推荐文章列表,目前根据标签关联性筛选 @@ -88,3 +96,78 @@ export function checkSlugHasMorThanTwoSlash(row) { !checkStartWithHttp(slug) ) } + +/** + * 处理文章数据 + * @param props + * @param from + * @returns {Promise} + */ +export async function processPostData(props, from) { + // 文章内容加载 + if (!props?.post?.blockMap) { + props.post.blockMap = await getPostBlocks(props.post.id, from) + } + + // 目录默认加载 + if (props.post?.blockMap?.block) { + props.post.content = Object.keys(props.post.blockMap.block).filter( + key => props.post.blockMap.block[key]?.value?.parent_id === props.post.id + ) + props.post.toc = getPageTableOfContents(props.post, props.post.blockMap) + + const aiSummaryAPI = siteConfig('AI_SUMMARY_API') + if (aiSummaryAPI) { + const post = props.post + const cacheKey = `ai_summary_${post.id}` + let aiSummary = await getDataFromCache(cacheKey) + if (aiSummary) { + props.post.aiSummary = aiSummary + } else { + const aiSummaryKey = siteConfig('AI_SUMMARY_KEY') + const aiSummaryCacheTime = siteConfig('AI_SUMMARY_CACHE_TIME') + const wordLimit = siteConfig('AI_SUMMARY_WORD_LIMIT', '1000') + let content = '' + for (let heading of post.toc) { + content += heading.text + ' ' + } + content += getPageContentText(post, post.blockMap) + const combinedText = post.title + ' ' + content + const truncatedText = combinedText.slice(0, wordLimit) + aiSummary = await getAiSummary( + aiSummaryAPI, + aiSummaryKey, + truncatedText + ) + await setDataToCache(cacheKey, aiSummary, aiSummaryCacheTime) + props.post.aiSummary = aiSummary + } + } + } + + // 生成全文索引 && JSON.parse(BLOG.ALGOLIA_RECREATE_DATA) + if (BLOG.ALGOLIA_APP_ID) { + uploadDataToAlgolia(props?.post) + } + + // 推荐关联文章处理 + const allPosts = props.allPages?.filter( + page => page.type === 'Post' && page.status === 'Published' + ) + if (allPosts && allPosts.length > 0) { + const index = allPosts.indexOf(props.post) + props.prev = allPosts.slice(index - 1, index)[0] ?? allPosts.slice(-1)[0] + props.next = allPosts.slice(index + 1, index + 2)[0] ?? allPosts[0] + props.recommendPosts = getRecommendPost( + props.post, + allPosts, + siteConfig('POST_RECOMMEND_COUNT') + ) + } else { + props.prev = null + props.next = null + props.recommendPosts = [] + } + + delete props.allPages +} diff --git a/pages/[prefix]/[slug]/[...suffix].js b/pages/[prefix]/[slug]/[...suffix].js index 90ca5a15..5e55ef85 100644 --- a/pages/[prefix]/[slug]/[...suffix].js +++ b/pages/[prefix]/[slug]/[...suffix].js @@ -1,9 +1,7 @@ import BLOG from '@/blog.config' import { siteConfig } from '@/lib/config' -import { getGlobalData, getPost, getPostBlocks } from '@/lib/db/getSiteData' -import { getPageTableOfContents } from '@/lib/notion/getPageTableOfContents' -import { uploadDataToAlgolia } from '@/lib/plugins/algolia' -import { checkSlugHasMorThanTwoSlash, getRecommendPost } from '@/lib/utils/post' +import { getGlobalData, getPost } from '@/lib/db/getSiteData' +import { checkSlugHasMorThanTwoSlash, processPostData } from '@/lib/utils/post' import { idToUuid } from 'notion-utils' import Slug from '..' @@ -79,59 +77,12 @@ export async function getStaticProps({ } } - // 无法获取文章 if (!props?.post) { + // 无法获取文章 props.post = null - return { - props, - revalidate: process.env.EXPORT - ? undefined - : siteConfig( - 'NEXT_REVALIDATE_SECOND', - BLOG.NEXT_REVALIDATE_SECOND, - props.NOTION_CONFIG - ) - } - } - - // 文章内容加载 - if (!props?.post?.blockMap) { - props.post.blockMap = await getPostBlocks(props.post.id, from) - } - - // 目录默认加载 - if (props.post?.blockMap?.block) { - props.post.content = Object.keys(props.post.blockMap.block).filter( - key => props.post.blockMap.block[key]?.value?.parent_id === props.post.id - ) - props.post.toc = getPageTableOfContents(props.post, props.post.blockMap) - } - - // 生成全文索引 && JSON.parse(BLOG.ALGOLIA_RECREATE_DATA) - if (BLOG.ALGOLIA_APP_ID) { - uploadDataToAlgolia(props?.post) - } - - // 推荐关联文章处理 - const allPosts = props.allPages?.filter( - page => page.type === 'Post' && page.status === 'Published' - ) - if (allPosts && allPosts.length > 0) { - const index = allPosts.indexOf(props.post) - props.prev = allPosts.slice(index - 1, index)[0] ?? allPosts.slice(-1)[0] - props.next = allPosts.slice(index + 1, index + 2)[0] ?? allPosts[0] - props.recommendPosts = getRecommendPost( - props.post, - allPosts, - siteConfig('POST_RECOMMEND_COUNT') - ) } else { - props.prev = null - props.next = null - props.recommendPosts = [] + await processPostData(props, from) } - - delete props.allPages return { props, revalidate: process.env.EXPORT diff --git a/pages/[prefix]/[slug]/index.js b/pages/[prefix]/[slug]/index.js index fa9ced8e..cb81cfd6 100644 --- a/pages/[prefix]/[slug]/index.js +++ b/pages/[prefix]/[slug]/index.js @@ -1,14 +1,9 @@ import BLOG from '@/blog.config' import { siteConfig } from '@/lib/config' -import { getGlobalData, getPost, getPostBlocks } from '@/lib/db/getSiteData' -import { getPageTableOfContents } from '@/lib/notion/getPageTableOfContents' -import { uploadDataToAlgolia } from '@/lib/plugins/algolia' -import { checkSlugHasOneSlash, getRecommendPost } from '@/lib/utils/post' +import { getGlobalData, getPost } from '@/lib/db/getSiteData' +import { checkSlugHasOneSlash, processPostData } from '@/lib/utils/post' import { idToUuid } from 'notion-utils' import Slug from '..' -import { getPageContentText } from '@/pages/search/[keyword]' -import { getAiSummary } from '@/lib/plugins/aiSummary' -import { getDataFromCache, setDataToCache } from '@/lib/cache/cache_manager' /** * 根据notion的slug访问页面 @@ -71,87 +66,12 @@ export async function getStaticProps({ params: { prefix, slug }, locale }) { } } - // 无法获取文章 if (!props?.post) { + // 无法获取文章 props.post = null - return { - props, - revalidate: process.env.EXPORT - ? undefined - : siteConfig( - 'NEXT_REVALIDATE_SECOND', - BLOG.NEXT_REVALIDATE_SECOND, - props.NOTION_CONFIG - ) - } - } - - // 文章内容加载 - if (!props?.post?.blockMap) { - props.post.blockMap = await getPostBlocks(props.post.id, from) - } - - // 目录默认加载 - if (props.post?.blockMap?.block) { - props.post.content = Object.keys(props.post.blockMap.block).filter( - key => props.post.blockMap.block[key]?.value?.parent_id === props.post.id - ) - props.post.toc = getPageTableOfContents(props.post, props.post.blockMap) - - const aiSummaryAPI = siteConfig('AI_SUMMARY_API') - if (aiSummaryAPI) { - const post = props.post - const cacheKey = `ai_summary_${post.id}` - let aiSummary = await getDataFromCache(cacheKey) - if (aiSummary) { - props.post.aiSummary = aiSummary - } else { - const aiSummaryKey = siteConfig('AI_SUMMARY_KEY') - const aiSummaryCacheTime = siteConfig('AI_SUMMARY_CACHE_TIME') - const wordLimit = siteConfig('AI_SUMMARY_WORD_LIMIT', '1000') - let content = '' - for (let heading of post.toc) { - content += heading.text + ' ' - } - content += getPageContentText(post, post.blockMap) - const combinedText = post.title + ' ' + content - const truncatedText = combinedText.slice(0, wordLimit) - aiSummary = await getAiSummary( - aiSummaryAPI, - aiSummaryKey, - truncatedText - ) - await setDataToCache(cacheKey, aiSummary, aiSummaryCacheTime) - props.post.aiSummary = aiSummary - } - } - } - - // 生成全文索引 && JSON.parse(BLOG.ALGOLIA_RECREATE_DATA) - if (BLOG.ALGOLIA_APP_ID) { - uploadDataToAlgolia(props?.post) - } - - // 推荐关联文章处理 - const allPosts = props.allPages?.filter( - page => page.type === 'Post' && page.status === 'Published' - ) - if (allPosts && allPosts.length > 0) { - const index = allPosts.indexOf(props.post) - props.prev = allPosts.slice(index - 1, index)[0] ?? allPosts.slice(-1)[0] - props.next = allPosts.slice(index + 1, index + 2)[0] ?? allPosts[0] - props.recommendPosts = getRecommendPost( - props.post, - allPosts, - siteConfig('POST_RECOMMEND_COUNT') - ) } else { - props.prev = null - props.next = null - props.recommendPosts = [] + await processPostData(props, from) } - - delete props.allPages return { props, revalidate: process.env.EXPORT diff --git a/pages/[prefix]/index.js b/pages/[prefix]/index.js index e26a95a9..672e6a0d 100644 --- a/pages/[prefix]/index.js +++ b/pages/[prefix]/index.js @@ -2,12 +2,11 @@ import BLOG from '@/blog.config' import useNotification from '@/components/Notification' import OpenWrite from '@/components/OpenWrite' import { siteConfig } from '@/lib/config' -import { getGlobalData, getPost, getPostBlocks } from '@/lib/db/getSiteData' +import { getGlobalData, getPost } from '@/lib/db/getSiteData' import { useGlobal } from '@/lib/global' import { getPageTableOfContents } from '@/lib/notion/getPageTableOfContents' import { getPasswordQuery } from '@/lib/password' -import { uploadDataToAlgolia } from '@/lib/plugins/algolia' -import { checkSlugHasNoSlash, getRecommendPost } from '@/lib/utils/post' +import { checkSlugHasNoSlash, processPostData } from '@/lib/utils/post' import { DynamicLayout } from '@/themes/theme' import md5 from 'js-md5' import { useRouter } from 'next/router' @@ -141,59 +140,12 @@ export async function getStaticProps({ params: { prefix }, locale }) { props.post = post } } - // 无法获取文章 if (!props?.post) { + // 无法获取文章 props.post = null - return { - props, - revalidate: process.env.EXPORT - ? undefined - : siteConfig( - 'NEXT_REVALIDATE_SECOND', - BLOG.NEXT_REVALIDATE_SECOND, - props.NOTION_CONFIG - ) - } - } - - // 文章内容加载 - if (!props?.post?.blockMap) { - props.post.blockMap = await getPostBlocks(props.post.id, from) - } - - // 目录默认加载 - if (props.post?.blockMap?.block) { - props.post.content = Object.keys(props.post.blockMap.block).filter( - key => props.post.blockMap.block[key]?.value?.parent_id === props.post.id - ) - props.post.toc = getPageTableOfContents(props.post, props.post.blockMap) - } - - // 生成全文索引 && process.env.npm_lifecycle_event === 'build' && JSON.parse(BLOG.ALGOLIA_RECREATE_DATA) - if (BLOG.ALGOLIA_APP_ID) { - uploadDataToAlgolia(props?.post) - } - - // 推荐关联文章处理 - const allPosts = props.allPages?.filter( - page => page.type === 'Post' && page.status === 'Published' - ) - if (allPosts && allPosts.length > 0) { - const index = allPosts.indexOf(props.post) - props.prev = allPosts.slice(index - 1, index)[0] ?? allPosts.slice(-1)[0] - props.next = allPosts.slice(index + 1, index + 2)[0] ?? allPosts[0] - props.recommendPosts = getRecommendPost( - props.post, - allPosts, - siteConfig('POST_RECOMMEND_COUNT') - ) } else { - props.prev = null - props.next = null - props.recommendPosts = [] + await processPostData(props, from) } - - delete props.allPages return { props, revalidate: process.env.EXPORT From ff89e16ee29241d1971a8fa99266954c797e85c1 Mon Sep 17 00:00:00 2001 From: anime Date: Mon, 23 Dec 2024 20:10:53 +0800 Subject: [PATCH 6/8] =?UTF-8?q?feat(=E5=8E=9F=E7=94=9F=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=AD=97=E6=95=B0=E7=BB=9F=E8=AE=A1=E5=92=8C=E9=98=85=E8=AF=BB?= =?UTF-8?q?=E6=97=B6=E9=95=BF):=20=E7=BB=9F=E4=B8=80=E4=BD=BF=E7=94=A8Word?= =?UTF-8?q?Count=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit d8180e1a783ad50c501b741adc72f2747896bdc1) --- components/WordCount.js | 74 +++++-------------------- lib/plugins/wordCount.js | 27 +++++++++ lib/utils/post.js | 8 ++- themes/heo/components/PostHeader.js | 10 ++-- themes/matery/components/ArticleInfo.js | 68 ++++++++++++----------- themes/matery/components/WordCount.js | 68 ----------------------- themes/next/components/ArticleDetail.js | 4 +- themes/next/components/WordCount.js | 62 --------------------- 8 files changed, 91 insertions(+), 230 deletions(-) create mode 100644 lib/plugins/wordCount.js delete mode 100644 themes/matery/components/WordCount.js delete mode 100644 themes/next/components/WordCount.js diff --git a/components/WordCount.js b/components/WordCount.js index 7f7066cf..af808251 100644 --- a/components/WordCount.js +++ b/components/WordCount.js @@ -1,67 +1,23 @@ import { useGlobal } from '@/lib/global' -import { useEffect } from 'react' /** * 字数统计 * @returns */ -export default function WordCount() { +export default function WordCount({ wordCount, readTime }) { const { locale } = useGlobal() - useEffect(() => { - countWords() - }) - - return - - - 0 - - - - - 0 {locale.COMMON.MINUTE} - + return ( + + + + {locale.COMMON.WORD_COUNT}  + {wordCount} + + + + {locale.COMMON.READ_TIME}≈  + {readTime} {locale.COMMON.MINUTE} + -} - -/** - * 更新字数统计和阅读时间 - */ -function countWords() { - const articleText = deleteHtmlTag(document.querySelector('#article-wrapper #notion-article')?.innerHTML) - const wordCount = fnGetCpmisWords(articleText) - // 阅读速度 300-500每分钟 - document.getElementById('wordCount').innerHTML = wordCount - document.getElementById('readTime').innerHTML = Math.floor(wordCount / 400) + 1 - const wordCountWrapper = document.getElementById('wordCountWrapper') - wordCountWrapper.classList.remove('hidden') -} - -// 去除html标签 -function deleteHtmlTag(str) { - if (!str) { - return '' - } - str = str.replace(/<[^>]+>|&[^>]+;/g, '').trim()// 去掉所有的html标签和 之类的特殊符合 - return str -} - -// 用word方式计算正文字数 -function fnGetCpmisWords(str) { - if (!str) { - return 0 - } - let sLen = 0 - try { - // eslint-disable-next-line no-irregular-whitespace - str = str.replace(/(\r\n+|\s+| +)/g, '龘') - // eslint-disable-next-line no-control-regex - str = str.replace(/[\x00-\xff]/g, 'm') - str = str.replace(/m+/g, '*') - str = str.replace(/龘+/g, '') - sLen = str.length - } catch (e) { - - } - return sLen -} + ) +} \ No newline at end of file diff --git a/lib/plugins/wordCount.js b/lib/plugins/wordCount.js new file mode 100644 index 00000000..156c8b85 --- /dev/null +++ b/lib/plugins/wordCount.js @@ -0,0 +1,27 @@ +/** + * 更新字数统计和阅读时间 + */ +export function countWords(pageContentText) { + const wordCount = fnGetCpmisWords(pageContentText) + // 阅读速度 300-500每分钟 + const readTime = Math.floor(wordCount / 400) + 1 + return { wordCount, readTime } +} + +// 用word方式计算正文字数 +function fnGetCpmisWords(str) { + if (!str) { + return 0 + } + let sLen = 0 + try { + // eslint-disable-next-line no-irregular-whitespace + str = str.replace(/(\r\n+|\s+| +)/g, '龘') + // eslint-disable-next-line no-control-regex + str = str.replace(/[\x00-\xff]/g, 'm') + str = str.replace(/m+/g, '*') + str = str.replace(/龘+/g, '') + sLen = str.length + } catch (e) {} + return sLen +} diff --git a/lib/utils/post.js b/lib/utils/post.js index efcb2904..be16cc1d 100644 --- a/lib/utils/post.js +++ b/lib/utils/post.js @@ -10,6 +10,7 @@ import { getPageContentText } from '@/pages/search/[keyword]' import { getAiSummary } from '@/lib/plugins/aiSummary' import BLOG from '@/blog.config' import { uploadDataToAlgolia } from '@/lib/plugins/algolia' +import { countWords } from '@/lib/plugins/wordCount' /** * 获取文章的关联推荐文章列表,目前根据标签关联性筛选 @@ -115,7 +116,10 @@ export async function processPostData(props, from) { key => props.post.blockMap.block[key]?.value?.parent_id === props.post.id ) props.post.toc = getPageTableOfContents(props.post, props.post.blockMap) - + const pageContentText = getPageContentText(props.post, props.post.blockMap) + const { wordCount, readTime } = countWords(pageContentText) + props.post.wordCount = wordCount + props.post.readTime = readTime const aiSummaryAPI = siteConfig('AI_SUMMARY_API') if (aiSummaryAPI) { const post = props.post @@ -131,7 +135,7 @@ export async function processPostData(props, from) { for (let heading of post.toc) { content += heading.text + ' ' } - content += getPageContentText(post, post.blockMap) + content += pageContentText const combinedText = post.title + ' ' + content const truncatedText = combinedText.slice(0, wordLimit) aiSummary = await getAiSummary( diff --git a/themes/heo/components/PostHeader.js b/themes/heo/components/PostHeader.js index e4452e92..88711b50 100644 --- a/themes/heo/components/PostHeader.js +++ b/themes/heo/components/PostHeader.js @@ -31,9 +31,8 @@ export default function PostHeader({ post, siteInfo, isDarkMode }) { height: 100%; top: 0; left: 0; - box-shadow: 110px -130px 500px 100px ${isDarkMode - ? '#CA8A04' - : '#0060e0'} inset; + box-shadow: 110px -130px 500px 100px + ${isDarkMode ? '#CA8A04' : '#0060e0'} inset; } `} @@ -105,7 +104,10 @@ export default function PostHeader({ post, siteInfo, isDarkMode }) {
- +
{post?.type !== 'Page' && ( <> diff --git a/themes/matery/components/ArticleInfo.js b/themes/matery/components/ArticleInfo.js index af3fa823..010d5e85 100644 --- a/themes/matery/components/ArticleInfo.js +++ b/themes/matery/components/ArticleInfo.js @@ -1,46 +1,48 @@ import Link from 'next/link' import { useGlobal } from '@/lib/global' import TagItemMiddle from './TagItemMiddle' -import WordCount from './WordCount' import { formatDateFmt } from '@/lib/utils/formatDate' +import WordCount from '@/components/WordCount' -export const ArticleInfo = (props) => { +export const ArticleInfo = props => { const { post } = props const { locale } = useGlobal() return ( -
-
- {post.tagItems && ( -
- {post.tagItems.map(tag => ( - - ))} -
- )} -
+
+
+ {post.tagItems && ( +
+ {post.tagItems.map(tag => ( + + ))} +
+ )} +
-
- {post?.type !== 'Page' && (<> - - - {locale.COMMON.POST_TIME}: {post?.publishDay} - - - - {locale.COMMON.LAST_EDITED_TIME}: {post.lastEditedDay} - - - - - - )} -
- -
+
+ {post?.type !== 'Page' && ( + <> + + {' '} + {locale.COMMON.POST_TIME}: {post?.publishDay} + + + + {locale.COMMON.LAST_EDITED_TIME}: {post.lastEditedDay} + + + + + + + + )} +
+
) } diff --git a/themes/matery/components/WordCount.js b/themes/matery/components/WordCount.js deleted file mode 100644 index d7d7c02b..00000000 --- a/themes/matery/components/WordCount.js +++ /dev/null @@ -1,68 +0,0 @@ -import { useGlobal } from '@/lib/global' -import { useEffect } from 'react' - -/** - * 字数统计 - * @returns - */ -export default function WordCount() { - const { locale } = useGlobal() - useEffect(() => { - countWords() - }) - - return - - - {locale.COMMON.WORD_COUNT}  - 0 - - - - - 0 {locale.COMMON.MINUTE} - - -} - -/** - * 更新字数统计和阅读时间 - */ -function countWords() { - const articleText = deleteHtmlTag(document.querySelector('#article-wrapper #notion-article')?.innerHTML) - const wordCount = fnGetCpmisWords(articleText) - // 阅读速度 300-500每分钟 - document.getElementById('wordCount').innerHTML = wordCount - document.getElementById('readTime').innerHTML = Math.floor(wordCount / 400) + 1 - const wordCountWrapper = document.getElementById('wordCountWrapper') - wordCountWrapper.classList.remove('hidden') -} - -// 去除html标签 -function deleteHtmlTag(str) { - if (!str) { - return '' - } - str = str.replace(/<[^>]+>|&[^>]+;/g, '').trim()// 去掉所有的html标签和 之类的特殊符合 - return str -} - -// 用word方式计算正文字数 -function fnGetCpmisWords(str) { - if (!str) { - return 0 - } - let sLen = 0 - try { - // eslint-disable-next-line no-irregular-whitespace - str = str.replace(/(\r\n+|\s+| +)/g, '龘') - // eslint-disable-next-line no-control-regex - str = str.replace(/[\x00-\xff]/g, 'm') - str = str.replace(/m+/g, '*') - str = str.replace(/龘+/g, '') - sLen = str.length - } catch (e) { - - } - return sLen -} diff --git a/themes/next/components/ArticleDetail.js b/themes/next/components/ArticleDetail.js index 61bcdb2f..902cf6a6 100644 --- a/themes/next/components/ArticleDetail.js +++ b/themes/next/components/ArticleDetail.js @@ -14,7 +14,7 @@ import ArticleCopyright from './ArticleCopyright' import BlogAround from './BlogAround' import RecommendPosts from './RecommendPosts' import TagItem from './TagItem' -import WordCount from './WordCount' +import WordCount from '@/components/WordCount' /** * @@ -92,7 +92,7 @@ export default function ArticleDetail(props) { )}
- +
)} diff --git a/themes/next/components/WordCount.js b/themes/next/components/WordCount.js deleted file mode 100644 index 6cfd1cc6..00000000 --- a/themes/next/components/WordCount.js +++ /dev/null @@ -1,62 +0,0 @@ -import { useGlobal } from '@/lib/global' -import { useEffect } from 'react' - -/** - * 字数统计 - * @returns - */ -export default function WordCount() { - useEffect(() => { - countWords() - }) - - const { locale } = useGlobal() - - return
- - {locale.COMMON.WORD_COUNT}≈ - 0  |  {locale.COMMON.READ_TIME} 0 {locale.COMMON.MINUTE} -
-} - -/** - * 更新字数统计和阅读时间 - */ -function countWords() { - const articleText = deleteHtmlTag(document.querySelector('#article-wrapper #notion-article')?.innerHTML) - const wordCount = fnGetCpmisWords(articleText) - // 阅读速度 300-500每分钟 - document.getElementById('wordCount').innerHTML = wordCount - document.getElementById('readTime').innerHTML = Math.floor(wordCount / 400) + 1 - const wordCountWrapper = document.getElementById('wordCountWrapper') - wordCountWrapper.classList.remove('hidden') -} - -// 去除html标签 -function deleteHtmlTag(str) { - if (!str) { - return '' - } - str = str.replace(/<[^>]+>|&[^>]+;/g, '').trim()// 去掉所有的html标签和 之类的特殊符合 - return str -} - -// 用word方式计算正文字数 -function fnGetCpmisWords(str) { - if (!str) { - return 0 - } - let sLen = 0 - try { - // eslint-disable-next-line no-irregular-whitespace - str = str.replace(/(\r\n+|\s+| +)/g, '龘') - // eslint-disable-next-line no-control-regex - str = str.replace(/[\x00-\xff]/g, 'm') - str = str.replace(/m+/g, '*') - str = str.replace(/龘+/g, '') - sLen = str.length - } catch (e) { - - } - return sLen -} From f8258a1de792cdaec8c493d62f77a3b37d5e9d19 Mon Sep 17 00:00:00 2001 From: anime Date: Mon, 23 Dec 2024 20:22:51 +0800 Subject: [PATCH 7/8] =?UTF-8?q?refactor(=E9=87=8D=E6=9E=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=A2=9E=E5=BC=BA=E5=8F=AF=E8=AF=BB=E6=80=A7):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 304107fff915f037326b87cb78629a2cdcd9a5e4) --- lib/utils/post.js | 62 ++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/lib/utils/post.js b/lib/utils/post.js index be16cc1d..a3ee0f91 100644 --- a/lib/utils/post.js +++ b/lib/utils/post.js @@ -98,6 +98,38 @@ export function checkSlugHasMorThanTwoSlash(row) { ) } +/** + * 获取文章摘要 + * @param props + * @param pageContentText + * @returns {Promise} + */ +async function getPageAISummary(props, pageContentText) { + const aiSummaryAPI = siteConfig('AI_SUMMARY_API') + if (aiSummaryAPI) { + const post = props.post + const cacheKey = `ai_summary_${post.id}` + let aiSummary = await getDataFromCache(cacheKey) + if (aiSummary) { + props.post.aiSummary = aiSummary + } else { + const aiSummaryKey = siteConfig('AI_SUMMARY_KEY') + const aiSummaryCacheTime = siteConfig('AI_SUMMARY_CACHE_TIME') + const wordLimit = siteConfig('AI_SUMMARY_WORD_LIMIT', '1000') + let content = '' + for (let heading of post.toc) { + content += heading.text + ' ' + } + content += pageContentText + const combinedText = post.title + ' ' + content + const truncatedText = combinedText.slice(0, wordLimit) + aiSummary = await getAiSummary(aiSummaryAPI, aiSummaryKey, truncatedText) + await setDataToCache(cacheKey, aiSummary, aiSummaryCacheTime) + props.post.aiSummary = aiSummary + } + } +} + /** * 处理文章数据 * @param props @@ -110,8 +142,8 @@ export async function processPostData(props, from) { props.post.blockMap = await getPostBlocks(props.post.id, from) } - // 目录默认加载 if (props.post?.blockMap?.block) { + // 目录默认加载 props.post.content = Object.keys(props.post.blockMap.block).filter( key => props.post.blockMap.block[key]?.value?.parent_id === props.post.id ) @@ -120,33 +152,7 @@ export async function processPostData(props, from) { const { wordCount, readTime } = countWords(pageContentText) props.post.wordCount = wordCount props.post.readTime = readTime - const aiSummaryAPI = siteConfig('AI_SUMMARY_API') - if (aiSummaryAPI) { - const post = props.post - const cacheKey = `ai_summary_${post.id}` - let aiSummary = await getDataFromCache(cacheKey) - if (aiSummary) { - props.post.aiSummary = aiSummary - } else { - const aiSummaryKey = siteConfig('AI_SUMMARY_KEY') - const aiSummaryCacheTime = siteConfig('AI_SUMMARY_CACHE_TIME') - const wordLimit = siteConfig('AI_SUMMARY_WORD_LIMIT', '1000') - let content = '' - for (let heading of post.toc) { - content += heading.text + ' ' - } - content += pageContentText - const combinedText = post.title + ' ' + content - const truncatedText = combinedText.slice(0, wordLimit) - aiSummary = await getAiSummary( - aiSummaryAPI, - aiSummaryKey, - truncatedText - ) - await setDataToCache(cacheKey, aiSummary, aiSummaryCacheTime) - props.post.aiSummary = aiSummary - } - } + await getPageAISummary(props, pageContentText) } // 生成全文索引 && JSON.parse(BLOG.ALGOLIA_RECREATE_DATA) From b84f16aca2c867cd593ac447a3a93f9d7ca46dd9 Mon Sep 17 00:00:00 2001 From: anime Date: Fri, 27 Dec 2024 21:48:31 +0800 Subject: [PATCH 8/8] =?UTF-8?q?feat(=E7=89=B9=E6=AE=8A=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=AF=BB=E5=8F=96=E6=B7=BB=E5=8A=A0=20AI=5FSUMMARY=20=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E9=85=8D=E7=BD=AE=E9=A1=B9):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 7a53b1950c8238636b4c525332e48a50f43ace2d) --- lib/config.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/config.js b/lib/config.js index bf03f95f..314e8318 100644 --- a/lib/config.js +++ b/lib/config.js @@ -38,9 +38,10 @@ export const siteConfig = (key, defaultVal = null, extendConfig = {}) => { case 'TAG_SORT_BY_COUNT': case 'THEME': case 'LINK': - case 'NPM_CDN_BASE': - case 'CDNJS_CDN_BASE': - case 'JSDELIVR_CDN_BASE': + case 'AI_SUMMARY_API': + case 'AI_SUMMARY_KEY': + case 'AI_SUMMARY_CACHE_TIME': + case 'AI_SUMMARY_WORD_LIMIT': // LINK比较特殊, if (key === 'LINK') { if (!extendConfig || Object.keys(extendConfig).length === 0) {