import { useEffect, useState, type ReactNode } from "react"; import { createDiscoveryCache } from "./discovery-cache"; import { BookOpenIcon } from "lucide-react"; import { CapabilityCatalogRow, type CapabilityCatalogStatus } from "./capability-catalog-row"; import { useOpenGeniText } from "./messages/runtime"; export type SkillDiscoveryItem = { id: string; name: string; source: string; installs: number; url: string; }; export type SkillDiscoveryPage = { items: SkillDiscoveryItem[]; nextCursor: null }; const discoveryCache = createDiscoveryCache(); export type SkillDiscoveryClient = { searchPublicSkills(workspaceId: string, query: string): Promise; }; export type SkillDiscoveryProps = { installedSkills?: readonly { name: string; repositoryUrl: string; sourceUrl: string }[]; client: SkillDiscoveryClient; workspaceId: string; query: string; onImport: (url: string) => void; canManage: boolean; localSkills?: readonly { id: string; name: string; description?: string; status?: CapabilityCatalogStatus; statusLabel?: string; onOpen: () => void; }[]; resultLimit?: number; onShowMore?: () => void; onSearch?: (() => void) | undefined; /** Display name for a registry slug ("agent-browser" -> "Agent browser"). Defaults to the slug. */ formatName?: (name: string) => string; /** The line under a remote skill ("From vercel-labs on skills.sh"). Defaults to the source. */ formatSource?: (source: string) => string; /** The row tile. Defaults to a bare book glyph. */ icon?: ReactNode; }; /** Host-owned query and import flow; browse presentation is reusable independently. */ export function SkillDiscovery(props: SkillDiscoveryProps) { const t = useOpenGeniText(); const query = props.query.trim(); const discoveryQuery = query || "agent"; return (

{props.resultLimit ? t("skillDiscovery.skills") : query ? t("skillDiscovery.searchSkills") : t("skillDiscovery.browseSkills")}

{!props.resultLimit ? ( {t("skillDiscovery.browseSkillsSh")} ) : null}
{!query ? (

{t("skillDiscovery.popularOnSkillsSh")}

) : query.length < 2 ? (

{t("skillDiscovery.enterAtLeastTwoCharacters")}

) : null}
); } const keepText = (value: string) => value; const DEFAULT_ICON =