import { useState } from "react"; import { pluginMcpUnavailableReason, type PluginDiscoveryItem } from "@opengeni/sdk"; import { BoxesIcon } from "lucide-react"; import { Markdown } from "./components/markdown"; import { ConnectionLogo } from "./connection-logo"; import { useOpenGeniText, type OpenGeniText } from "./messages/runtime"; const componentLabels = (t: OpenGeniText): Record => ({ skills: t("pluginDetails.skills"), mcp: t("pluginDetails.mcpConnections"), apps: t("pluginDetails.hostedApps"), hooks: t("pluginDetails.hooks"), agents: t("pluginDetails.agents"), commands: t("pluginDetails.commands"), lsp: t("pluginDetails.languageServers"), }); const EMPTY_CONNECTIONS: Record = {}; /** Embeddable plugin overview. Hosts own the dialog and installation actions. */ export function PluginDetails({ item, onInstall, busy = false, installed = false, error, connections = EMPTY_CONNECTIONS, onConnect, }: { item: PluginDiscoveryItem; onInstall?: () => void; busy?: boolean; installed?: boolean; error?: string | null; connections?: Record; onConnect?: (server: { name: string; endpoint: string | null }) => void; }) { const t = useOpenGeniText(); const supported = (server: NonNullable[number]) => pluginMcpUnavailableReason(server) === null; const contentsUnknown = item.skills == null || item.mcpServers == null; const installableCount = (item.skills?.length ?? 0) + (item.mcpServers?.filter(supported).length ?? 0); const exceedsInstallLimit = installableCount > 64; const installable = !contentsUnknown && installableCount > 0 && !exceedsInstallLimit; const [expanded, setExpanded] = useState(false); const description = item.longDescription || item.description; const lengthy = description.length > 700; const registryLabel = item.provider === "openai" ? t("pluginDetails.openaiPluginRegistry") : item.provider === "anthropic" ? t("pluginDetails.anthropicPluginRegistry") : t("pluginDetails.customSource"); return (
{registryLabel} {item.category ? {item.category} : null} {item.version ? v{item.version} : null}

{t("pluginDetails.skills")} {item.skills?.length ?? "—"}

{item.skills?.length ? ( ) : (

{item.skills ? t("pluginDetails.noBundledSkills") : t("pluginDetails.contentsHavenTBeenIndexed")}

)}

{t("pluginDetails.mcpServers")} {item.mcpServers?.length ?? "—"}

{item.mcpServers?.length ? (
    {item.mcpServers.map((server) => { const connected = Boolean(server.endpoint && connections[server.endpoint]); // Existing connections remain manageable regardless of the source manifest's setup requirements. const unavailableReason = connected ? null : pluginMcpUnavailableReason(server); return (
  • {server.name} {!unavailableReason && onConnect ? ( ) : null}
    {connected ? `${t("pluginDetails.connectedPrefix")} ` : ""} {unavailableReason ?? server.endpoint}
  • ); })}
) : (

{item.mcpServers ? t("pluginDetails.noBundledMcpServers") : t("pluginDetails.serverDetailsHavenTBeen")}

)} {item.components?.some((component) => !["skills", "mcp"].includes(component)) ? (
{t("pluginDetails.notInstalledHere")}{" "} {item.components .filter((component) => !["skills", "mcp"].includes(component)) .map((component) => componentLabels(t)[component] ?? component) .join(", ")}
) : null}
{description}
{lengthy ? ( ) : null}
); }