import { SkillCard } from "@site/src/components/skill-card"; import { searchSkills, getAllTags } from "@site/src/utils/skills"; import type { Skill } from "@site/src/pages/skills/types"; import { useState, useEffect } from "react"; import { motion } from "framer-motion"; import Layout from "@theme/Layout"; import Admonition from "@theme/Admonition"; import { Button } from "@site/src/components/ui/button"; import { SidebarFilter, type SidebarFilterGroup } from "@site/src/components/ui/sidebar-filter"; import { Menu, X } from "lucide-react"; import Link from '@docusaurus/Link'; export default function SkillsPage() { const [skills, setSkills] = useState([]); const [searchQuery, setSearchQuery] = useState(""); const [selectedFilters, setSelectedFilters] = useState>({}); const [isMobileFilterOpen, setIsMobileFilterOpen] = useState(false); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [currentPage, setCurrentPage] = useState(1); const skillsPerPage = 10; // Build tag filter options from loaded skills const uniqueTags = Array.from( new Set( skills.flatMap((s) => s.tags || []) ) ).sort().map((tag) => ({ label: tag.charAt(0).toUpperCase() + tag.slice(1), value: tag })); // Build source filter options (Community only - official is the default) const sourceOptions = [ { label: "Community", value: "community" } ]; const sidebarFilterGroups: SidebarFilterGroup[] = [ { title: "Source", options: sourceOptions }, { title: "Tags", options: uniqueTags, maxHeight: "max-h-64 overflow-y-auto" } ]; useEffect(() => { const loadSkills = async () => { try { setIsLoading(true); setError(null); const results = await searchSkills(searchQuery); setSkills(results); } catch (err) { const errorMessage = err instanceof Error ? err.message : "Unknown error"; setError(`Failed to load skills: ${errorMessage}`); console.error("Error loading skills:", err); } finally { setIsLoading(false); } }; const timeoutId = setTimeout(loadSkills, 300); return () => clearTimeout(timeoutId); }, [searchQuery]); // Apply filters let filteredSkills = skills; Object.entries(selectedFilters).forEach(([group, values]) => { if (values.length > 0) { filteredSkills = filteredSkills.filter((skill) => { if (group === "Tags") { return skill.tags?.some((tag) => values.includes(tag)) ?? false; } if (group === "Source") { // Use isCommunity field from manifest (true if author is not "goose") const isCommunity = skill.isCommunity ?? false; if (values.includes("community")) return isCommunity; return true; } return true; }); } }); return (

Skills Marketplace

Browse community-contributed{" "} skills {" "} that teach goose how to perform specific tasks. Skills are reusable instruction sets with optional supporting files.

{ setSearchQuery(e.target.value); setCurrentPage(1); }} />
{ setSelectedFilters(prev => ({ ...prev, [group]: values })); setCurrentPage(1); }} />

{searchQuery ? `${filteredSkills.length} result${filteredSkills.length !== 1 ? "s" : ""} for "${searchQuery}"` : `${filteredSkills.length} skill${filteredSkills.length !== 1 ? "s" : ""} available`}

{error && (

{error}

)} {isLoading ? (
Loading skills...
) : filteredSkills.length === 0 ? (

{searchQuery ? "No skills found matching your search." : "No skills have been submitted yet."}

) : ( <>
{filteredSkills .slice((currentPage - 1) * skillsPerPage, currentPage * skillsPerPage) .map((skill) => ( ))}
{filteredSkills.length > skillsPerPage && (
Page {currentPage} of {Math.ceil(filteredSkills.length / skillsPerPage)}
)} )}
); }