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:
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { useChatStore, Message } from '../store/chatStore';
|
||||
import { useGatewayStore } from '../store/gatewayStore';
|
||||
import { Send, Paperclip, ChevronDown, Terminal, Loader2, SquarePen } from 'lucide-react';
|
||||
import { Paperclip, ChevronDown, Terminal, SquarePen, ArrowUp } from 'lucide-react';
|
||||
|
||||
const MODELS = ['glm-5', 'qwen3.5-plus', 'kimi-k2.5', 'minimax-m2.5'];
|
||||
|
||||
@@ -41,7 +41,7 @@ export function ChatArea() {
|
||||
}, [messages]);
|
||||
|
||||
const handleSend = () => {
|
||||
if (!input.trim() || isStreaming) return;
|
||||
if (!input.trim() || isStreaming || !connected) return;
|
||||
sendToGateway(input);
|
||||
setInput('');
|
||||
};
|
||||
@@ -58,13 +58,20 @@ export function ChatArea() {
|
||||
return (
|
||||
<>
|
||||
{/* Header */}
|
||||
<div className="h-14 border-b border-gray-100 flex items-center justify-between px-6 flex-shrink-0">
|
||||
<div className="h-14 border-b border-gray-100 flex items-center justify-between px-6 flex-shrink-0 bg-white">
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="font-semibold text-gray-900">{currentAgent?.name || 'ZCLAW'}</h2>
|
||||
<span className={`text-xs flex items-center gap-1 ${connected ? 'text-green-500' : 'text-gray-400'}`}>
|
||||
<span className={`w-1.5 h-1.5 rounded-full ${connected ? 'bg-green-400' : 'bg-gray-300'}`}></span>
|
||||
{connected ? 'Gateway 已连接' : 'Gateway 未连接'}
|
||||
</span>
|
||||
{isStreaming ? (
|
||||
<span className="text-xs text-gray-400 flex items-center gap-1">
|
||||
<span className="w-1.5 h-1.5 bg-gray-400 rounded-full thinking-dot"></span>
|
||||
正在输入中
|
||||
</span>
|
||||
) : (
|
||||
<span className={`text-xs flex items-center gap-1 ${connected ? 'text-green-500' : 'text-gray-400'}`}>
|
||||
<span className={`w-1.5 h-1.5 rounded-full ${connected ? 'bg-green-400' : 'bg-gray-300'}`}></span>
|
||||
{connected ? 'Gateway 已连接' : 'Gateway 未连接'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{messages.length > 0 && (
|
||||
@@ -81,7 +88,7 @@ export function ChatArea() {
|
||||
</div>
|
||||
|
||||
{/* Messages */}
|
||||
<div ref={scrollRef} className="flex-1 overflow-y-auto custom-scrollbar p-6 space-y-4">
|
||||
<div ref={scrollRef} className="flex-1 overflow-y-auto custom-scrollbar p-6 space-y-6">
|
||||
{messages.length === 0 && (
|
||||
<div className="text-center text-gray-400 py-20">
|
||||
<p className="text-lg mb-2">欢迎使用 ZCLAW 🦞</p>
|
||||
@@ -92,13 +99,6 @@ export function ChatArea() {
|
||||
{messages.map((message) => (
|
||||
<MessageBubble key={message.id} message={message} />
|
||||
))}
|
||||
|
||||
{isStreaming && (
|
||||
<div className="flex items-center gap-2 text-gray-400 text-xs pl-12">
|
||||
<Loader2 className="w-3 h-3 animate-spin" />
|
||||
Agent 思考中...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Input */}
|
||||
@@ -114,10 +114,16 @@ export function ChatArea() {
|
||||
value={input}
|
||||
onChange={(e) => { setInput(e.target.value); adjustTextarea(); }}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={isStreaming ? 'Agent 正在回复...' : '发送给 ZCLAW(Shift+Enter 换行)'}
|
||||
disabled={isStreaming}
|
||||
placeholder={
|
||||
!connected
|
||||
? '请先连接 Gateway'
|
||||
: isStreaming
|
||||
? 'Agent 正在回复...'
|
||||
: `发送给 ${currentAgent?.name || 'ZCLAW'}`
|
||||
}
|
||||
disabled={isStreaming || !connected}
|
||||
rows={1}
|
||||
className="w-full bg-transparent border-none focus:outline-none text-gray-700 placeholder-gray-400 disabled:opacity-50 resize-none leading-relaxed"
|
||||
className="w-full bg-transparent border-none focus:outline-none text-gray-700 placeholder-gray-400 disabled:opacity-50 resize-none leading-relaxed mt-1"
|
||||
style={{ minHeight: '24px', maxHeight: '160px' }}
|
||||
/>
|
||||
</div>
|
||||
@@ -144,10 +150,10 @@ export function ChatArea() {
|
||||
)}
|
||||
<button
|
||||
onClick={handleSend}
|
||||
disabled={isStreaming || !input.trim()}
|
||||
disabled={isStreaming || !input.trim() || !connected}
|
||||
className="w-8 h-8 bg-gray-900 text-white rounded-full flex items-center justify-center hover:bg-gray-800 transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
<Send className="w-4 h-4" />
|
||||
<ArrowUp className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user