- 使用 React 18 + TypeScript + Vite + Tailwind CSS 构建前端 - 实现 WebSocket 实时通信(useWebSocket hook) - 添加聊天界面组件(MessageList, MessageBubble, MessageInput) - 集成 Topic 管理(新建、列出、切换) - 支持 Markdown 渲染(react-markdown + remark-gfm) - 添加工具调用展示面板 - 实现深色科技主题(Tech Dark) - 后端集成静态文件服务(tower-http) - 添加 Makefile 和 build.sh 构建脚本 - 更新 .gitignore 忽略前端构建产物 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
50 lines
1.7 KiB
TypeScript
50 lines
1.7 KiB
TypeScript
import { useEffect, useRef } from 'react'
|
|
import { MessageBubble } from './MessageBubble'
|
|
import type { ChatMessage } from '../../types/protocol'
|
|
import { Sparkles } from 'lucide-react'
|
|
|
|
interface MessageListProps {
|
|
messages: ChatMessage[]
|
|
}
|
|
|
|
export function MessageList({ messages }: MessageListProps) {
|
|
const bottomRef = useRef<HTMLDivElement>(null)
|
|
const containerRef = useRef<HTMLDivElement>(null)
|
|
|
|
useEffect(() => {
|
|
if (bottomRef.current) {
|
|
bottomRef.current.scrollIntoView({ behavior: 'smooth' })
|
|
}
|
|
}, [messages])
|
|
|
|
if (messages.length === 0) {
|
|
return (
|
|
<div className="flex h-full items-center justify-center">
|
|
<div className="text-center animate-fade-in">
|
|
<div className="mb-6 inline-flex h-20 w-20 items-center justify-center rounded-full bg-gradient-to-br from-[#00f0ff]/20 to-[#3b82f6]/20 shadow-2xl shadow-[#00f0ff]/20">
|
|
<Sparkles className="h-10 w-10 text-[#00f0ff]" />
|
|
</div>
|
|
<h2 className="mb-2 text-2xl font-bold text-white">开始新的对话</h2>
|
|
<p className="text-zinc-500">在下方输入消息开始与 AI 助手聊天</p>
|
|
<div className="mt-8 flex items-center justify-center gap-4 text-sm text-zinc-600">
|
|
<span className="px-3 py-1 rounded-full bg-zinc-800/50 border border-zinc-700">/new 创建话题</span>
|
|
<span className="px-3 py-1 rounded-full bg-zinc-800/50 border border-zinc-700">/list 查看列表</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div
|
|
ref={containerRef}
|
|
className="h-full overflow-y-auto p-6 space-y-6"
|
|
>
|
|
{messages.map((message) => (
|
|
<MessageBubble key={message.id} message={message} />
|
|
))}
|
|
<div ref={bottomRef} />
|
|
</div>
|
|
)
|
|
}
|