Files
NotionNext/components/AISummary.js
anime 2b2d5b2d33 feat(优化原生AI摘要功能显示情况): 没有数据不加载,默认渲染完整摘要,利于SEO
(cherry picked from commit 1964bc96fdce688129287594103e6555fb42c779)
2024-12-29 00:21:39 +08:00

99 lines
3.1 KiB
JavaScript

import styles from './AISummary.module.css'
import { useEffect, useState } from 'react'
import { useGlobal } from '@/lib/global'
const AISummary = ({ aiSummary }) => {
const { locale } = useGlobal()
const [summary, setSummary] = useState(aiSummary)
useEffect(() => {
showAiSummaryAnimation(aiSummary, setSummary)
}, [])
return (
aiSummary && (
<div className={styles['post-ai']}>
<div className={styles['ai-container']}>
<div className={styles['ai-header']}>
<div className={styles['ai-icon']}>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 24 24'
width='24'
height='24'>
<path
fill='#ffffff'
d='M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4M12,6A6,6 0 0,1 18,12A6,6 0 0,1 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6M12,8A4,4 0 0,0 8,12A4,4 0 0,0 12,16A4,4 0 0,0 16,12A4,4 0 0,0 12,8Z'
/>
</svg>
</div>
<div className={styles['ai-title']}>{locale.AI_SUMMARY.NAME}</div>
<div className={styles['ai-tag']}>GPT</div>
</div>
<div className={styles['ai-content']}>
<div className={styles['ai-explanation']}>
{summary}
{summary !== aiSummary && (
<span className={styles['blinking-cursor']}></span>
)}
</div>
</div>
</div>
</div>
)
)
}
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