diff --git a/web/src/components/LandingPage.tsx b/web/src/components/LandingPage.tsx
index ee8064f3..0e8a2d4c 100644
--- a/web/src/components/LandingPage.tsx
+++ b/web/src/components/LandingPage.tsx
@@ -15,6 +15,7 @@ import {
Cpu,
} from 'lucide-react'
import { CryptoFeatureCard } from './CryptoFeatureCard'
+import Typewriter from './Typewriter'
// Animation variants
const fadeInUp = {
@@ -515,14 +516,19 @@ export function LandingPage() {
boxShadow: '0 20px 60px rgba(240, 185, 11, 0.2)',
}}
>
-
- {`$ git clone https://github.com/...
-$ docker compose up -d
-🚀 NOFX 已启动
-✓ AI 代理运行中
-✓ 交易所已连接
-✓ 策略已激活`}
-
+
diff --git a/web/src/components/Typewriter.tsx b/web/src/components/Typewriter.tsx
new file mode 100644
index 00000000..e8e33349
--- /dev/null
+++ b/web/src/components/Typewriter.tsx
@@ -0,0 +1,63 @@
+import { useEffect, useRef, useState } from 'react'
+
+interface TypewriterProps {
+ lines: string[]
+ typingSpeed?: number // 毫秒/字符
+ lineDelay?: number // 每行结束的额外等待
+ className?: string
+}
+
+export default function Typewriter({
+ lines,
+ typingSpeed = 25,
+ lineDelay = 400,
+ className,
+}: TypewriterProps) {
+ const [text, setText] = useState('')
+ const [showCursor, setShowCursor] = useState(true)
+ const lineIndexRef = useRef(0)
+ const charIndexRef = useRef(0)
+ const timerRef = useRef(null)
+ const blinkRef = useRef(null)
+
+ useEffect(() => {
+ function typeNext() {
+ const currentLine = lines[lineIndexRef.current] ?? ''
+ if (charIndexRef.current < currentLine.length) {
+ setText((t) => t + currentLine[charIndexRef.current])
+ charIndexRef.current += 1
+ timerRef.current = window.setTimeout(typeNext, typingSpeed)
+ } else {
+ // 行结束
+ if (lineIndexRef.current < lines.length - 1) {
+ setText((t) => t + '\n')
+ lineIndexRef.current += 1
+ charIndexRef.current = 0
+ timerRef.current = window.setTimeout(typeNext, lineDelay)
+ } else {
+ // 最后一行输入完毕
+ timerRef.current = null
+ }
+ }
+ }
+
+ typeNext()
+
+ // 光标闪烁
+ blinkRef.current = window.setInterval(() => {
+ setShowCursor((v) => !v)
+ }, 500)
+
+ return () => {
+ if (timerRef.current) window.clearTimeout(timerRef.current)
+ if (blinkRef.current) window.clearInterval(blinkRef.current)
+ }
+ }, [lines, typingSpeed, lineDelay])
+
+ return (
+
+ {text}
+ ▍
+
+ )
+}