Files
ai-terminal-assistant/packages/web/src/pages/Chat.tsx
T
kurihada 68ab6a2016 feat(ui): 创建共享 UI 组件包
将 web 和 desktop 的重复代码抽取到 @ai-assistant/ui 包:

- 添加可配置的 API 客户端 (configureApiClient)
- 迁移共享组件: ChatMessage, ChatInput, Sidebar, FileBrowser, ConfigPanel
- 迁移共享 hook: useChat
- 添加 responsive prop 支持响应式布局
- 更新 web/desktop 依赖并删除重复代码
2025-12-12 15:52:53 +08:00

99 lines
2.6 KiB
TypeScript

/**
* Chat Page
*/
import { useEffect, useRef } from 'react';
import { Wifi, WifiOff } from 'lucide-react';
import {
useChat,
ChatMessage,
StreamingMessage,
TypingIndicator,
ChatInput,
} from '@ai-assistant/ui';
interface ChatPageProps {
sessionId: string;
onSessionNotFound?: () => void;
responsive?: boolean;
}
export function ChatPage({ sessionId, onSessionNotFound, responsive = false }: ChatPageProps) {
const {
messages,
isConnected,
isLoading,
streamingContent,
sendMessage,
cancelProcessing,
} = useChat({
sessionId,
onError: (error) => {
console.error('Chat error:', error);
},
onSessionNotFound,
});
const messagesEndRef = useRef<HTMLDivElement>(null);
// 自动滚动到底部
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages, streamingContent]);
return (
<div className="flex-1 flex flex-col h-screen">
{/* Header */}
<div className="flex items-center justify-between px-6 py-3 border-b border-gray-700 bg-gray-800">
<h1 className="text-lg font-medium">Chat</h1>
<div className="flex items-center gap-2 text-sm">
{isConnected ? (
<>
<Wifi size={16} className="text-green-500" />
<span className="text-green-500">Connected</span>
</>
) : (
<>
<WifiOff size={16} className="text-red-500" />
<span className="text-red-500">Disconnected</span>
</>
)}
</div>
</div>
{/* Messages */}
<div className="flex-1 overflow-y-auto p-6">
<div className="max-w-4xl mx-auto space-y-4">
{messages.length === 0 && !isLoading && (
<div className="text-center py-20">
<h2 className="text-2xl font-semibold mb-2">Start a conversation</h2>
<p className="text-gray-400">
Type a message below to begin chatting with your AI assistant.
</p>
</div>
)}
{messages.map((message) => (
<ChatMessage key={message.id} message={message} />
))}
{streamingContent && <StreamingMessage content={streamingContent} />}
{isLoading && !streamingContent && <TypingIndicator />}
<div ref={messagesEndRef} />
</div>
</div>
{/* Input */}
<ChatInput
onSend={sendMessage}
onCancel={cancelProcessing}
isLoading={isLoading}
disabled={!isConnected}
responsive={responsive}
/>
</div>
);
}