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>
This commit is contained in:
iven
2026-03-14 23:16:32 +08:00
parent 67e1da635d
commit 07079293f4
126 changed files with 36229 additions and 1035 deletions

View File

@@ -1,29 +1,46 @@
import { useState } from 'react';
import { useGatewayStore } from '../store/gatewayStore';
import { Settings, MessageSquare, Clock, Bot, Radio } from 'lucide-react';
import { Settings } from 'lucide-react';
import { CloneManager } from './CloneManager';
import { ConversationList } from './ConversationList';
import { ChannelList } from './ChannelList';
import { HandList } from './HandList';
import { TaskList } from './TaskList';
import { useGatewayStore } from '../store/gatewayStore';
export type MainViewType = 'chat' | 'hands' | 'workflow';
interface SidebarProps {
onOpenSettings?: () => void;
onMainViewChange?: (view: MainViewType) => void;
selectedHandId?: string;
onSelectHand?: (handId: string) => void;
}
type Tab = 'chats' | 'clones' | 'channels' | 'tasks';
type Tab = 'clones' | 'hands' | 'workflow';
const TABS: { key: Tab; label: string; icon: typeof MessageSquare }[] = [
{ key: 'chats', label: '对话', icon: MessageSquare },
{ key: 'clones', label: '分身', icon: Bot },
{ key: 'channels', label: '频道', icon: Radio },
{ key: 'tasks', label: '任务', icon: Clock },
const TABS: { key: Tab; label: string; mainView?: MainViewType }[] = [
{ key: 'clones', label: '分身' },
{ key: 'hands', label: 'HANDS', mainView: 'hands' },
{ key: 'workflow', label: 'Workflow', mainView: 'workflow' },
];
export function Sidebar({ onOpenSettings }: SidebarProps) {
const { connectionState } = useGatewayStore();
const [activeTab, setActiveTab] = useState<Tab>('chats');
export function Sidebar({ onOpenSettings, onMainViewChange, selectedHandId, onSelectHand }: SidebarProps) {
const [activeTab, setActiveTab] = useState<Tab>('clones');
const userName = useGatewayStore((state) => state.quickConfig.userName) || '用户7141';
const connected = connectionState === 'connected';
const handleTabClick = (key: Tab, mainView?: MainViewType) => {
setActiveTab(key);
if (mainView && onMainViewChange) {
onMainViewChange(mainView);
} else if (onMainViewChange) {
onMainViewChange('chat');
}
};
const handleSelectHand = (handId: string) => {
onSelectHand?.(handId);
// 切换到 hands 视图
setActiveTab('hands');
onMainViewChange?.('hands');
};
return (
<aside className="w-64 bg-gray-50 border-r border-gray-200 flex flex-col flex-shrink-0">
@@ -32,12 +49,12 @@ export function Sidebar({ onOpenSettings }: SidebarProps) {
{TABS.map(({ key, label }) => (
<button
key={key}
className={`flex-1 py-3 px-2 text-xs font-medium transition-colors ${
className={`flex-1 py-3 px-4 text-xs font-medium transition-colors ${
activeTab === key
? 'text-orange-600 border-b-2 border-orange-500'
? 'text-gray-900 border-b-2 border-gray-900'
: 'text-gray-500 hover:text-gray-700'
}`}
onClick={() => setActiveTab(key)}
onClick={() => handleTabClick(key, TABS.find(t => t.key === key)?.mainView)}
>
{label}
</button>
@@ -46,25 +63,24 @@ export function Sidebar({ onOpenSettings }: SidebarProps) {
{/* Tab content */}
<div className="flex-1 overflow-hidden">
{activeTab === 'chats' && <ConversationList />}
{activeTab === 'clones' && <CloneManager />}
{activeTab === 'channels' && <ChannelList onOpenSettings={onOpenSettings} />}
{activeTab === 'tasks' && <TaskList />}
{activeTab === 'hands' && (
<HandList
selectedHandId={selectedHandId}
onSelectHand={handleSelectHand}
/>
)}
{activeTab === 'workflow' && <TaskList />}
</div>
{/* 底部用户 */}
<div className="p-3 border-t border-gray-200 bg-gray-50">
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-gradient-to-br from-orange-400 to-red-500 rounded-full flex items-center justify-center text-white text-xs font-bold">
🦞
</div>
<div className="flex-1 min-w-0">
<span className="font-medium text-gray-700 text-sm">7141</span>
<div className={`text-xs ${connected ? 'text-green-500' : 'text-gray-400'}`}>
{connected ? '已连接' : '未连接'}
</div>
</div>
<button className="text-gray-400 hover:text-gray-600" onClick={onOpenSettings}>
<span className="font-medium text-gray-700">{userName}</span>
<button className="ml-auto text-gray-400 hover:text-gray-600" onClick={onOpenSettings}>
<Settings className="w-4 h-4" />
</button>
</div>