{"version":3,"file":"useMermaidCdn.mjs","names":[],"sources":["../../src/hooks/useMermaidCdn.ts"],"sourcesContent":["'use client';\n\ninterface CdnMermaidConfig {\n  fontFamily?: string;\n  gantt?: { useWidth?: number };\n  securityLevel?: string;\n  startOnLoad?: boolean;\n  theme?: string;\n  themeVariables?: Record<string, string | undefined>;\n}\n\ninterface CdnMermaidApi {\n  initialize: (config: CdnMermaidConfig) => void;\n  render: (id: string, content: string) => Promise<{ svg: string }>;\n}\n\nconst DEFAULT_MERMAID_CDN_URL = 'https://esm.sh/mermaid@11';\n\nlet mermaidCdnUrl = DEFAULT_MERMAID_CDN_URL;\nlet mermaidPromise: Promise<CdnMermaidApi | null> | null = null;\n\n/**\n * Point the fallback loader at a mirror, or at a self-hosted copy for offline\n * and strict-CSP deployments. Resets any in-flight load.\n */\nexport const setMermaidCdnUrl = (url: string) => {\n  if (url === mermaidCdnUrl) return;\n  mermaidCdnUrl = url;\n  mermaidPromise = null;\n};\n\nexport const getMermaidCdnUrl = () => mermaidCdnUrl;\n\nexport const loadCdnMermaid = (): Promise<CdnMermaidApi | null> => {\n  if (typeof window === 'undefined') return Promise.resolve(null);\n\n  mermaidPromise ??= import(/* @vite-ignore */ mermaidCdnUrl)\n    .then((mod) => (mod.default ?? mod) as CdnMermaidApi)\n    .catch(() => null);\n\n  return mermaidPromise;\n};\n\n/**\n * Upstream mermaid runs colour maths over themeVariables, so it needs literal\n * values rather than var() references. Reading antd's CSS variables here keeps\n * the cost off the render path — no theme context subscription, and it only\n * runs for the diagram types that actually fall back to the CDN.\n */\nconst readCssVars = <T extends string>(names: Record<T, string>): Record<T, string | undefined> => {\n  const result = {} as Record<T, string | undefined>;\n  if (typeof document === 'undefined') return result;\n\n  const computed = globalThis.getComputedStyle(document.documentElement);\n  for (const [key, variable] of Object.entries(names) as [T, string][]) {\n    result[key] = computed.getPropertyValue(variable).trim() || undefined;\n  }\n  return result;\n};\n\nexport const createCdnMermaidConfig = (\n  isDarkMode: boolean,\n  customTheme?: string,\n): CdnMermaidConfig => {\n  const token = readCssVars({\n    border: '--ant-color-border',\n    error: '--ant-color-error',\n    fontFamily: '--ant-font-family',\n    fontFamilyCode: '--ant-font-family-code',\n    infoBg: '--ant-color-info-bg',\n    infoBorder: '--ant-color-info-border',\n    infoText: '--ant-color-info-text',\n    primary: '--ant-color-primary',\n    success: '--ant-color-success',\n    successBg: '--ant-color-success-bg',\n    successBorder: '--ant-color-success-border',\n    successText: '--ant-color-success-text',\n    text: '--ant-color-text',\n    textDescription: '--ant-color-text-description',\n    textSecondary: '--ant-color-text-secondary',\n    bgContainer: '--ant-color-bg-container',\n    warning: '--ant-color-warning',\n  });\n\n  return {\n    fontFamily: token.fontFamilyCode,\n    gantt: { useWidth: 1920 },\n    // SECURITY: 'strict' keeps Mermaid from rendering node labels via innerHTML,\n    // which would be an XSS vector for model-generated diagram content.\n    securityLevel: 'strict',\n    startOnLoad: false,\n    theme: customTheme || (isDarkMode ? 'dark' : 'neutral'),\n    themeVariables: customTheme\n      ? undefined\n      : {\n          errorBkgColor: token.textDescription,\n          errorTextColor: token.textDescription,\n          fontFamily: token.fontFamily,\n          lineColor: token.textSecondary,\n          mainBkg: token.bgContainer,\n          noteBkgColor: token.infoBg,\n          noteTextColor: token.infoText,\n          pie1: token.primary,\n          pie2: token.warning,\n          pie3: token.success,\n          pie4: token.error,\n          primaryBorderColor: token.border,\n          primaryColor: token.bgContainer,\n          primaryTextColor: token.text,\n          secondaryBorderColor: token.infoBorder,\n          secondaryColor: token.infoBg,\n          secondaryTextColor: token.infoText,\n          tertiaryBorderColor: token.successBorder,\n          tertiaryColor: token.successBg,\n          tertiaryTextColor: token.successText,\n          textColor: token.text,\n        },\n  };\n};\n\nexport const renderWithCdnMermaid = async (\n  content: string,\n  id: string,\n  config: CdnMermaidConfig,\n): Promise<string> => {\n  const mermaid = await loadCdnMermaid();\n  if (!mermaid) return '';\n\n  mermaid.initialize(config);\n  const { svg } = await mermaid.render(id, content);\n  return svg;\n};\n"],"mappings":";;AAkBA,IAAI,gBAAgB;AACpB,IAAI,iBAAuD;;;;;AAM3D,MAAa,oBAAoB,QAAgB;CAC/C,IAAI,QAAQ,eAAe;CAC3B,gBAAgB;CAChB,iBAAiB;AACnB;AAEA,MAAa,yBAAyB;AAEtC,MAAa,uBAAsD;CACjE,IAAI,OAAO,WAAW,aAAa,OAAO,QAAQ,QAAQ,IAAI;CAE9D,mBAAmB;;EAA0B;CAAc,CACxD,MAAM,QAAS,IAAI,WAAW,GAAqB,CAAC,CACpD,YAAY,IAAI;CAEnB,OAAO;AACT;;;;;;;AAQA,MAAM,eAAiC,UAA4D;CACjG,MAAM,SAAS,CAAC;CAChB,IAAI,OAAO,aAAa,aAAa,OAAO;CAE5C,MAAM,WAAW,WAAW,iBAAiB,SAAS,eAAe;CACrE,KAAK,MAAM,CAAC,KAAK,aAAa,OAAO,QAAQ,KAAK,GAChD,OAAO,OAAO,SAAS,iBAAiB,QAAQ,CAAC,CAAC,KAAK,KAAK,KAAA;CAE9D,OAAO;AACT;AAEA,MAAa,0BACX,YACA,gBACqB;CACrB,MAAM,QAAQ,YAAY;EACxB,QAAQ;EACR,OAAO;EACP,YAAY;EACZ,gBAAgB;EAChB,QAAQ;EACR,YAAY;EACZ,UAAU;EACV,SAAS;EACT,SAAS;EACT,WAAW;EACX,eAAe;EACf,aAAa;EACb,MAAM;EACN,iBAAiB;EACjB,eAAe;EACf,aAAa;EACb,SAAS;CACX,CAAC;CAED,OAAO;EACL,YAAY,MAAM;EAClB,OAAO,EAAE,UAAU,KAAK;EAGxB,eAAe;EACf,aAAa;EACb,OAAO,gBAAgB,aAAa,SAAS;EAC7C,gBAAgB,cACZ,KAAA,IACA;GACE,eAAe,MAAM;GACrB,gBAAgB,MAAM;GACtB,YAAY,MAAM;GAClB,WAAW,MAAM;GACjB,SAAS,MAAM;GACf,cAAc,MAAM;GACpB,eAAe,MAAM;GACrB,MAAM,MAAM;GACZ,MAAM,MAAM;GACZ,MAAM,MAAM;GACZ,MAAM,MAAM;GACZ,oBAAoB,MAAM;GAC1B,cAAc,MAAM;GACpB,kBAAkB,MAAM;GACxB,sBAAsB,MAAM;GAC5B,gBAAgB,MAAM;GACtB,oBAAoB,MAAM;GAC1B,qBAAqB,MAAM;GAC3B,eAAe,MAAM;GACrB,mBAAmB,MAAM;GACzB,WAAW,MAAM;EACnB;CACN;AACF;AAEA,MAAa,uBAAuB,OAClC,SACA,IACA,WACoB;CACpB,MAAM,UAAU,MAAM,eAAe;CACrC,IAAI,CAAC,SAAS,OAAO;CAErB,QAAQ,WAAW,MAAM;CACzB,MAAM,EAAE,QAAQ,MAAM,QAAQ,OAAO,IAAI,OAAO;CAChD,OAAO;AACT"}