import { useEffect, useState } from 'react'; import { api } from '@renderer/api'; import { CodeBlockViewer } from '@renderer/components/chat/viewers/CodeBlockViewer'; import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@renderer/components/ui/alert-dialog'; import { Badge } from '@renderer/components/ui/badge'; import { Button } from '@renderer/components/ui/button'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@renderer/components/ui/dialog'; import { useStore } from '@renderer/store'; import { AlertTriangle, ExternalLink, FolderOpen, Pencil, Trash2 } from 'lucide-react'; interface SkillDetailDialogProps { skillId: string | null; open: boolean; onClose: () => void; projectPath: string | null; onEdit: () => void; onDeleted: () => void; } export const SkillDetailDialog = ({ skillId, open, onClose, projectPath, onEdit, onDeleted, }: SkillDetailDialogProps): React.JSX.Element => { const fetchSkillDetail = useStore((s) => s.fetchSkillDetail); const deleteSkill = useStore((s) => s.deleteSkill); const detail = useStore((s) => (skillId ? s.skillsDetailsById[skillId] : undefined)); const loading = useStore((s) => skillId ? (s.skillsDetailLoadingById[skillId] ?? false) : false ); const detailError = useStore((s) => skillId ? (s.skillsDetailErrorById[skillId] ?? null) : null ); const [deleteLoading, setDeleteLoading] = useState(false); const [deleteError, setDeleteError] = useState(null); const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false); useEffect(() => { if (!open || !skillId) return; void fetchSkillDetail(skillId, projectPath ?? undefined).catch(() => undefined); }, [fetchSkillDetail, open, projectPath, skillId]); useEffect(() => { if (!open) { setDeleteError(null); setDeleteLoading(false); setDeleteConfirmOpen(false); } }, [open]); const item = detail?.item; function formatRootKind(rootKind: 'claude' | 'cursor' | 'agents'): string { return `.${rootKind}`; } function formatScopeLabel(scope: 'user' | 'project'): string { return scope === 'project' ? 'This project only' : 'Your personal skills'; } function formatInvocationLabel(invocationMode: 'auto' | 'manual-only'): string { return invocationMode === 'manual-only' ? 'Claude will only use this when you explicitly ask for it.' : 'Claude can pick this automatically when it matches the task.'; } async function handleDelete(): Promise { if (!item) return; setDeleteLoading(true); setDeleteError(null); try { await deleteSkill({ skillId: item.id, projectPath: projectPath ?? undefined, }); setDeleteConfirmOpen(false); onDeleted(); } catch (error) { setDeleteError(error instanceof Error ? error.message : 'Failed to delete skill'); } finally { setDeleteLoading(false); } } return ( !next && onClose()}> {item?.name ?? 'Skill details'} {item?.description ?? 'Inspect discovered skill metadata and raw instructions.'} {(loading || (open && skillId && detail === undefined)) && (

Loading skill details...

)} {!loading && detailError && (

{detailError}

{skillId && ( )}
)} {!loading && !detailError && detail === null && (
Unable to load this skill.
)} {!loading && detail && item && (
{deleteError && (
{deleteError}
)}
{formatScopeLabel(item.scope)} Stored in {formatRootKind(item.rootKind)} {item.invocationMode === 'manual-only' ? 'Manual use' : 'Auto use'} {item.flags.hasScripts && Has scripts} {item.flags.hasReferences && References} {item.flags.hasAssets && Assets}
{item.issues.length > 0 && (

Review this skill carefully before using it

{item.issues.map((issue, index) => (
{issue.message}
))}
)}

Who can use it

{formatScopeLabel(item.scope)}

How Claude uses it

{formatInvocationLabel(item.invocationMode)}

What comes with it

{[ item.flags.hasReferences ? 'references' : null, item.flags.hasScripts ? 'scripts' : null, item.flags.hasAssets ? 'assets' : null, ] .filter(Boolean) .join(', ') || 'Just the skill instructions'}

Stored at

{item.skillDir}

{detail.scriptFiles.length > 0 && (

Scripts

{detail.scriptFiles.map((file) => (

{file}

))}
)} {detail.referencesFiles.length > 0 && (

References

{detail.referencesFiles.map((file) => (

{file}

))}
)} {detail.assetFiles.length > 0 && (

Assets

{detail.assetFiles.map((file) => (

{file}

))}
)}
Advanced file details
)}
Delete skill? {item ? `Delete "${item.name}" and move it to Trash? You can restore it later from Trash if needed.` : 'Delete this skill and move it to Trash?'} Cancel void handleDelete()} disabled={deleteLoading}> {deleteLoading ? 'Deleting...' : 'Delete Skill'}
); };