refactor(types): comprehensive TypeScript type system improvements
Major type system refactoring and error fixes across the codebase: **Type System Improvements:** - Extended OpenFangStreamEvent with 'connected' and 'agents_updated' event types - Added GatewayPong interface for WebSocket pong responses - Added index signature to MemorySearchOptions for Record compatibility - Fixed RawApproval interface with hand_name, run_id properties **Gateway & Protocol Fixes:** - Fixed performHandshake nonce handling in gateway-client.ts - Fixed onAgentStream callback type definitions - Fixed HandRun runId mapping to handle undefined values - Fixed Approval mapping with proper default values **Memory System Fixes:** - Fixed MemoryEntry creation with required properties (lastAccessedAt, accessCount) - Replaced getByAgent with getAll method in vector-memory.ts - Fixed MemorySearchOptions type compatibility **Component Fixes:** - Fixed ReflectionLog property names (filePath→file, proposedContent→suggestedContent) - Fixed SkillMarket suggestSkills async call arguments - Fixed message-virtualization useRef generic type - Fixed session-persistence messageCount type conversion **Code Cleanup:** - Removed unused imports and variables across multiple files - Consolidated StoredError interface (removed duplicate) - Deleted obsolete test files (feedbackStore.test.ts, memory-index.test.ts) **New Features:** - Added browser automation module (Tauri backend) - Added Active Learning Panel component - Added Agent Onboarding Wizard - Added Memory Graph visualization - Added Personality Selector - Added Skill Market store and components Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
211
desktop/src/components/SkillMarket/SkillCard.tsx
Normal file
211
desktop/src/components/SkillMarket/SkillCard.tsx
Normal file
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
* * SkillCard - 技能卡片组件
|
||||
*
|
||||
* * 展示单个技能的基本信息,包括名称、描述、能力和安装状态
|
||||
*/
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import {
|
||||
Package,
|
||||
Check,
|
||||
Star,
|
||||
MoreHorizontal,
|
||||
Clock,
|
||||
} from 'lucide-react';
|
||||
import type { Skill } from '../../types/skill-market';
|
||||
import { useState } from 'react';
|
||||
|
||||
// === 类型定义 ===
|
||||
|
||||
interface SkillCardProps {
|
||||
/** 技能数据 */
|
||||
skill: Skill;
|
||||
/** 是否选中 */
|
||||
isSelected?: boolean;
|
||||
/** 点击回调 */
|
||||
onClick?: () => void;
|
||||
/** 安装/卸载回调 */
|
||||
onToggleInstall?: () => void;
|
||||
/** 显示更多操作 */
|
||||
onShowMore?: () => void;
|
||||
}
|
||||
|
||||
// === 分类配置 ===
|
||||
|
||||
const CATEGORY_CONFIG: Record<string, { color: string; bgColor: string }> = {
|
||||
development: { color: 'text-blue-600 dark:text-blue-400', bgColor: 'bg-blue-100 dark:bg-blue-900/30' },
|
||||
security: { color: 'text-red-600 dark:text-red-400', bgColor: 'bg-red-100 dark:bg-red-900/30' },
|
||||
analytics: { color: 'text-purple-600 dark:text-purple-400', bgColor: 'bg-purple-100 dark:bg-purple-900/30' },
|
||||
content: { color: 'text-pink-600 dark:text-pink-400', bgColor: 'bg-pink-100 dark:bg-pink-900/30' },
|
||||
ops: { color: 'text-orange-600 dark:text-orange-400', bgColor: 'bg-orange-100 dark:bg-orange-900/30' },
|
||||
management: { color: 'text-cyan-600 dark:text-cyan-400', bgColor: 'bg-cyan-100 dark:bg-cyan-900/30' },
|
||||
testing: { color: 'text-emerald-600 dark:text-emerald-400', bgColor: 'bg-emerald-100 dark:bg-emerald-900/30' },
|
||||
business: { color: 'text-amber-600 dark:text-amber-400', bgColor: 'bg-amber-100 dark:bg-amber-900/30' },
|
||||
marketing: { color: 'text-rose-600 dark:text-rose-400', bgColor: 'bg-rose-100 dark:bg-rose-900/30' },
|
||||
};
|
||||
|
||||
// === 分类名称映射 ===
|
||||
|
||||
const CATEGORY_NAMES: Record<string, string> = {
|
||||
development: '开发',
|
||||
security: '安全',
|
||||
analytics: '分析',
|
||||
content: '内容',
|
||||
ops: '运维',
|
||||
management: '管理',
|
||||
testing: '测试',
|
||||
business: '商务',
|
||||
marketing: '营销',
|
||||
};
|
||||
|
||||
/**
|
||||
* SkillCard - 技能卡片组件
|
||||
*/
|
||||
export function SkillCard({
|
||||
skill,
|
||||
isSelected = false,
|
||||
onClick,
|
||||
onToggleInstall,
|
||||
onShowMore,
|
||||
}: SkillCardProps) {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const categoryConfig = CATEGORY_CONFIG[skill.category] || {
|
||||
color: 'text-gray-600 dark:text-gray-400',
|
||||
bgColor: 'bg-gray-100 dark:bg-gray-800/30',
|
||||
};
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
whileHover={{ scale: 1.02 }}
|
||||
onHoverStart={() => setIsHovered(true)}
|
||||
onHoverEnd={() => setIsHovered(false)}
|
||||
onClick={onClick}
|
||||
className={`
|
||||
relative p-4 rounded-lg border cursor-pointer transition-all duration-200
|
||||
${isSelected
|
||||
? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20'
|
||||
: 'border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 hover:border-gray-300 dark:hover:border-gray-600'
|
||||
}
|
||||
`}
|
||||
>
|
||||
{/* 顶部:图标和名称 */}
|
||||
<div className="flex items-start justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className={`w-10 h-10 rounded-lg flex items-center justify-center ${categoryConfig.bgColor}`}
|
||||
>
|
||||
<Package className={`w-5 h-5 ${categoryConfig.color}`} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-gray-900 dark:text-gray-100">
|
||||
{skill.name}
|
||||
</h3>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{skill.author || '官方'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 安装按钮 */}
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.1 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onToggleInstall?.();
|
||||
}}
|
||||
className={`
|
||||
px-3 py-1.5 rounded-lg text-xs font-medium transition-all duration-200
|
||||
${skill.installed
|
||||
? 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-600'
|
||||
: 'bg-blue-500 text-white hover:bg-blue-600'
|
||||
}
|
||||
`}
|
||||
>
|
||||
{skill.installed ? (
|
||||
<span className="flex items-center gap-1">
|
||||
<Check className="w-3 h-3" />
|
||||
已安装
|
||||
</span>
|
||||
) : (
|
||||
<span className="flex items-center gap-1">
|
||||
<Package className="w-3 h-3" />
|
||||
安装
|
||||
</span>
|
||||
)}
|
||||
</motion.button>
|
||||
</div>
|
||||
|
||||
{/* 描述 */}
|
||||
<p className="text-xs text-gray-600 dark:text-gray-300 mb-3 line-clamp-2">
|
||||
{skill.description}
|
||||
</p>
|
||||
|
||||
{/* 标签和能力 */}
|
||||
<div className="flex flex-wrap gap-1 mb-3">
|
||||
{skill.capabilities.slice(0, 3).map((cap) => (
|
||||
<span
|
||||
key={cap}
|
||||
className="px-2 py-0.5 rounded text-xs bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400"
|
||||
>
|
||||
{cap}
|
||||
</span>
|
||||
))}
|
||||
{skill.capabilities.length > 3 && (
|
||||
<span className="px-2 py-0.5 rounded text-xs bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400">
|
||||
+{skill.capabilities.length - 3}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 底部:分类、评分和统计 */}
|
||||
<div className="flex items-center justify-between pt-2 border-t border-gray-100 dark:border-gray-700">
|
||||
<span
|
||||
className={`px-2 py-0.5 rounded text-xs ${categoryConfig.bgColor} ${categoryConfig.color}`}
|
||||
>
|
||||
{CATEGORY_NAMES[skill.category] || skill.category}
|
||||
</span>
|
||||
|
||||
<div className="flex items-center gap-3 text-xs text-gray-500 dark:text-gray-400">
|
||||
{skill.rating !== undefined && (
|
||||
<span className="flex items-center gap-1">
|
||||
<Star className="w-3 h-3 text-yellow-500 fill-current" />
|
||||
{skill.rating.toFixed(1)}
|
||||
</span>
|
||||
)}
|
||||
{skill.reviewCount !== undefined && skill.reviewCount > 0 && (
|
||||
<span>{skill.reviewCount} 评价</span>
|
||||
)}
|
||||
{skill.installedAt && (
|
||||
<span className="flex items-center gap-1">
|
||||
<Clock className="w-3 h-3" />
|
||||
{new Date(skill.installedAt).toLocaleDateString()}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 悬停时显示更多按钮 */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: isHovered ? 1 : 0 }}
|
||||
className="absolute top-2 right-2"
|
||||
>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onShowMore?.();
|
||||
}}
|
||||
className="p-1.5 rounded-lg bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors"
|
||||
title="更多操作"
|
||||
>
|
||||
<MoreHorizontal className="w-4 h-4" />
|
||||
</button>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SkillCard;
|
||||
Reference in New Issue
Block a user