/** * @file MdItemList.tsx * @description Generic list + card for markdown-frontmatter config artifacts * (skills, agents, commands, output styles). Tool-agnostic: callers pass the * items and the open/edit/delete handlers, nothing here reaches for an API. * @author Nguyễn Ngọc Trí Vĩ */ import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { ExternalLink, Pencil, Trash2 } from "lucide-react"; import type { CcArtifactType, CcMdItem } from "../../lib/api"; import { ScopeBadge } from "./ScopeBadge"; import { Empty, SkeletonRows } from "./configPrimitives"; interface MdItemListProps { items: CcMdItem[] | null; search: string; onOpen: (path: string) => void; onEdit: ( type: CcArtifactType, item: { scope: "user" | "project"; name: string; filePath: string } ) => void; onDelete: ( type: CcArtifactType, scope: "user" | "project", name: string | undefined, path: string ) => void; kind: "skills" | "agents" | "commands" | "outputStyles"; } export function MdItemList({ items, search, onOpen, onEdit, onDelete, kind }: MdItemListProps) { const filtered = useMemo(() => { if (!items) return null; const q = search.toLowerCase(); return items.filter((it) => { if (!q) return true; const blob = [it.name, it.frontmatter.description, it.frontmatter.name] .filter(Boolean) .join(" ") .toLowerCase(); return blob.includes(q); }); }, [items, search]); if (!filtered) return ; if (filtered.length === 0) return ; return (
{filtered.map((it) => ( ))}
); } interface MdItemCardProps { item: CcMdItem; onOpen: (p: string) => void; onEdit: ( type: CcArtifactType, item: { scope: "user" | "project"; name: string; filePath: string } ) => void; onDelete: ( type: CcArtifactType, scope: "user" | "project", name: string | undefined, path: string ) => void; kind: "skills" | "agents" | "commands" | "outputStyles"; } function MdItemCard({ item, onOpen, onEdit, onDelete, kind }: MdItemCardProps) { const { t } = useTranslation("ccConfig"); const artifactType: CcArtifactType = kind === "outputStyles" ? "output-styles" : kind; const filePath = item.file || `${item.path}/SKILL.md`; const description = item.frontmatter.description || item.preview .replace(/^#+\s.*\n/, "") .trim() .slice(0, 200); return (
{item.name} {item.frontmatter.model && ( {item.frontmatter.model} )}
{description && (

{description}

)} {kind === "agents" && item.frontmatter.tools && (
{t("agents.tools")}:{" "} {item.frontmatter.tools}
)}
{filePath}
); }