Files
zclaw_openfang/desktop/src/components/ConversationList.tsx
iven 07079293f4 feat(hands): restructure Hands UI with Chinese localization
Major changes:
- Add HandList.tsx component for left sidebar
- Add HandTaskPanel.tsx for middle content area
- Restructure Sidebar tabs: 分身/HANDS/Workflow
- Remove Hands tab from RightPanel
- Localize all UI text to Chinese
- Archive legacy OpenClaw documentation
- Add Hands integration lessons document
- Update feature checklist with new components

UI improvements:
- Left sidebar now shows Hands list with status icons
- Middle area shows selected Hand's tasks and results
- Consistent styling with Tailwind CSS
- Chinese status labels and buttons

Documentation:
- Create docs/archive/openclaw-legacy/ for old docs
- Add docs/knowledge-base/hands-integration-lessons.md
- Update docs/knowledge-base/feature-checklist.md
- Update docs/knowledge-base/README.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 23:16:32 +08:00

117 lines
4.4 KiB
TypeScript

import { useChatStore } from '../store/chatStore';
import { MessageSquare, Trash2, SquarePen } from 'lucide-react';
export function ConversationList() {
const {
conversations, currentConversationId, messages, agents, currentAgent,
newConversation, switchConversation, deleteConversation,
} = useChatStore();
const hasActiveChat = messages.length > 0;
return (
<div className="h-full flex flex-col">
{/* Header */}
<div className="flex items-center justify-between px-3 py-2 border-b border-gray-200">
<span className="text-xs font-medium text-gray-500"></span>
<button
onClick={newConversation}
className="p-1 text-gray-400 hover:text-orange-500 rounded"
title="新对话"
>
<SquarePen className="w-4 h-4" />
</button>
</div>
<div className="flex-1 overflow-y-auto custom-scrollbar">
{/* Current active chat (unsaved) */}
{hasActiveChat && !currentConversationId && (
<div className="flex items-center gap-3 px-3 py-3 bg-orange-50 border-b border-orange-100 cursor-default">
<div className="w-7 h-7 bg-orange-500 rounded-lg flex items-center justify-center text-white flex-shrink-0">
<MessageSquare className="w-3.5 h-3.5" />
</div>
<div className="flex-1 min-w-0">
<div className="text-xs font-medium text-orange-700 truncate"></div>
<div className="text-[11px] text-orange-500 truncate">
{messages.filter(m => m.role === 'user').length} · {currentAgent?.name || 'ZCLAW'}
</div>
</div>
</div>
)}
{/* Saved conversations */}
{conversations.map((conv) => {
const isActive = conv.id === currentConversationId;
const msgCount = conv.messages.filter(m => m.role === 'user').length;
const timeStr = formatTime(conv.updatedAt);
const agentName = conv.agentId
? agents.find((agent) => agent.id === conv.agentId)?.name || conv.agentId
: 'ZCLAW';
return (
<div
key={conv.id}
onClick={() => switchConversation(conv.id)}
className={`group flex items-center gap-3 px-3 py-3 cursor-pointer border-b border-gray-50 transition-colors ${
isActive ? 'bg-orange-50' : 'hover:bg-gray-100'
}`}
>
<div className={`w-7 h-7 rounded-lg flex items-center justify-center flex-shrink-0 ${
isActive ? 'bg-orange-500 text-white' : 'bg-gray-200 text-gray-500'
}`}>
<MessageSquare className="w-3.5 h-3.5" />
</div>
<div className="flex-1 min-w-0">
<div className={`text-xs font-medium truncate ${isActive ? 'text-orange-700' : 'text-gray-900'}`}>
{conv.title}
</div>
<div className="text-[11px] text-gray-400 truncate">
{msgCount} · {agentName} · {timeStr}
</div>
</div>
<button
onClick={(e) => {
e.stopPropagation();
if (confirm('删除该对话?')) {
deleteConversation(conv.id);
}
}}
className="opacity-0 group-hover:opacity-100 p-1 text-gray-300 hover:text-red-500 transition-opacity"
title="删除"
>
<Trash2 className="w-3 h-3" />
</button>
</div>
);
})}
{conversations.length === 0 && !hasActiveChat && (
<div className="text-center py-8 text-xs text-gray-400">
<MessageSquare className="w-8 h-8 mx-auto mb-2 opacity-30" />
<p></p>
<p className="mt-1"></p>
</div>
)}
</div>
</div>
);
}
function formatTime(date: Date): string {
const now = new Date();
const d = new Date(date);
const diffMs = now.getTime() - d.getTime();
const diffMin = Math.floor(diffMs / 60000);
if (diffMin < 1) return '刚刚';
if (diffMin < 60) return `${diffMin} 分钟前`;
const diffHr = Math.floor(diffMin / 60);
if (diffHr < 24) return `${diffHr} 小时前`;
const diffDay = Math.floor(diffHr / 24);
if (diffDay < 7) return `${diffDay} 天前`;
return `${d.getMonth() + 1}/${d.getDate()}`;
}