feat: 添加 Tauri 桌面应用

- 创建 packages/desktop 模块
- 实现 Tauri 2.0 + React 桌面应用
- 复用 Web 前端代码
- 添加系统托盘功能
- 实现本地文件访问命令
- 配置 Vite + Tauri 集成
- 更新 .gitignore 添加 Rust/Tauri 相关规则
This commit is contained in:
2025-12-12 13:19:00 +08:00
parent b5d3b7df57
commit 6ef9d95172
86 changed files with 14495 additions and 1 deletions
+90
View File
@@ -0,0 +1,90 @@
/**
* Chat Page
*/
import { useEffect, useRef } from 'react';
import { Wifi, WifiOff } from 'lucide-react';
import { useChat } from '../hooks/useChat';
import { ChatMessage, StreamingMessage, TypingIndicator } from '../components/ChatMessage';
import { ChatInput } from '../components/ChatInput';
interface ChatPageProps {
sessionId: string;
}
export function ChatPage({ sessionId }: ChatPageProps) {
const {
messages,
isConnected,
isLoading,
streamingContent,
sendMessage,
cancelProcessing,
} = useChat({
sessionId,
onError: (error) => {
console.error('Chat error:', error);
},
});
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}
/>
</div>
);
}