docs(guide): rewrite CLAUDE.md with ZCLAW-first perspective
Major changes: - Shift from "OpenFang desktop client" to "independent AI Agent desktop app" - Add decision principle: "Is this useful for ZCLAW? Does it affect ZCLAW?" - Simplify project structure and tech stack sections - Replace OpenClaw vs OpenFang comparison with unified backend approach - Consolidate troubleshooting from scattered sections into organized FAQ - Update Hands system documentation with 8 capabilities and status - Stream
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import { useHandStore } from '../../store/handStore';
|
||||
import { useGatewayStore } from '../../store/gatewayStore';
|
||||
import { useWorkflowStore } from '../../store/workflowStore';
|
||||
import {
|
||||
type AutomationItem,
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
Plus,
|
||||
Calendar,
|
||||
Search,
|
||||
X,
|
||||
} from 'lucide-react';
|
||||
import { useToast } from '../ui/Toast';
|
||||
|
||||
@@ -50,14 +51,14 @@ export function AutomationPanel({
|
||||
onSelect,
|
||||
showBatchActions = true,
|
||||
}: AutomationPanelProps) {
|
||||
// Store state
|
||||
const hands = useHandStore(s => s.hands);
|
||||
const workflows = useWorkflowStore(s => s.workflows);
|
||||
const isLoadingHands = useHandStore(s => s.isLoading);
|
||||
const isLoadingWorkflows = useWorkflowStore(s => s.isLoading);
|
||||
const loadHands = useHandStore(s => s.loadHands);
|
||||
const loadWorkflows = useWorkflowStore(s => s.loadWorkflows);
|
||||
const triggerHand = useHandStore(s => s.triggerHand);
|
||||
// Store state - use gatewayStore which has the actual data
|
||||
const hands = useGatewayStore(s => s.hands);
|
||||
const workflows = useGatewayStore(s => s.workflows);
|
||||
const isLoading = useGatewayStore(s => s.isLoading);
|
||||
const loadHands = useGatewayStore(s => s.loadHands);
|
||||
const loadWorkflows = useGatewayStore(s => s.loadWorkflows);
|
||||
const triggerHand = useGatewayStore(s => s.triggerHand);
|
||||
// workflowStore for triggerWorkflow (not in gatewayStore)
|
||||
const triggerWorkflow = useWorkflowStore(s => s.triggerWorkflow);
|
||||
|
||||
// UI state
|
||||
@@ -66,6 +67,8 @@ export function AutomationPanel({
|
||||
const [viewMode, setViewMode] = useState<ViewMode>('grid');
|
||||
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
||||
const [executingIds, setExecutingIds] = useState<Set<string>>(new Set());
|
||||
const [showWorkflowDialog, setShowWorkflowDialog] = useState(false);
|
||||
const [showSchedulerDialog, setShowSchedulerDialog] = useState(false);
|
||||
|
||||
const { toast } = useToast();
|
||||
|
||||
@@ -115,6 +118,15 @@ export function AutomationPanel({
|
||||
setSelectedIds(new Set());
|
||||
}, []);
|
||||
|
||||
// Workflow dialog handlers
|
||||
const handleCreateWorkflow = useCallback(() => {
|
||||
setShowWorkflowDialog(true);
|
||||
}, []);
|
||||
|
||||
const handleSchedulerManage = useCallback(() => {
|
||||
setShowSchedulerDialog(true);
|
||||
}, []);
|
||||
|
||||
// Execute handler
|
||||
const handleExecute = useCallback(async (item: AutomationItem, params?: Record<string, unknown>) => {
|
||||
setExecutingIds(prev => new Set(prev).add(item.id));
|
||||
@@ -173,8 +185,6 @@ export function AutomationPanel({
|
||||
toast('数据已刷新', 'success');
|
||||
}, [loadHands, loadWorkflows, toast]);
|
||||
|
||||
const isLoading = isLoadingHands || isLoadingWorkflows;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
{/* Header */}
|
||||
@@ -198,12 +208,14 @@ export function AutomationPanel({
|
||||
<RefreshCw className={`w-4 h-4 ${isLoading ? 'animate-spin' : ''}`} />
|
||||
</button>
|
||||
<button
|
||||
onClick={handleCreateWorkflow}
|
||||
className="p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"
|
||||
title="新建工作流"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSchedulerManage}
|
||||
className="p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"
|
||||
title="调度管理"
|
||||
>
|
||||
@@ -271,6 +283,96 @@ export function AutomationPanel({
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Create Workflow Dialog */}
|
||||
{showWorkflowDialog && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-md mx-4">
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">新建工作流</h3>
|
||||
<button
|
||||
onClick={() => setShowWorkflowDialog(false)}
|
||||
className="p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
工作流名称
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="输入工作流名称..."
|
||||
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-gray-400"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
描述
|
||||
</label>
|
||||
<textarea
|
||||
placeholder="描述这个工作流的用途..."
|
||||
rows={3}
|
||||
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-gray-400 resize-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end gap-2 p-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<button
|
||||
onClick={() => setShowWorkflowDialog(false)}
|
||||
className="px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
toast('工作流创建功能开发中', 'info');
|
||||
setShowWorkflowDialog(false);
|
||||
}}
|
||||
className="px-4 py-2 text-sm bg-gray-700 dark:bg-gray-600 text-white rounded-lg hover:bg-gray-800 dark:hover:bg-gray-500"
|
||||
>
|
||||
创建
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Scheduler Dialog */}
|
||||
{showSchedulerDialog && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-lg mx-4">
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">调度管理</h3>
|
||||
<button
|
||||
onClick={() => setShowSchedulerDialog(false)}
|
||||
className="p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="text-center py-8 text-gray-500 dark:text-gray-400">
|
||||
<Calendar className="w-12 h-12 mx-auto mb-3 opacity-50" />
|
||||
<p>调度管理功能开发中</p>
|
||||
<p className="text-sm mt-1">将支持定时执行、Cron 表达式配置等</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end p-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<button
|
||||
onClick={() => setShowSchedulerDialog(false)}
|
||||
className="px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg"
|
||||
>
|
||||
关闭
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user