Skip to content

Commit

Permalink
Merge branch 'main' into deployment/victorliaocs
Browse files Browse the repository at this point in the history
  • Loading branch information
tangly1024 committed Feb 11, 2025
2 parents 4968a00 + f5be02e commit 0f027c8
Show file tree
Hide file tree
Showing 193 changed files with 6,545 additions and 4,662 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,4 @@
# ENABLE_CACHE=
# VERCEL_ENV=
# NEXT_PUBLIC_VERSION=
# NEXT_BUILD_STANDALONE=
31 changes: 25 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,23 +1,42 @@
ARG NOTION_PAGE_ID
ARG NEXT_PUBLIC_THEME

# Install dependencies only when needed
FROM node:18-alpine3.18 AS deps
FROM node:18-alpine3.18 AS base

# 1. Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json ./
RUN yarn install --frozen-lockfile

# Rebuild the source code only when needed
FROM node:18-alpine3.18 AS builder
# 2. Rebuild the source code only when needed
FROM base AS builder
ARG NOTION_PAGE_ID
ENV NEXT_BUILD_STANDALONE=true

WORKDIR /app

COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN yarn build

ENV NODE_ENV production
# 3. Production image, copy all the files and run next
FROM base AS runner
ENV NODE_ENV=production

WORKDIR /app

COPY --from=builder /app/public ./public

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static

# 个人仓库把将配置好的.env.local文件放到项目根目录,可自动使用环境变量
# COPY --from=builder /app/.env.local ./

EXPOSE 3000

Expand All @@ -26,4 +45,4 @@ EXPOSE 3000
# Uncomment the following line in case you want to disable telemetry.
# ENV NEXT_TELEMETRY_DISABLED 1

CMD ["yarn", "start"]
CMD ["node", "server.js"]
550 changes: 36 additions & 514 deletions blog.config.js

Large diffs are not rendered by default.

98 changes: 98 additions & 0 deletions components/AISummary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
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
53 changes: 53 additions & 0 deletions components/AISummary.module.css
Original file line number Diff line number Diff line change
@@ -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; }
}
32 changes: 23 additions & 9 deletions components/Artalk.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,32 @@ const Artalk = ({ siteInfo }) => {

const initArtalk = async () => {
await loadExternalResource(artalkCss, 'css')
window?.Artalk?.init({
server: artalkServer, // 后端地址
el: '#artalk', // 容器元素
const artalk = window?.Artalk?.init({
server: artalkServer,
el: '#artalk',
locale: artalkLocale,
// pageKey: '/post/1', // 固定链接 (留空自动获取)
// pageTitle: '关于引入 Artalk 的这档子事', // 页面标题 (留空自动获取)
site: site // 你的站点名
site: site,
darkMode: document.documentElement.classList.contains('dark')
})

const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === 'class') {
const isDark = document.documentElement.classList.contains('dark')
artalk?.setDarkMode(isDark)
}
})
})

observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class']
})

return () => observer.disconnect()
}
return (
<div id="artalk"></div>
)

return <div id="artalk"></div>
}

export default Artalk
5 changes: 3 additions & 2 deletions components/BeiAnSite.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import { siteConfig } from '@/lib/config'
*/
export default function BeiAnSite() {
const beian = siteConfig('BEI_AN')
const beianLink = siteConfig('BEI_AN_LINK')
if (!beian) {
return null
}
return (
<span>
<i className='fas fa-shield-alt' />
<a href='https://beian.miit.gov.cn/' className='mx-1'>
{siteConfig('BEI_AN')}
<a href={beianLink} className='mx-1'>
{beian}
</a>
<br />
</span>
Expand Down
30 changes: 17 additions & 13 deletions components/Collapse.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ import { useEffect, useImperativeHandle, useRef } from 'react'

/**
* 折叠面板组件,支持水平折叠、垂直折叠
* @param {type:['horizontal','vertical'],isOpen} props
* @param {type:['horizontal','vertical'], isOpen} props
* @returns
*/
const Collapse = props => {
const { collapseRef } = props
const Collapse = ({
type = 'vertical',
isOpen = false,
children,
onHeightChange,
className,
collapseRef
}) => {
const ref = useRef(null)
const type = props.type || 'vertical'

useImperativeHandle(collapseRef, () => {
return {
Expand All @@ -17,7 +22,7 @@ const Collapse = props => {
* @param {*} param0
*/
updateCollapseHeight: ({ height, increase }) => {
if (props.isOpen) {
if (isOpen) {
ref.current.style.height = ref.current.scrollHeight
ref.current.style.height = 'auto'
}
Expand Down Expand Up @@ -76,18 +81,18 @@ const Collapse = props => {
}

useEffect(() => {
if (props.isOpen) {
if (isOpen) {
expandSection(ref.current)
} else {
collapseSection(ref.current)
}
// 通知父组件高度变化
props?.onHeightChange &&
props.onHeightChange({
onHeightChange &&
onHeightChange({
height: ref.current.scrollHeight,
increase: props.isOpen
increase: isOpen
})
}, [props.isOpen])
}, [isOpen])

return (
<div
Expand All @@ -97,11 +102,10 @@ const Collapse = props => {
? { height: '0px', willChange: 'height' }
: { width: '0px', willChange: 'width' }
}
className={`${props.className || ''} overflow-hidden duration-300`}>
{props.children}
className={`${className || ''} overflow-hidden duration-300`}>
{children}
</div>
)
}
Collapse.defaultProps = { isOpen: false }

export default Collapse
Loading

0 comments on commit 0f027c8

Please sign in to comment.