'use client'; import React, { useEffect, useState, useRef } from 'react'; import { Puzzle, Upload, Download, Trash2, RefreshCw, Loader2, AlertCircle, X, } from 'lucide-react'; import { listSkills, deleteSkill, uploadSkill, downloadSkill } from '@/lib/api'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import type { Skill } from '@/types'; import { pickAppText } from '@/lib/i18n/core'; import { useAppI18n } from '@/lib/i18n/provider'; export default function SkillsPage() { const { locale } = useAppI18n(); const [skills, setSkills] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [showUpload, setShowUpload] = useState(false); const [deleting, setDeleting] = useState(null); const loadSkills = async () => { setLoading(true); setError(null); try { const data = await listSkills(); setSkills(Array.isArray(data) ? data : []); } catch (err: any) { setError(err.message || pickAppText(locale, '加载技能失败', 'Failed to load skills')); } finally { setLoading(false); } }; useEffect(() => { loadSkills(); }, []); const handleDelete = async (name: string) => { setDeleting(name); }; const confirmDelete = async (name: string) => { try { await deleteSkill(name); setDeleting(null); loadSkills(); } catch (err: any) { setError(err.message || pickAppText(locale, '删除技能失败', 'Failed to delete the skill')); setDeleting(null); } }; const handleUploadDone = () => { setShowUpload(false); loadSkills(); }; if (loading) { return (
); } return (

{pickAppText(locale, '技能', 'Skills')}

{error && (
{error}
)} {/* Upload Dialog */} {showUpload && ( setShowUpload(false)} onError={(msg) => setError(msg)} /> )} {/* Delete Confirmation */} {deleting && (

{pickAppText(locale, '确定删除技能', 'Delete skill')} {deleting} {pickAppText(locale, '吗?此操作不可撤销。', '? This action cannot be undone.')}

)} {/* Skills Table */} {skills.length === 0 ? (

{pickAppText(locale, '暂无技能', 'No skills yet')}

{pickAppText(locale, '上传一个技能 zip 包即可开始使用。', 'Upload a skill zip package to get started.')}

) : ( {pickAppText(locale, '名称', 'Name')} {pickAppText(locale, '描述', 'Description')} {pickAppText(locale, '来源', 'Source')} {pickAppText(locale, '状态', 'Status')} {pickAppText(locale, '操作', 'Actions')} {skills.map((skill) => ( {skill.name} {skill.description} {skill.source === 'builtin' ? ( {pickAppText(locale, '内置', 'Built in')} ) : ( {pickAppText(locale, '工作区', 'Workspace')} )} {skill.available ? ( {pickAppText(locale, '可用', 'Available')} ) : ( {pickAppText(locale, '不可用', 'Unavailable')} )}
{skill.source === 'workspace' && ( )}
))}
)}
); } function UploadSkillForm({ onDone, onCancel, onError, }: { onDone: () => void; onCancel: () => void; onError: (msg: string) => void; }) { const { locale } = useAppI18n(); const [uploading, setUploading] = useState(false); const fileRef = useRef(null); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); const file = fileRef.current?.files?.[0]; if (!file) return; setUploading(true); try { await uploadSkill(file); onDone(); } catch (err: any) { onError(err.message || pickAppText(locale, '上传失败', 'Upload failed')); } finally { setUploading(false); } }; return (
{pickAppText(locale, '上传技能', 'Upload skill')}

{pickAppText(locale, '压缩包中必须包含 `SKILL.md` 文件', 'The archive must contain a `SKILL.md` file')}

); }