"use client"; import { useEffect } from "react"; import type { SkillStatusEntry } from "@/lib/skills/types"; import { buildSkillMissingDetails, canRemoveSkill, deriveSkillReadinessState, resolvePreferredInstallOption, } from "@/lib/skills/presentation"; type SkillSetupMessage = { kind: "success" | "error"; message: string }; type AgentSkillsSetupModalProps = { skill: SkillStatusEntry | null; skillsBusy: boolean; skillsBusyKey: string | null; skillMessage: SkillSetupMessage | null; apiKeyDraft: string; defaultAgentScopeWarning?: string | null; onClose: () => void; onInstallSkill: (skillKey: string, name: string, installId: string) => Promise | void; onSetSkillGlobalEnabled: (skillKey: string, enabled: boolean) => Promise | void; onRemoveSkill: ( skill: { skillKey: string; source: string; baseDir: string } ) => Promise | void; onSkillApiKeyChange: (skillKey: string, value: string) => Promise | void; onSaveSkillApiKey: (skillKey: string) => Promise | void; }; const READINESS_LABELS = { ready: "Ready", "needs-setup": "Needs setup", unavailable: "Unavailable", "disabled-globally": "Disabled globally", } as const; const READINESS_CLASSES = { ready: "ui-badge-status-running", "needs-setup": "ui-badge-status-error", unavailable: "ui-badge-status-error", "disabled-globally": "ui-badge-status-error", } as const; export const AgentSkillsSetupModal = ({ skill, skillsBusy, skillsBusyKey, skillMessage, apiKeyDraft, defaultAgentScopeWarning = null, onClose, onInstallSkill, onSetSkillGlobalEnabled, onRemoveSkill, onSkillApiKeyChange, onSaveSkillApiKey, }: AgentSkillsSetupModalProps) => { useEffect(() => { if (!skill) { return; } const handleKeyDown = (event: KeyboardEvent) => { if (event.key !== "Escape") { return; } event.preventDefault(); onClose(); }; window.addEventListener("keydown", handleKeyDown); return () => { window.removeEventListener("keydown", handleKeyDown); }; }, [onClose, skill]); if (!skill) { return null; } const readiness = deriveSkillReadinessState(skill); const readinessLabel = READINESS_LABELS[readiness]; const readinessClassName = READINESS_CLASSES[readiness]; const missingDetails = buildSkillMissingDetails(skill); const installOption = resolvePreferredInstallOption(skill); const canDeleteSkill = canRemoveSkill(skill); const busyForSkill = skillsBusyKey === skill.skillKey; const anySkillBusy = skillsBusy || Boolean(skillsBusyKey); const trimmedApiKey = apiKeyDraft.trim(); return (
event.stopPropagation()} >
System setup
{skill.name} {readinessLabel}
Changes affect all agents on this gateway.
{defaultAgentScopeWarning ? (
{defaultAgentScopeWarning}
) : null}
{skill.description}
{skill.blockedByAllowlist ? (
Blocked by bundled skills policy (`skills.allowBundled`).
) : null} {missingDetails.map((line) => (
{line}
))} {skillMessage ? (
{skillMessage.message}
) : null}
{installOption ? ( ) : null} {skill.primaryEnv ? ( <> { void onSkillApiKeyChange(skill.skillKey, event.target.value); }} disabled={anySkillBusy} className="w-full rounded-md border border-border/60 bg-surface-1 px-3 py-2 text-[10px] text-foreground outline-none transition focus:border-border" placeholder={`Set ${skill.primaryEnv}`} aria-label={`API key for ${skill.name}`} /> ) : null} {canDeleteSkill ? ( ) : null}
); };