feat(ui): Phase 8 UI/UX optimization and system documentation update
## Sidebar Enhancement - Change tabs to icon + small label layout for better space utilization - Add Teams tab with team collaboration entry point ## Settings Page Improvements - Connect theme toggle to gatewayStore.saveQuickConfig for persistence - Remove OpenFang backend download section, simplify UI - Add time range filter to UsageStats (7d/30d/all) - Add stat cards with icons (sessions, messages, input/output tokens) - Add token usage overview bar chart - Add 8 ZCLAW system skill definitions with categories ## Bug Fixes - Fix ChannelList duplicate content with deduplication logic - Integrate CreateTriggerModal in TriggersPanel - Add independent SecurityStatusPanel with 12 default enabled layers - Change workflow view to use SchedulerPanel as unified entry ## New Components - CreateTriggerModal: Event trigger creation modal - HandApprovalModal: Hand approval workflow dialog - HandParamsForm: Enhanced Hand parameter form - SecurityLayersPanel: 16-layer security status display ## Architecture - Add TOML config parsing support (toml-utils.ts, config-parser.ts) - Add request timeout and retry mechanism (request-helper.ts) - Add secure token storage (secure-storage.ts, secure_storage.rs) ## Tests - Add unit tests for config-parser, toml-utils, request-helper - Add team-client and teamStore tests ## Documentation - Update SYSTEM_ANALYSIS.md with Phase 8 completion - UI completion: 100% (30/30 components) - API coverage: 93% (63/68 endpoints) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,72 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useGatewayStore } from '../../store/gatewayStore';
|
||||
import { Wrench, Zap, FileCode, Globe, Mail, Database, Search, MessageSquare } from 'lucide-react';
|
||||
|
||||
// ZCLAW 内置系统技能
|
||||
const SYSTEM_SKILLS = [
|
||||
{
|
||||
id: 'code-assistant',
|
||||
name: '代码助手',
|
||||
description: '代码编写、调试、重构和优化',
|
||||
category: '开发',
|
||||
icon: FileCode,
|
||||
},
|
||||
{
|
||||
id: 'web-search',
|
||||
name: '网络搜索',
|
||||
description: '实时搜索互联网信息',
|
||||
category: '信息',
|
||||
icon: Search,
|
||||
},
|
||||
{
|
||||
id: 'file-manager',
|
||||
name: '文件管理',
|
||||
description: '文件读写、搜索和整理',
|
||||
category: '系统',
|
||||
icon: Database,
|
||||
},
|
||||
{
|
||||
id: 'web-browsing',
|
||||
name: '网页浏览',
|
||||
description: '访问和解析网页内容',
|
||||
category: '信息',
|
||||
icon: Globe,
|
||||
},
|
||||
{
|
||||
id: 'email-handler',
|
||||
name: '邮件处理',
|
||||
description: '发送和管理电子邮件',
|
||||
category: '通讯',
|
||||
icon: Mail,
|
||||
},
|
||||
{
|
||||
id: 'chat-skill',
|
||||
name: '对话技能',
|
||||
description: '自然语言对话和问答',
|
||||
category: '交互',
|
||||
icon: MessageSquare,
|
||||
},
|
||||
{
|
||||
id: 'automation',
|
||||
name: '自动化任务',
|
||||
description: '自动化工作流程执行',
|
||||
category: '系统',
|
||||
icon: Zap,
|
||||
},
|
||||
{
|
||||
id: 'tool-executor',
|
||||
name: '工具执行器',
|
||||
description: '执行系统命令和脚本',
|
||||
category: '系统',
|
||||
icon: Wrench,
|
||||
},
|
||||
];
|
||||
|
||||
export function Skills() {
|
||||
const { connectionState, quickConfig, skillsCatalog, loadSkillsCatalog, saveQuickConfig } = useGatewayStore();
|
||||
const connected = connectionState === 'connected';
|
||||
const [extraDir, setExtraDir] = useState('');
|
||||
const [activeFilter, setActiveFilter] = useState<'all' | 'system' | 'builtin' | 'extra'>('all');
|
||||
|
||||
useEffect(() => {
|
||||
if (connected) {
|
||||
@@ -23,6 +85,13 @@ export function Skills() {
|
||||
await loadSkillsCatalog();
|
||||
};
|
||||
|
||||
const filteredCatalog = skillsCatalog.filter(skill => {
|
||||
if (activeFilter === 'all') return true;
|
||||
if (activeFilter === 'builtin') return skill.source === 'builtin';
|
||||
if (activeFilter === 'extra') return skill.source === 'extra';
|
||||
return true;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="max-w-3xl">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
@@ -41,16 +110,47 @@ export function Skills() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 系统技能 */}
|
||||
<div className="mb-6">
|
||||
<h3 className="text-sm font-semibold text-gray-700 mb-3">ZCLAW 系统技能</h3>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
{SYSTEM_SKILLS.map((skill) => {
|
||||
const Icon = skill.icon;
|
||||
return (
|
||||
<div
|
||||
key={skill.id}
|
||||
className="bg-white rounded-xl border border-gray-200 p-4 shadow-sm hover:shadow-md transition-shadow"
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-10 h-10 bg-gradient-to-br from-blue-500 to-purple-500 rounded-lg flex items-center justify-center flex-shrink-0">
|
||||
<Icon className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm font-medium text-gray-900">{skill.name}</span>
|
||||
<span className="text-[10px] px-1.5 py-0.5 bg-purple-50 text-purple-600 rounded">
|
||||
{skill.category}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 mt-1">{skill.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6 mb-6 shadow-sm">
|
||||
<h3 className="font-medium mb-2 text-gray-900">额外技能目录</h3>
|
||||
<p className="text-xs text-gray-500 mb-4">包含 SKILL.md 文件的额外目录。保存到 Gateway 配置的 skills.load.extraDirs 中。</p>
|
||||
<div className="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
<input
|
||||
type="text"
|
||||
value={extraDir}
|
||||
onChange={(e) => setExtraDir(e.target.value)}
|
||||
placeholder="输入额外技能目录"
|
||||
className="flex-1 px-3 py-2 border border-gray-200 rounded-lg text-sm bg-gray-50 focus:outline-none"
|
||||
className="flex-1 px-3 py-2 border border-gray-200 rounded-lg text-sm bg-gray-50 focus:outline-none"
|
||||
/>
|
||||
<button
|
||||
onClick={() => { handleAddDir().catch(() => {}); }}
|
||||
@@ -70,8 +170,30 @@ export function Skills() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Gateway 技能 */}
|
||||
<div className="mb-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-sm font-semibold text-gray-700">Gateway 技能</h3>
|
||||
<div className="flex items-center gap-2">
|
||||
{['all', 'builtin', 'extra'].map((filter) => (
|
||||
<button
|
||||
key={filter}
|
||||
onClick={() => setActiveFilter(filter as typeof activeFilter)}
|
||||
className={`text-xs px-2 py-1 rounded-md transition-colors ${
|
||||
activeFilter === filter
|
||||
? 'bg-blue-100 text-blue-700'
|
||||
: 'text-gray-500 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
{filter === 'all' ? '全部' : filter === 'builtin' ? '内置' : '额外'}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl border border-gray-200 shadow-sm divide-y divide-gray-100">
|
||||
{skillsCatalog.length > 0 ? skillsCatalog.map((skill) => (
|
||||
{filteredCatalog.length > 0 ? filteredCatalog.map((skill) => (
|
||||
<div key={skill.id} className="p-4">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user