import { CheckIcon, InfoIcon, ShieldCheckIcon, XIcon } from "lucide-react"; import { Dialog } from "radix-ui"; import { containedPortal } from "../ui/portal-container"; import { useRef, useState, type ReactNode, type RefObject } from "react"; import { usePortalTokenStyle } from "../lib/use-portal-token-style"; import { useOpenGeniText } from "../messages/runtime"; const DialogContainedPortal = containedPortal(Dialog.Portal); export type SessionCapabilityFrameProps = { name: string; subtitle: string; logo: string | null; typeLabel: string; description: string; skill: boolean; expanded: boolean; complete: boolean; /** Completion copy must reflect the provider's actual scope of access. */ completeLabel?: string | undefined; actionLabel: string; /** Direct navigation actions should not announce a dialog on the card. */ opensDialog?: boolean; note: string; onOpen(): void; onClose(): void; busy?: boolean; opener?: RefObject; cardRef?: RefObject; children?: ReactNode; /** A catalogue row can own the opener while reusing the same details dialog. */ dialogOnly?: boolean; /** * Replaces the action when the viewer cannot start setup here (the provider * is not configured, or someone else must connect it). Say why and who can. */ actionUnavailable?: ReactNode; /** Host-owned content inside the card, under its status or actions. */ details?: ReactNode; /** Protected setup can stay in the conversation instead of opening a dialog. */ inlineSetup?: ReactNode; }; function Mark({ src, name }: { src: string | null; name: string }) { const [failed, setFailed] = useState(null); return ( {src && failed !== src ? ( setFailed(src)} /> ) : ( name.trim().slice(0, 2).toUpperCase() )} ); } /** The same conversation card in the console and embedded clients. Setup * content owns the provider flow; this frame owns identity and protected focus. */ export function SessionCapabilityFrame({ name, subtitle, logo, typeLabel, description, skill, expanded, complete, completeLabel, actionLabel, opensDialog = true, note, onOpen, onClose, busy = false, opener, cardRef, children, dialogOnly = false, actionUnavailable, details, inlineSetup, }: SessionCapabilityFrameProps) { const t = useOpenGeniText(); const localOpener = useRef(null); const localCard = useRef(null); const buttonRef = opener ?? localOpener; const sectionRef = cardRef ?? localCard; const portalStyle = usePortalTokenStyle(sectionRef.current); const identity = ( <>

{name}

{subtitle}
{typeLabel} ); return (
{!complete && !dialogOnly && note ? (

{note}

) : null} { if (!open && !busy) onClose(); }} > { if (busy) event.preventDefault(); }} onInteractOutside={(event) => { if (busy) event.preventDefault(); }} onCloseAutoFocus={(event) => { event.preventDefault(); const target = buttonRef.current?.isConnected ? buttonRef.current : sectionRef.current; if (target?.isConnected) target.focus(); }} >
{name} {typeLabel} {subtitle ? ` ยท ${subtitle}` : ""}
{!busy ? ( ) : null}
{children}
); }