feat(ui): 创建共享 UI 组件包

将 web 和 desktop 的重复代码抽取到 @ai-assistant/ui 包:

- 添加可配置的 API 客户端 (configureApiClient)
- 迁移共享组件: ChatMessage, ChatInput, Sidebar, FileBrowser, ConfigPanel
- 迁移共享 hook: useChat
- 添加 responsive prop 支持响应式布局
- 更新 web/desktop 依赖并删除重复代码
This commit is contained in:
2025-12-12 15:52:53 +08:00
parent 563224fa73
commit 68ab6a2016
30 changed files with 711 additions and 1388 deletions
@@ -0,0 +1,80 @@
/**
* Chat Message Component
*/
import { User, Bot } from 'lucide-react';
import clsx from 'clsx';
import type { Message } from '../api/client.js';
interface ChatMessageProps {
message: Message;
}
export function ChatMessage({ message }: ChatMessageProps) {
const isUser = message.role === 'user';
return (
<div
className={clsx(
'flex gap-4 p-4 rounded-lg',
isUser ? 'bg-gray-800' : 'bg-gray-850'
)}
>
<div
className={clsx(
'w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0',
isUser ? 'bg-primary-600' : 'bg-green-600'
)}
>
{isUser ? <User size={18} /> : <Bot size={18} />}
</div>
<div className="flex-1 min-w-0">
<div className="text-sm text-gray-400 mb-1">
{isUser ? 'You' : 'AI Assistant'}
</div>
<div className="message-content whitespace-pre-wrap break-words">
{message.content}
</div>
</div>
</div>
);
}
interface StreamingMessageProps {
content: string;
}
export function StreamingMessage({ content }: StreamingMessageProps) {
return (
<div className="flex gap-4 p-4 rounded-lg bg-gray-850">
<div className="w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0 bg-green-600">
<Bot size={18} />
</div>
<div className="flex-1 min-w-0">
<div className="text-sm text-gray-400 mb-1">AI Assistant</div>
<div className="message-content whitespace-pre-wrap break-words">
{content}
<span className="inline-block w-2 h-4 bg-gray-400 animate-pulse ml-1" />
</div>
</div>
</div>
);
}
export function TypingIndicator() {
return (
<div className="flex gap-4 p-4 rounded-lg bg-gray-850">
<div className="w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0 bg-green-600">
<Bot size={18} />
</div>
<div className="flex-1">
<div className="text-sm text-gray-400 mb-1">AI Assistant</div>
<div className="typing-indicator">
<span />
<span />
<span />
</div>
</div>
</div>
);
}