import type { ReactNode } from "react"; import { CapabilityCatalogIndicator, CapabilityCatalogRow, type CapabilityCatalogStatus, } from "./capability-catalog-row"; import { useOpenGeniText } from "./messages/runtime"; /** Status comes from the host/controller, never inferred from a display label. */ export type ConnectionCatalogOption = { id: string; name: string; description?: string | undefined; status: string; connected: boolean; /** Explicit health/availability. Omit to retain visible legacy status text. */ state?: CapabilityCatalogStatus; onOpen: () => void; action?: ReactNode; }; export type ConnectionCatalogService = { id: string; name: string; logo?: ReactNode; options: ConnectionCatalogOption[]; }; export type ConnectionCatalogProps = { services: ConnectionCatalogService[]; query?: string; columns?: 1 | 2; resultLimit?: number; onShowMore?: () => void; /** Group capabilities by service, or show every capability independently. */ grouped?: boolean; className?: string; emptyMessage?: string; }; export function ConnectionOptionRow({ option }: { option: ConnectionCatalogOption }) { return (
{option.action ? {option.action} : null}
); } export function ConnectionServiceRow({ service }: { service: ConnectionCatalogService }) { const t = useOpenGeniText(); if (!service.options.length) return null; const connected = service.options.filter((option) => option.connected).length; const attention = service.options.find( (option) => option.state === "attention" || option.state === "unavailable", ); const groupState = attention?.state ?? (service.options.some((option) => option.state === "loading") ? "loading" : connected === service.options.length ? "added" : "available"); const identity = ( <> {service.logo} {service.name} {service.options.length === 1 ? service.options[0]?.description : service.options.map((option) => option.name).join(" · ")} ); return service.options.length === 1 ? (
{service.options[0]?.action}
) : (
{identity} ⌄
{service.options.map((option) => ( ))}
); } export function ConnectionCatalog({ services, query = "", columns = 1, grouped = true, resultLimit, onShowMore, className = "", emptyMessage: emptyMessageProp, }: ConnectionCatalogProps) { const t = useOpenGeniText(); const emptyMessage = emptyMessageProp === undefined ? t("connectionCatalog.noConnectionsMatchYourSearch") : emptyMessageProp; const search = query.trim().toLowerCase(); const entries = grouped ? services : services.flatMap((service) => service.options.map((option) => ({ ...service, id: `${service.id}/${option.id}`, name: service.options.length > 1 && option.name !== service.name ? `${service.name} · ${option.name}` : service.name, options: [option], })), ); const visible = entries.filter((service) => [service.name, ...service.options.flatMap((option) => [option.name, option.description ?? ""])] .join(" ") .toLowerCase() .includes(search), ); return ( <>
{visible.length ? ( visible .slice(0, resultLimit) .map((service) => ) ) : (

{emptyMessage}

)}
{resultLimit && visible.length > resultLimit ? ( ) : null} ); }