import React, { useState } from 'react'; import { Plus, RefreshCw, Trash2, Edit2, Zap, X, Code } from 'lucide-react'; import { motion, AnimatePresence } from 'motion/react'; import { useTranslation } from 'react-i18next'; import { useAppContext } from '../context/AppContext'; import { Skill } from '../types'; const initialSkillForm: Omit = { name: '', description: '', tools: [], system_prompt: '' }; const Skills: React.FC = () => { const { t } = useTranslation(); const { skills, refreshSkills, q } = useAppContext(); const [installName, setInstallName] = useState(''); const qp = (k: string, v: string) => `${q}${q ? '&' : '?'}${k}=${encodeURIComponent(v)}`; const [isModalOpen, setIsModalOpen] = useState(false); const [editingSkill, setEditingSkill] = useState(null); const [form, setForm] = useState>(initialSkillForm); async function deleteSkill(id: string) { if (!confirm('Are you sure you want to delete this skill?')) return; try { await fetch(`/webui/api/skills${qp('id', id)}`, { method: 'DELETE' }); await refreshSkills(); } catch (e) { console.error(e); } } async function installSkill() { const name = installName.trim(); if (!name) return; const r = await fetch(`/webui/api/skills${q}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'install', name }), }); if (!r.ok) { alert(await r.text()); return; } setInstallName(''); await refreshSkills(); } async function handleSubmit() { try { const action = editingSkill ? 'update' : 'create'; const r = await fetch(`/webui/api/skills${q}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action, ...(editingSkill && { id: editingSkill.id }), ...form }) }); if (r.ok) { setIsModalOpen(false); await refreshSkills(); } else { alert(await r.text()); } } catch (e) { alert(e); } } return (

{t('skills')}

setInstallName(e.target.value)} placeholder="skill name" className="px-3 py-2 bg-zinc-950 border border-zinc-800 rounded-lg text-sm" />
{skills.map(s => (

{s.name}

ID: {s.id.slice(-6)}

{s.description || 'No description provided.'}

Tools
{(Array.isArray(s.tools) ? s.tools : []).map(tool => ( {tool} ))} {(!Array.isArray(s.tools) || s.tools.length === 0) && No tools defined}
{s.system_prompt && (
System Prompt
{s.system_prompt}
)}
))} {skills.length === 0 && (

No skills defined

)}
{isModalOpen && (
setIsModalOpen(false)} className="absolute inset-0 bg-black/60 backdrop-blur-sm" />

{editingSkill ? 'Edit Skill' : 'Add Skill'}