// Renderers for the connector namespace picker and connector catalog pages. // Styled to match the reference connector extension UI. import { CATEGORY } from "./categories.mjs"; import { buildSandboxUrl } from "./sandbox.mjs"; const CONNECT_ICON = ''; // Official Azure Connector Namespace mark — a gray viewfinder frame wrapping // two interlocking blue-gradient chain links. Path + gradient data is lifted // verbatim from the portal's ConnectorNamespaceIcon brand asset. idSuffix keeps // the gradient element IDs unique when the mark renders more than once per page. export function brandMark(size = 28, idSuffix = "m") { const g0 = `cn-g0-${idSuffix}`; const g1 = `cn-g1-${idSuffix}`; return ``; } export function baseStyles() { return ``; } // --------------------------------------------------------------------------- // Setup / Namespace Picker // --------------------------------------------------------------------------- export function renderSetupHtml(subscriptions, notice = "", capabilityToken = "") { const subOptions = subscriptions.map((s) => `` ).join(""); return ` Select Connector Namespace${baseStyles()}

${brandMark(30, "setup")}Select a Connector Namespace

Choose which connector namespace to browse. This choice is saved for future sessions.
${notice ? `
${esc(notice)}
` : ""}
Select a subscription to see available connector namespaces.
`; } // --------------------------------------------------------------------------- // Catalog // --------------------------------------------------------------------------- const CSS_HEX_COLOR = /^#[0-9a-fA-F]{6}$/; function iconBackgroundStyle(brandColor) { const color = String(brandColor || "").trim(); return CSS_HEX_COLOR.test(color) ? ` style="background:${color}22"` : ""; } export function renderCatalogHtml(instanceId, catalog, { filter, category, source, config }, capabilityToken = "") { const renderItem = (c) => { // Items carry their home grid so hydrateState can move them into // "My MCPs" when added and back to Microsoft/Partner on remove. const home = c.category === CATEGORY.microsoft ? "microsoft" : "partner"; const icon = c.iconUri ? `
` : `
${esc(c.displayName.charAt(0))}
`; // Button state is hydrated client-side from /api/state on load. const btn = ``; const haystack = esc((c.displayName + " " + (c.description || "")).toLowerCase()); const sandboxUrl = esc(buildSandboxUrl(config, c.apiName)); return `
${icon}
${esc(c.displayName)}
${esc(c.description)}
${btn}
`; }; const byName = (a, b) => a.displayName.localeCompare(b.displayName); const microsoft = catalog.filter((c) => c.category === CATEGORY.microsoft).sort(byName); const partner = catalog.filter((c) => c.category !== CATEGORY.microsoft).sort(byName); const section = (key, title, rows, { collapsed, hidden }) => { const cls = ["section", "collapsible"]; if (collapsed) cls.push("collapsed"); if (hidden) cls.push("is-hidden"); const n = rows.length; return `
` + `` + `
${rows.map(renderItem).join("")}
` + `
`; }; // Server paints the first-run layout: My MCPs hidden+empty (filled by // hydrateState), Microsoft expanded so there's something to browse, Partner // collapsed. updateSections() flips to the steady layout on the first hydrate // if anything is already added. let sectionsHtml = section("mine", "My MCPs", [], { collapsed: false, hidden: true }) + section("microsoft", "Microsoft", microsoft, { collapsed: false, hidden: microsoft.length === 0 }) + section("partner", "Partners", partner, { collapsed: true, hidden: partner.length === 0 }); if (!catalog.length) { sectionsHtml = `
No connectors available.
`; } return ` Connectors${baseStyles()}

${brandMark(24, "cat")}Connectors

Namespace ${esc(config.gatewayName)} · RG ${esc(config.resourceGroup)}
${sectionsHtml} `; } // --------------------------------------------------------------------------- // Error // --------------------------------------------------------------------------- export function renderErrorHtml(message) { return ` Error${baseStyles()}

Error

${esc(message)}
`; } // --------------------------------------------------------------------------- function esc(s) { return String(s ?? "").replace(/[&<>"']/g, (c) => ({"&":"&","<":"<",">":">",'"':""","'":"'"}[c])); }