import React, { useState, useMemo, useCallback, useRef, useEffect } from "react"; import Layout from "@theme/Layout"; import skills from "../../data/skills.json"; import styles from "./styles.module.css"; interface Skill { name: string; description: string; category: string; categoryLabel: string; source: string; tags: string[]; platforms: string[]; author: string; version: string; } const allSkills: Skill[] = skills as Skill[]; const CATEGORY_ICONS: Record = { apple: "\u{f179}", "autonomous-ai-agents": "\u{1F916}", blockchain: "\u{26D3}", communication: "\u{1F4AC}", creative: "\u{1F3A8}", "data-science": "\u{1F4CA}", devops: "\u{2699}", dogfood: "\u{1F436}", domain: "\u{1F310}", email: "\u{2709}", feeds: "\u{1F4E1}", gaming: "\u{1F3AE}", gifs: "\u{1F3AC}", github: "\u{1F4BB}", health: "\u{2764}", "inference-sh": "\u{26A1}", leisure: "\u{2615}", mcp: "\u{1F50C}", media: "\u{1F3B5}", migration: "\u{1F4E6}", mlops: "\u{1F9EA}", "note-taking": "\u{1F4DD}", productivity: "\u{2705}", "red-teaming": "\u{1F6E1}", research: "\u{1F50D}", security: "\u{1F512}", "smart-home": "\u{1F3E0}", "social-media": "\u{1F4F1}", "software-development": "\u{1F4BB}", translation: "\u{1F30D}", other: "\u{1F4E6}", }; const SOURCE_CONFIG: Record< string, { label: string; color: string; bg: string; border: string; icon: string } > = { "built-in": { label: "Built-in", color: "#4ade80", bg: "rgba(74, 222, 128, 0.08)", border: "rgba(74, 222, 128, 0.2)", icon: "\u{2713}", }, optional: { label: "Optional", color: "#fbbf24", bg: "rgba(251, 191, 36, 0.08)", border: "rgba(251, 191, 36, 0.2)", icon: "\u{2B50}", }, Anthropic: { label: "Anthropic", color: "#d4845a", bg: "rgba(212, 132, 90, 0.08)", border: "rgba(212, 132, 90, 0.2)", icon: "\u{25C6}", }, LobeHub: { label: "LobeHub", color: "#60a5fa", bg: "rgba(96, 165, 250, 0.08)", border: "rgba(96, 165, 250, 0.2)", icon: "\u{25CB}", }, "Claude Marketplace": { label: "Marketplace", color: "#a78bfa", bg: "rgba(167, 139, 250, 0.08)", border: "rgba(167, 139, 250, 0.2)", icon: "\u{25A0}", }, }; const SOURCE_ORDER = ["all", "built-in", "optional", "Anthropic", "LobeHub", "Claude Marketplace"]; function highlightMatch(text: string, query: string): React.ReactNode { if (!query || !text) return text; const idx = text.toLowerCase().indexOf(query.toLowerCase()); if (idx === -1) return text; return ( <> {text.slice(0, idx)} {text.slice(idx, idx + query.length)} {text.slice(idx + query.length)} ); } function SkillCard({ skill, query, expanded, onToggle, onCategoryClick, onTagClick, style, }: { skill: Skill; query: string; expanded: boolean; onToggle: () => void; onCategoryClick: (cat: string) => void; onTagClick: (tag: string) => void; style?: React.CSSProperties; }) { const src = SOURCE_CONFIG[skill.source] || SOURCE_CONFIG["optional"]; const icon = CATEGORY_ICONS[skill.category] || "\u{1F4E6}"; return (
{icon}

{highlightMatch(skill.name, query)}

{src.icon} {src.label}

{highlightMatch(skill.description || "No description available.", query)}

{skill.platforms?.map((p) => ( {p === "macos" ? "\u{F8FF} macOS" : p === "linux" ? "\u{1F427} Linux" : p} ))}
{expanded && (
{skill.tags?.length > 0 && (
{skill.tags.map((tag) => ( ))}
)} {skill.author && (
Author {skill.author}
)} {skill.version && (
Version {skill.version}
)}
hermes skills install {skill.name}
)}
); } function StatCard({ value, label, color }: { value: number; label: string; color: string }) { return (
{value} {label}
); } const PAGE_SIZE = 60; export default function SkillsDashboard() { const [search, setSearch] = useState(""); const [sourceFilter, setSourceFilter] = useState("all"); const [categoryFilter, setCategoryFilter] = useState("all"); const [expandedCard, setExpandedCard] = useState(null); const [visibleCount, setVisibleCount] = useState(PAGE_SIZE); const [sidebarOpen, setSidebarOpen] = useState(false); const searchRef = useRef(null); const gridRef = useRef(null); useEffect(() => { const handler = (e: KeyboardEvent) => { if (e.key === "/" && document.activeElement?.tagName !== "INPUT") { e.preventDefault(); searchRef.current?.focus(); } if (e.key === "Escape") { searchRef.current?.blur(); setExpandedCard(null); } }; window.addEventListener("keydown", handler); return () => window.removeEventListener("keydown", handler); }, []); const sources = useMemo(() => { const set = new Set(allSkills.map((s) => s.source)); return SOURCE_ORDER.filter((s) => s === "all" || set.has(s)); }, []); const categoryEntries = useMemo(() => { const pool = sourceFilter === "all" ? allSkills : allSkills.filter((s) => s.source === sourceFilter); const map = new Map(); for (const s of pool) { const key = s.category || "uncategorized"; const existing = map.get(key); if (existing) { existing.count++; } else { map.set(key, { label: s.categoryLabel || s.category || "Uncategorized", count: 1, }); } } return Array.from(map.entries()) .sort((a, b) => b[1].count - a[1].count) .map(([key, { label, count }]) => ({ key, label, count })); }, [sourceFilter]); const filtered = useMemo(() => { const q = search.toLowerCase().trim(); return allSkills.filter((s) => { if (sourceFilter !== "all" && s.source !== sourceFilter) return false; if (categoryFilter !== "all" && s.category !== categoryFilter) return false; if (q) { const haystack = [s.name, s.description, s.categoryLabel, s.author, ...(s.tags || [])] .join(" ") .toLowerCase(); return haystack.includes(q); } return true; }); }, [search, sourceFilter, categoryFilter]); useEffect(() => { setVisibleCount(PAGE_SIZE); setExpandedCard(null); }, [search, sourceFilter, categoryFilter]); const visible = filtered.slice(0, visibleCount); const hasMore = visibleCount < filtered.length; const handleSourceChange = useCallback( (src: string) => { setSourceFilter(src); setCategoryFilter("all"); }, [] ); const handleCategoryClick = useCallback((cat: string) => { setCategoryFilter(cat); gridRef.current?.scrollIntoView({ behavior: "smooth", block: "start" }); setSidebarOpen(false); }, []); const handleTagClick = useCallback((tag: string) => { setSearch(tag); searchRef.current?.focus(); }, []); const clearAll = useCallback(() => { setSearch(""); setSourceFilter("all"); setCategoryFilter("all"); }, []); return (

Hermes Agent

Skills Hub

Discover, search, and install from{" "} {allSkills.length} skills across {sources.length - 1} registries

s.source === "built-in").length} label="Built-in" color="#4ade80" /> s.source === "optional").length} label="Optional" color="#fbbf24" /> s.source !== "built-in" && s.source !== "optional" ).length } label="Community" color="#60a5fa" /> s.category)).size} label="Categories" color="#a78bfa" />
setSearch(e.target.value)} className={styles.searchInput} /> {search && ( )}
{sources.map((src) => { const active = sourceFilter === src; const conf = SOURCE_CONFIG[src]; const count = src === "all" ? allSkills.length : allSkills.filter((s) => s.source === src).length; return ( ); })}
{(search || sourceFilter !== "all" || categoryFilter !== "all") && (
{filtered.length} result{filtered.length !== 1 ? "s" : ""} {search && ( “{search}” )} {sourceFilter !== "all" && ( {SOURCE_CONFIG[sourceFilter]?.label || sourceFilter} )} {categoryFilter !== "all" && ( {categoryEntries.find((c) => c.key === categoryFilter)?.label || categoryFilter} )}
)} {visible.length > 0 ? ( <>
{visible.map((skill, i) => { const key = `${skill.source}-${skill.name}-${i}`; return ( setExpandedCard(expandedCard === key ? null : key) } onCategoryClick={handleCategoryClick} onTagClick={handleTagClick} style={{ animationDelay: `${Math.min(i, 20) * 25}ms` }} /> ); })}
{hasMore && (
)} ) : (
{"\u{1F50D}"}

No skills found

Try a different search term or clear your filters.

)}
{sidebarOpen && (
setSidebarOpen(false)} /> )} ); }