import { __name } from './chunk-CGILA3WO.mjs';
import React6, { lazy, createContext, useState, useMemo, useCallback, Suspense, useContext, useReducer, useRef, useEffect } from 'react';
import '@djangocfg/ui-core/styles/palette';
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import ReactMarkdown from 'react-markdown';
import rehypeExternalLinks from 'rehype-external-links';
import rehypeRaw from 'rehype-raw';
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
import remarkBreaks from 'remark-breaks';
import remarkEmoji from 'remark-emoji';
import remarkGfm from 'remark-gfm';
import remarkSmartypants from 'remark-smartypants';
import { CopyButton } from '@djangocfg/ui-core/components';
import { useResolvedTheme, useSessionStorage } from '@djangocfg/ui-core/hooks';
import consola2 from 'consola';
import { sample } from 'openapi-sampler';

function smartTruncate(content, maxLength) {
  if (content.length <= maxLength) {
    return content;
  }
  let breakPoint = maxLength;
  while (breakPoint > maxLength - 50 && breakPoint > 0) {
    const char = content[breakPoint];
    if (char === " " || char === "\n" || char === "	") {
      break;
    }
    breakPoint--;
  }
  if (breakPoint <= maxLength - 50) {
    breakPoint = maxLength;
  }
  let truncated = content.slice(0, breakPoint).trimEnd();
  truncated = fixUnclosedMarkdown(truncated);
  return truncated;
}
__name(smartTruncate, "smartTruncate");
function truncateByLines(content, maxLines) {
  const lines = content.split("\n");
  if (lines.length <= maxLines) {
    return content;
  }
  let truncated = lines.slice(0, maxLines).join("\n").trimEnd();
  truncated = fixUnclosedMarkdown(truncated);
  return truncated;
}
__name(truncateByLines, "truncateByLines");
function fixUnclosedMarkdown(content) {
  let result = content;
  const countOccurrences = /* @__PURE__ */ __name((str, marker) => {
    const escaped = marker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
    const matches = str.match(new RegExp(escaped, "g"));
    return matches ? matches.length : 0;
  }, "countOccurrences");
  const boldCount = countOccurrences(result, "**");
  if (boldCount % 2 !== 0) {
    result += "**";
  }
  const withoutBold = result.replace(/\*\*/g, "");
  const italicCount = countOccurrences(withoutBold, "*");
  if (italicCount % 2 !== 0) {
    result += "*";
  }
  const codeCount = countOccurrences(result, "`");
  const tripleCount = countOccurrences(result, "```");
  const singleCodeCount = codeCount - tripleCount * 3;
  if (singleCodeCount % 2 !== 0) {
    result += "`";
  }
  if (tripleCount % 2 !== 0) {
    result += "\n```";
  }
  const strikeCount = countOccurrences(result, "~~");
  if (strikeCount % 2 !== 0) {
    result += "~~";
  }
  const underlineBoldCount = countOccurrences(result, "__");
  if (underlineBoldCount % 2 !== 0) {
    result += "__";
  }
  const withoutUnderlineBold = result.replace(/__/g, "");
  const underlineItalicCount = countOccurrences(withoutUnderlineBold, "_");
  if (underlineItalicCount % 2 !== 0) {
    result += "_";
  }
  return result;
}
__name(fixUnclosedMarkdown, "fixUnclosedMarkdown");
function useCollapsibleContent(content, options = {}) {
  const { maxLength, maxLines, defaultExpanded = false } = options;
  const [isCollapsed, setIsCollapsed] = useState(!defaultExpanded);
  const originalLength = content.length;
  const originalLineCount = content.split("\n").length;
  const { shouldCollapse, truncatedContent } = useMemo(() => {
    if (maxLength === void 0 && maxLines === void 0) {
      return { shouldCollapse: false, truncatedContent: content };
    }
    let needsCollapse = false;
    let result = content;
    if (maxLines !== void 0 && originalLineCount > maxLines) {
      needsCollapse = true;
      result = truncateByLines(result, maxLines);
    }
    if (maxLength !== void 0 && result.length > maxLength) {
      needsCollapse = true;
      result = smartTruncate(result, maxLength);
    }
    return { shouldCollapse: needsCollapse, truncatedContent: result };
  }, [content, maxLength, maxLines, originalLineCount]);
  const displayContent = useMemo(() => {
    if (!shouldCollapse || !isCollapsed) {
      return content;
    }
    return truncatedContent;
  }, [content, truncatedContent, shouldCollapse, isCollapsed]);
  const toggleCollapsed = useCallback(() => {
    setIsCollapsed((prev) => !prev);
  }, []);
  const setCollapsed = useCallback((collapsed) => {
    setIsCollapsed(collapsed);
  }, []);
  return {
    isCollapsed,
    toggleCollapsed,
    setCollapsed,
    displayContent,
    shouldCollapse,
    originalLength,
    originalLineCount
  };
}
__name(useCollapsibleContent, "useCollapsibleContent");
function extractTextFromChildren(children) {
  if (typeof children === "string") return children;
  if (typeof children === "number") return String(children);
  if (React6.isValidElement(children)) {
    const props = children.props;
    return extractTextFromChildren(props.children);
  }
  if (Array.isArray(children)) {
    return children.map(extractTextFromChildren).join("");
  }
  return "";
}
__name(extractTextFromChildren, "extractTextFromChildren");
function looksLikePlainProse(text) {
  const trimmed = text.trim();
  if (trimmed.length === 0) return true;
  if (trimmed.length > 500) return false;
  if (/\n\s*\n/.test(trimmed)) return false;
  const newlineCount = (trimmed.match(/\n/g) || []).length;
  if (newlineCount > 4) return false;
  if (hasMarkdownSyntax(trimmed)) return false;
  return true;
}
__name(looksLikePlainProse, "looksLikePlainProse");
function hasMarkdownSyntax(text) {
  if (text.trim().includes("\n")) return true;
  if (/<\/?[a-zA-Z][a-zA-Z0-9-]*(\s[^>]*)?\/?>/.test(text)) return true;
  const patterns = [
    /^#{1,6}\s/m,
    // Headers
    /\*\*[^*]+\*\*/,
    // Bold
    /\*[^*]+\*/,
    // Italic
    /__[^_]+__/,
    // Bold (underscore)
    /_[^_]+_/,
    // Italic (underscore)
    /\[.+\]\(.+\)/,
    // Links
    /!\[.*\]\(.+\)/,
    // Images
    /```[\s\S]*```/,
    // Code blocks
    /`[^`]+`/,
    // Inline code
    /^\s*[-*+]\s/m,
    // Unordered lists
    /^\s*\d+\.\s/m,
    // Ordered lists
    /^\s*>/m,
    // Blockquotes
    /\|.+\|/,
    // Tables
    /^---+$/m,
    // Horizontal rules
    /~~[^~]+~~/
    // Strikethrough
  ];
  return patterns.some((p) => p.test(text));
}
__name(hasMarkdownSyntax, "hasMarkdownSyntax");
var MermaidClient = lazy(() => import('./Mermaid.client-SXRRI2YW.mjs'));
var LoadingFallback = /* @__PURE__ */ __name(() => /* @__PURE__ */ jsx("div", { className: "flex justify-center items-center min-h-[100px]", children: /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-6 w-6 border-b-2 border-primary" }) }), "LoadingFallback");
var Mermaid = /* @__PURE__ */ __name((props) => {
  return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(LoadingFallback, {}), children: /* @__PURE__ */ jsx(MermaidClient, { ...props }) });
}, "Mermaid");
var Mermaid_default = Mermaid;
var PrettyCodeClient = lazy(() => import('./PrettyCode.client-SPMTQEG4.mjs'));
var LoadingFallback2 = /* @__PURE__ */ __name(() => /* @__PURE__ */ jsx("div", { className: "relative rounded-sm border border-border overflow-hidden bg-muted dark:bg-zinc-900", children: /* @__PURE__ */ jsx("div", { className: "p-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
  /* @__PURE__ */ jsx("div", { className: "animate-pulse h-4 w-4 rounded-full bg-muted-foreground/20" }),
  /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Loading code..." })
] }) }) }), "LoadingFallback");
var PrettyCode = /* @__PURE__ */ __name((props) => {
  return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(LoadingFallback2, {}), children: /* @__PURE__ */ jsx(PrettyCodeClient, { ...props }) });
}, "PrettyCode");
var PrettyCode_default = PrettyCode;
var HTML_SCHEMA_BASE = {
  ...defaultSchema,
  tagNames: [
    ...defaultSchema.tagNames ?? [],
    "br",
    "b",
    "i",
    "u",
    "s",
    "sub",
    "sup",
    "small",
    "mark",
    "kbd",
    "code",
    "pre",
    "details",
    "summary"
  ]
};
function buildSchema(extraProtocols) {
  if (!extraProtocols || extraProtocols.length === 0) return HTML_SCHEMA_BASE;
  const baseProtocols = HTML_SCHEMA_BASE.protocols ?? defaultSchema.protocols ?? {};
  const baseHref = baseProtocols.href ?? ["http", "https", "mailto", "xmpp", "irc", "ircs"];
  return {
    ...HTML_SCHEMA_BASE,
    protocols: {
      ...baseProtocols,
      href: [...baseHref, ...extraProtocols]
    }
  };
}
__name(buildSchema, "buildSchema");
function buildUrlTransform(extraProtocols) {
  if (!extraProtocols || extraProtocols.length === 0) return void 0;
  const lower = extraProtocols.map((p) => p.toLowerCase() + ":");
  return (url) => {
    const u = url.trim().toLowerCase();
    for (const p of lower) {
      if (u.startsWith(p)) return url;
    }
    if (/^(https?:|mailto:|tel:|xmpp:|irc:|ircs:|#|\/|\.\/|\.\.\/|\?)/i.test(u) || /^[a-z0-9._~!$&'()*+,;=:@%-]+$/i.test(u)) {
      return url;
    }
    return "";
  };
}
__name(buildUrlTransform, "buildUrlTransform");
var CodeBlock = /* @__PURE__ */ __name(({ code, language, isCompact = false }) => {
  const theme = useResolvedTheme();
  const textSizeClass = isCompact ? "text-xs" : "text-sm";
  return /* @__PURE__ */ jsx("div", { className: "my-3", children: /* @__PURE__ */ jsx(
    PrettyCode_default,
    {
      data: code,
      language,
      className: textSizeClass,
      customBg: "bg-code",
      mode: theme,
      isCompact,
      scrollIsolation: false
    }
  ) });
}, "CodeBlock");
var CodeBlockFallback = /* @__PURE__ */ __name(({ code, isUser }) => {
  const copyHoverClass = isUser ? "hover:bg-white/20 text-white" : "hover:bg-muted-foreground/20 text-muted-foreground hover:text-foreground";
  const copyButtonClass = `absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity h-8 w-8 ${copyHoverClass}`;
  return /* @__PURE__ */ jsxs("div", { className: "relative group my-3", children: [
    /* @__PURE__ */ jsx(
      CopyButton,
      {
        value: code,
        variant: "ghost",
        className: copyButtonClass,
        title: "Copy code"
      }
    ),
    /* @__PURE__ */ jsx("pre", { className: "p-3 rounded text-xs font-mono overflow-x-auto bg-code text-code-foreground border border-code-border", children: /* @__PURE__ */ jsx("code", { children: code }) })
  ] });
}, "CodeBlockFallback");
function createMarkdownComponents(isUser = false, isCompact = false) {
  const textSize = isCompact ? "text-xs" : "text-sm";
  const headingBase = isCompact ? "text-sm" : "text-base";
  const headingSm = isCompact ? "text-xs" : "text-sm";
  return {
    h1: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("h1", { className: `${headingBase} font-bold mb-2 mt-3 first:mt-0`, children }), "h1"),
    h2: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("h2", { className: `${headingSm} font-bold mb-2 mt-3 first:mt-0`, children }), "h2"),
    h3: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("h3", { className: `${headingSm} font-semibold mb-1 mt-2 first:mt-0`, children }), "h3"),
    h4: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("h4", { className: `${headingSm} font-semibold mb-1 mt-2 first:mt-0`, children }), "h4"),
    h5: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("h5", { className: `${headingSm} font-medium mb-1 mt-2 first:mt-0`, children }), "h5"),
    h6: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("h6", { className: `${headingSm} font-medium mb-1 mt-2 first:mt-0`, children }), "h6"),
    p: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("p", { className: `${textSize} mb-4 last:mb-0 leading-7 break-words font-light`, children }), "p"),
    ul: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("ul", { className: `list-disc list-inside mb-2 space-y-1 ${textSize}`, children }), "ul"),
    ol: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("ol", { className: `list-decimal list-inside mb-2 space-y-1 ${textSize}`, children }), "ol"),
    li: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("li", { className: "break-words", children }), "li"),
    // `target` / `rel` for external links are NOT set here — the
    // rehype-external-links plugin tags them on the rehype side, so
    // every `<a>` that sanitize let through gets the same security
    // treatment regardless of which renderer (default vs linkRules
    // override) emitted it. Doing it twice here would just duplicate
    // attributes; doing it only here would miss the linkRules path.
    a: /* @__PURE__ */ __name(({ href, children, ...rest }) => /* @__PURE__ */ jsx(
      "a",
      {
        ...rest,
        href,
        className: `${textSize} ${isUser ? "text-white/90 underline hover:text-white" : "text-primary underline hover:text-primary/80"} transition-colors break-all`,
        children
      }
    ), "a"),
    pre: /* @__PURE__ */ __name(({ children }) => {
      let codeContent = "";
      let language = "plaintext";
      if (React6.isValidElement(children)) {
        const child = children;
        if (child.type === "code" || typeof child.type === "function" && child.type.name === "code") {
          const codeProps = child.props;
          const rawClassName = codeProps.className;
          language = rawClassName?.replace(/language-/, "").trim() || "plaintext";
          codeContent = extractTextFromChildren(codeProps.children).trim();
        } else {
          codeContent = extractTextFromChildren(children).trim();
        }
      } else {
        codeContent = extractTextFromChildren(children).trim();
      }
      if (!codeContent) {
        return /* @__PURE__ */ jsx("div", { className: "my-3 p-3 bg-muted rounded text-sm text-muted-foreground", children: "No content available" });
      }
      if (language === "mermaid") {
        return /* @__PURE__ */ jsx("div", { className: "my-3 w-full", children: /* @__PURE__ */ jsx(Mermaid_default, { chart: codeContent, isCompact }) });
      }
      try {
        return /* @__PURE__ */ jsx(CodeBlock, { code: codeContent, language, isUser, isCompact });
      } catch (error) {
        console.warn("CodeBlock failed, using fallback:", error);
        return /* @__PURE__ */ jsx(CodeBlockFallback, { code: codeContent, language, isUser, isCompact });
      }
    }, "pre"),
    code: /* @__PURE__ */ __name(({ children, className }) => {
      if (className?.includes("language-")) {
        return /* @__PURE__ */ jsx("code", { className, children });
      }
      const inlineCodeClass = isUser ? "bg-primary-foreground/15 text-primary-foreground" : "bg-code-inline text-code-inline-foreground";
      return /* @__PURE__ */ jsx("code", { className: `px-1 py-0.5 rounded font-mono text-[0.875em] ${inlineCodeClass} break-all`, children: extractTextFromChildren(children) });
    }, "code"),
    // Modern chat convention drops italic on blockquotes — italic +
    // tight bubble = hard to read. Border-left at 2px (4px reads
    // heavy in a 320–480px bubble). On the saturated user bubble we
    // use a primary-foreground tint; on the assistant bubble we use
    // the muted-foreground role for de-emphasis.
    blockquote: /* @__PURE__ */ __name(({ children }) => {
      const cls = isUser ? "border-primary-foreground/40 text-primary-foreground/80" : "border-border text-muted-foreground";
      return /* @__PURE__ */ jsx("blockquote", { className: `${textSize} border-l-2 pl-3 my-3 break-words ${cls}`, children });
    }, "blockquote"),
    // Tables: outer wrapper handles overflow, inner `<table>`
    // inherits the chat-density text size. Borders / header use
    // semantic tokens — `border-code-border` for the assistant
    // (matches the code-fence panel for visual cohesion when both
    // appear in the same reply); primary-foreground/N for the user
    // bubble so lines read against the saturated `bg-primary`.
    table: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("div", { className: "overflow-x-auto my-3", children: /* @__PURE__ */ jsx("table", { className: `min-w-full ${textSize} border-collapse`, children }) }), "table"),
    thead: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("thead", { className: isUser ? "bg-primary-foreground/10" : "bg-muted/40", children }), "thead"),
    tbody: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("tbody", { children }), "tbody"),
    tr: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("tr", { className: isUser ? "border-b border-primary-foreground/15" : "border-b border-border", children }), "tr"),
    th: /* @__PURE__ */ __name(({ children }) => {
      const borderCls = isUser ? "border-primary-foreground/25" : "border-border";
      return /* @__PURE__ */ jsx("th", { className: `px-2 py-1.5 text-left font-semibold border-b ${borderCls} break-words`, children });
    }, "th"),
    td: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("td", { className: "px-2 py-1.5 break-words", children }), "td"),
    // Soft separator. ChatGPT / Slack / Linear strip the visible
    // line, Claude.ai keeps a hairline. We follow Claude — present
    // but quiet. Palette switches by role so the hairline reads on
    // both surfaces.
    hr: /* @__PURE__ */ __name(() => /* @__PURE__ */ jsx(
      "hr",
      {
        className: `my-4 border-0 h-px ${isUser ? "bg-primary-foreground/20" : "bg-border"}`
      }
    ), "hr"),
    strong: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("strong", { className: "font-semibold", children }), "strong"),
    em: /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("em", { className: "italic", children }), "em")
  };
}
__name(createMarkdownComponents, "createMarkdownComponents");
var CollapseToggle = /* @__PURE__ */ __name(({
  isCollapsed,
  onClick,
  readMoreLabel,
  showLessLabel,
  isUser,
  isCompact
}) => {
  const textSize = isCompact ? "text-xs" : "text-sm";
  return /* @__PURE__ */ jsx(
    "button",
    {
      type: "button",
      onClick,
      className: `
        ${textSize} font-medium cursor-pointer
        transition-colors duration-200
        ${isUser ? "text-white/80 hover:text-white" : "text-primary hover:text-primary/80"}
        inline-flex items-center gap-1
        mt-1
      `,
      children: isCollapsed ? /* @__PURE__ */ jsxs(Fragment, { children: [
        readMoreLabel,
        /* @__PURE__ */ jsx(Chevron, { direction: "down" })
      ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
        showLessLabel,
        /* @__PURE__ */ jsx(Chevron, { direction: "up" })
      ] })
    }
  );
}, "CollapseToggle");
function Chevron({ direction }) {
  return /* @__PURE__ */ jsx("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx(
    "path",
    {
      strokeLinecap: "round",
      strokeLinejoin: "round",
      strokeWidth: 2,
      d: direction === "down" ? "M19 9l-7 7-7-7" : "M5 15l7-7 7 7"
    }
  ) });
}
__name(Chevron, "Chevron");
function applyPreprocess(source, rules) {
  if (!rules || rules.length === 0) return source;
  let s = source;
  for (const rule of rules) {
    if (!rule.preprocess) continue;
    try {
      s = rule.preprocess(s);
    } catch (err) {
      console.warn(
        `[MarkdownMessage] linkRule "${rule.name ?? "(anonymous)"}" preprocess threw; skipping`,
        err
      );
    }
  }
  return s;
}
__name(applyPreprocess, "applyPreprocess");
function collectProtocols(extraHrefProtocols, rules) {
  const set = /* @__PURE__ */ new Set();
  if (extraHrefProtocols) for (const p of extraHrefProtocols) set.add(p);
  if (rules) {
    for (const r of rules) {
      if (r.protocols) for (const p of r.protocols) set.add(p);
    }
  }
  return set.size === 0 ? void 0 : Array.from(set);
}
__name(collectProtocols, "collectProtocols");
function buildLinkRulesComponent(rules, isUser, callerA) {
  const Renderer = /* @__PURE__ */ __name((props) => {
    const { href, children } = props;
    if (typeof href === "string") {
      for (const rule of rules) {
        if (rule.match(href)) {
          return React6.createElement(
            React6.Fragment,
            null,
            rule.render({ href, children, isUser })
          );
        }
      }
    }
    if (callerA && typeof callerA === "function") {
      const Caller = callerA;
      return React6.createElement(Caller, props);
    }
    return React6.createElement("a", props, children);
  }, "Renderer");
  return Renderer;
}
__name(buildLinkRulesComponent, "buildLinkRulesComponent");
var MarkdownMessage = /* @__PURE__ */ __name(({
  content,
  className = "",
  isUser = false,
  isCompact = false,
  plainText,
  customComponents,
  extraHrefProtocols,
  linkRules,
  collapsible = false,
  maxLength,
  maxLines,
  readMoreLabel = "Read more...",
  showLessLabel = "Show less",
  defaultExpanded = false,
  onCollapseChange
}) => {
  const preprocessed = React6.useMemo(
    () => applyPreprocess(content, linkRules),
    [content, linkRules]
  );
  const effectiveProtocols = React6.useMemo(
    () => collectProtocols(extraHrefProtocols, linkRules),
    [extraHrefProtocols, linkRules]
  );
  const effectiveCustomComponents = React6.useMemo(() => {
    if (!linkRules || linkRules.length === 0) return customComponents;
    const callerA = customComponents?.a;
    const aRenderer = buildLinkRulesComponent(linkRules, isUser, callerA);
    return { ...customComponents ?? {}, a: aRenderer };
  }, [customComponents, linkRules, isUser]);
  const trimmedContent = preprocessed.trim();
  const collapsibleOptions = React6.useMemo(() => {
    if (!collapsible) return {};
    return {
      maxLength: maxLength ?? 1e3,
      maxLines: maxLines ?? 10,
      defaultExpanded
    };
  }, [collapsible, maxLength, maxLines, defaultExpanded]);
  const { isCollapsed, toggleCollapsed, displayContent, shouldCollapse } = useCollapsibleContent(trimmedContent, collapsible ? collapsibleOptions : {});
  React6.useEffect(() => {
    if (collapsible && shouldCollapse && onCollapseChange) {
      onCollapseChange(isCollapsed);
    }
  }, [isCollapsed, collapsible, shouldCollapse, onCollapseChange]);
  const components = React6.useMemo(() => {
    const base = createMarkdownComponents(isUser, isCompact);
    return effectiveCustomComponents ? { ...base, ...effectiveCustomComponents } : base;
  }, [isUser, isCompact, effectiveCustomComponents]);
  const schema = React6.useMemo(() => buildSchema(effectiveProtocols), [effectiveProtocols]);
  const urlTransform = React6.useMemo(
    () => buildUrlTransform(effectiveProtocols),
    [effectiveProtocols]
  );
  const textSizeClass = isCompact ? "text-xs" : "text-sm";
  const proseClass = isCompact ? "prose-xs" : "prose-sm";
  const customComponentsBeyondLinks = React6.useMemo(() => {
    if (!customComponents) return false;
    return Object.keys(customComponents).some((k) => k !== "a");
  }, [customComponents]);
  const isPlainText = plainText !== void 0 ? plainText : !customComponentsBeyondLinks && looksLikePlainProse(displayContent);
  if (isPlainText) {
    return /* @__PURE__ */ jsxs(
      "div",
      {
        className: `${textSizeClass} leading-snug break-words whitespace-pre-wrap ${className}`,
        children: [
          displayContent,
          collapsible && shouldCollapse && /* @__PURE__ */ jsxs(Fragment, { children: [
            isCollapsed && "... ",
            /* @__PURE__ */ jsx(
              CollapseToggle,
              {
                isCollapsed,
                onClick: toggleCollapsed,
                readMoreLabel,
                showLessLabel,
                isUser,
                isCompact
              }
            )
          ] })
        ]
      }
    );
  }
  return /* @__PURE__ */ jsxs("div", { className, children: [
    /* @__PURE__ */ jsx(
      "div",
      {
        className: `
          prose ${proseClass} max-w-none break-words overflow-hidden ${textSizeClass}
          ${isUser ? "prose-invert" : "dark:prose-invert"}
          [&>*]:leading-relaxed
          [&>*:first-child]:mt-0 [&>*:last-child]:mb-0
          [&_p]:my-2
          [&_ul]:my-2 [&_ol]:my-2 [&_ul]:pl-5 [&_ol]:pl-5
          [&_li]:my-1 [&_li>p]:my-0
          [&_pre]:my-3
          [&_h1]:mt-4 [&_h1]:mb-2 [&_h1]:text-base [&_h1]:font-semibold
          [&_h2]:mt-3.5 [&_h2]:mb-1.5 [&_h2]:text-[15px] [&_h2]:font-semibold
          [&_h3]:mt-3 [&_h3]:mb-1 [&_h3]:text-sm [&_h3]:font-medium
          [&_h4]:mt-3 [&_h4]:mb-1 [&_h4]:text-sm [&_h4]:font-medium
        `,
        style: {
          // Inherit colors from parent — fixes issues with external
          // CSS variables overriding prose tokens.
          "--tw-prose-body": "inherit",
          "--tw-prose-headings": "inherit",
          "--tw-prose-bold": "inherit",
          "--tw-prose-links": "inherit",
          color: "inherit"
        },
        children: /* @__PURE__ */ jsx(
          ReactMarkdown,
          {
            remarkPlugins: [remarkGfm, remarkBreaks, remarkSmartypants, remarkEmoji],
            rehypePlugins: [
              rehypeRaw,
              [rehypeSanitize, schema],
              [rehypeExternalLinks, { target: "_blank", rel: ["noopener", "noreferrer"] }]
            ],
            components,
            urlTransform,
            children: displayContent
          }
        )
      }
    ),
    collapsible && shouldCollapse && /* @__PURE__ */ jsx(
      CollapseToggle,
      {
        isCollapsed,
        onClick: toggleCollapsed,
        readMoreLabel,
        showLessLabel,
        isUser,
        isCompact
      }
    )
  ] });
}, "MarkdownMessage");

// src/tools/OpenapiViewer/utils/apiKeyManager.ts
function findApiKeyById(apiKeys, keyId) {
  return apiKeys.find((key) => key.id === keyId) || null;
}
__name(findApiKeyById, "findApiKeyById");

// src/tools/OpenapiViewer/utils/codeSamples.ts
var CODE_SAMPLE_TARGETS = [
  { id: "curl", label: "cURL", prism: "bash" },
  { id: "fetch", label: "JavaScript", prism: "javascript" },
  { id: "axios", label: "Node (axios)", prism: "javascript" },
  { id: "python", label: "Python", prism: "python" },
  { id: "go", label: "Go", prism: "go" },
  { id: "php", label: "PHP", prism: "php" },
  { id: "ruby", label: "Ruby", prism: "ruby" },
  { id: "java", label: "Java", prism: "java" }
];
function goRawString(s) {
  return s.includes("`") ? JSON.stringify(s) : `\`${s}\``;
}
__name(goRawString, "goRawString");
function rubyHeredoc(s) {
  return `<<~JSON
${s}
JSON`;
}
__name(rubyHeredoc, "rubyHeredoc");
function phpHeredoc(s) {
  return `<<<JSON
${s}
JSON`;
}
__name(phpHeredoc, "phpHeredoc");
function javaTextBlock(s) {
  if (s.includes('"""')) return JSON.stringify(s);
  return `"""
${s}
"""`;
}
__name(javaTextBlock, "javaTextBlock");
function fullUrl(har) {
  if (!har.queryString.length) return har.url;
  const qs = har.queryString.map((p) => `${encodeURIComponent(p.name)}=${encodeURIComponent(p.value)}`).join("&");
  const sep = har.url.includes("?") ? "&" : "?";
  return `${har.url}${sep}${qs}`;
}
__name(fullUrl, "fullUrl");
function shellEscape(value) {
  return `'${value.replace(/'/g, `'\\''`)}'`;
}
__name(shellEscape, "shellEscape");
function renderCurl(har) {
  const lines = [`curl -X ${har.method} ${shellEscape(fullUrl(har))}`];
  for (const h of har.headers) {
    lines.push(`  -H ${shellEscape(`${h.name}: ${h.value}`)}`);
  }
  if (har.postData?.text) {
    lines.push(`  -d ${shellEscape(har.postData.text)}`);
  }
  return lines.join(" \\\n");
}
__name(renderCurl, "renderCurl");
function jsHeadersLiteral(har, indent) {
  if (!har.headers.length) return "{}";
  const entries = har.headers.map((h) => `${indent}  ${JSON.stringify(h.name)}: ${JSON.stringify(h.value)}`).join(",\n");
  return `{
${entries},
${indent}}`;
}
__name(jsHeadersLiteral, "jsHeadersLiteral");
function jsTemplateLiteral(s) {
  if (/[`$]/.test(s)) return JSON.stringify(s);
  return `\`${s}\``;
}
__name(jsTemplateLiteral, "jsTemplateLiteral");
function renderFetch(har) {
  const url = fullUrl(har);
  const options = [`  method: ${JSON.stringify(har.method)}`];
  if (har.headers.length) {
    options.push(`  headers: ${jsHeadersLiteral(har, "  ")}`);
  }
  if (har.postData?.text) {
    options.push(`  body: ${jsTemplateLiteral(har.postData.text)}`);
  }
  return `const response = await fetch(${JSON.stringify(url)}, {
${options.join(",\n")},
});
const data = await response.json();`;
}
__name(renderFetch, "renderFetch");
function renderAxios(har) {
  const url = fullUrl(har);
  const config = [
    `  method: ${JSON.stringify(har.method.toLowerCase())}`,
    `  url: ${JSON.stringify(url)}`
  ];
  if (har.headers.length) {
    config.push(`  headers: ${jsHeadersLiteral(har, "  ")}`);
  }
  if (har.postData?.text) {
    config.push(`  data: ${har.postData.text}`);
  }
  return `import axios from 'axios';

const { data } = await axios({
${config.join(",\n")},
});`;
}
__name(renderAxios, "renderAxios");
function renderPython(har) {
  const lines = [`import requests`, ``];
  lines.push(`url = ${JSON.stringify(fullUrl(har))}`);
  if (har.headers.length) {
    const headerEntries = har.headers.map((h) => `    ${JSON.stringify(h.name)}: ${JSON.stringify(h.value)}`).join(",\n");
    lines.push(`headers = {
${headerEntries},
}`);
  }
  if (har.postData?.text) {
    lines.push(`payload = ${har.postData.text}`);
  }
  const args = [`url`];
  if (har.headers.length) args.push(`headers=headers`);
  if (har.postData?.text) args.push(`json=payload`);
  lines.push(``, `response = requests.${har.method.toLowerCase()}(${args.join(", ")})`);
  lines.push(`data = response.json()`);
  return lines.join("\n");
}
__name(renderPython, "renderPython");
function renderGo(har) {
  const url = fullUrl(har);
  const lines = [
    `package main`,
    ``,
    `import (`,
    `    "fmt"`,
    `    "io"`
  ];
  if (har.postData?.text) lines.push(`    "strings"`);
  lines.push(`    "net/http"`);
  lines.push(`)`, ``, `func main() {`);
  if (har.postData?.text) {
    lines.push(`    payload := strings.NewReader(${goRawString(har.postData.text)})`);
    lines.push(`    req, _ := http.NewRequest(${JSON.stringify(har.method)}, ${JSON.stringify(url)}, payload)`);
  } else {
    lines.push(`    req, _ := http.NewRequest(${JSON.stringify(har.method)}, ${JSON.stringify(url)}, nil)`);
  }
  for (const h of har.headers) {
    lines.push(`    req.Header.Add(${JSON.stringify(h.name)}, ${JSON.stringify(h.value)})`);
  }
  lines.push(
    ``,
    `    res, _ := http.DefaultClient.Do(req)`,
    `    defer res.Body.Close()`,
    `    body, _ := io.ReadAll(res.Body)`,
    `    fmt.Println(string(body))`,
    `}`
  );
  return lines.join("\n");
}
__name(renderGo, "renderGo");
function renderPhp(har) {
  const lines = [`<?php`, ``, `$curl = curl_init();`, ``, `curl_setopt_array($curl, [`];
  lines.push(`    CURLOPT_URL => ${JSON.stringify(fullUrl(har))},`);
  lines.push(`    CURLOPT_RETURNTRANSFER => true,`);
  lines.push(`    CURLOPT_CUSTOMREQUEST => ${JSON.stringify(har.method)},`);
  if (har.postData?.text) {
    lines.push(`    CURLOPT_POSTFIELDS => ${phpHeredoc(har.postData.text)},`);
  }
  if (har.headers.length) {
    const headerList = har.headers.map((h) => `        ${JSON.stringify(`${h.name}: ${h.value}`)}`).join(",\n");
    lines.push(`    CURLOPT_HTTPHEADER => [
${headerList},
    ],`);
  }
  lines.push(`]);`, ``, `$response = curl_exec($curl);`, `curl_close($curl);`, `echo $response;`);
  return lines.join("\n");
}
__name(renderPhp, "renderPhp");
function renderRuby(har) {
  const lines = [
    `require 'net/http'`,
    `require 'uri'`,
    `require 'json'`,
    ``,
    `uri = URI(${JSON.stringify(fullUrl(har))})`,
    `http = Net::HTTP.new(uri.host, uri.port)`,
    `http.use_ssl = uri.scheme == 'https'`,
    ``
  ];
  const methodClass = har.method.charAt(0) + har.method.slice(1).toLowerCase();
  lines.push(`request = Net::HTTP::${methodClass}.new(uri)`);
  for (const h of har.headers) {
    lines.push(`request[${JSON.stringify(h.name)}] = ${JSON.stringify(h.value)}`);
  }
  if (har.postData?.text) {
    lines.push(`request.body = ${rubyHeredoc(har.postData.text)}`);
  }
  lines.push(``, `response = http.request(request)`, `puts response.body`);
  return lines.join("\n");
}
__name(renderRuby, "renderRuby");
function renderJava(har) {
  const lines = [
    `OkHttpClient client = new OkHttpClient();`,
    ``
  ];
  if (har.postData?.text) {
    lines.push(
      `MediaType mediaType = MediaType.parse("application/json");`,
      `RequestBody body = RequestBody.create(mediaType, ${javaTextBlock(har.postData.text)});`,
      ``
    );
  }
  lines.push(`Request request = new Request.Builder()`);
  lines.push(`  .url(${JSON.stringify(fullUrl(har))})`);
  if (har.postData?.text) {
    lines.push(`  .method(${JSON.stringify(har.method)}, body)`);
  } else {
    lines.push(`  .method(${JSON.stringify(har.method)}, null)`);
  }
  for (const h of har.headers) {
    lines.push(`  .addHeader(${JSON.stringify(h.name)}, ${JSON.stringify(h.value)})`);
  }
  lines.push(`  .build();`, ``, `Response response = client.newCall(request).execute();`);
  return lines.join("\n");
}
__name(renderJava, "renderJava");
var RENDERERS = {
  curl: renderCurl,
  fetch: renderFetch,
  axios: renderAxios,
  python: renderPython,
  go: renderGo,
  php: renderPhp,
  ruby: renderRuby,
  java: renderJava
};
function renderSnippet(har, targetId) {
  const renderer = RENDERERS[targetId];
  if (!renderer) return null;
  try {
    return renderer(har);
  } catch {
    return null;
  }
}
__name(renderSnippet, "renderSnippet");

// src/tools/OpenapiViewer/utils/operationToHar.ts
function buildUrl(endpoint, parameters, baseUrl) {
  const pathParamNames = new Set(
    (endpoint.parameters ?? []).filter((p) => endpoint.path.includes(`{${p.name}}`)).map((p) => p.name)
  );
  let path = endpoint.path;
  for (const name of pathParamNames) {
    const value = parameters[name] ?? `{${name}}`;
    path = path.replaceAll(`{${name}}`, encodeURIComponent(value));
  }
  const queryString = [];
  for (const param of endpoint.parameters ?? []) {
    if (pathParamNames.has(param.name)) continue;
    const value = parameters[param.name];
    if (value === void 0 || value === "") continue;
    queryString.push({ name: param.name, value });
  }
  const url = baseUrl ? `${baseUrl.replace(/\/+$/, "")}${path.startsWith("/") ? path : `/${path}`}` : path;
  return { url, queryString };
}
__name(buildUrl, "buildUrl");
function buildHarRequest(input) {
  const { endpoint, body, parameters = {}, headers = {}, baseUrl } = input;
  const { url, queryString } = buildUrl(endpoint, parameters, baseUrl);
  const hasBody = Boolean(body && body.trim().length > 0);
  const bodyMime = hasBody ? "application/json" : void 0;
  const mergedHeaders = {};
  if (hasBody && bodyMime) mergedHeaders["Content-Type"] = bodyMime;
  mergedHeaders["Accept"] = "application/json";
  for (const [k, v] of Object.entries(headers)) {
    if (v === void 0 || v === "") continue;
    mergedHeaders[k] = v;
  }
  const har = {
    method: endpoint.method.toUpperCase(),
    url,
    httpVersion: "HTTP/1.1",
    headers: Object.entries(mergedHeaders).map(([name, value]) => ({ name, value })),
    queryString,
    cookies: [],
    headersSize: -1,
    bodySize: hasBody ? new TextEncoder().encode(body).length : 0
  };
  if (hasBody && bodyMime) {
    har.postData = { mimeType: bodyMime, text: body };
  }
  return har;
}
__name(buildHarRequest, "buildHarRequest");

// src/tools/OpenapiViewer/utils/versionManager.ts
var API_VERSIONS = [
  {
    id: "v1",
    name: "v1",
    description: "Current stable version",
    isDefault: true
  }
];
var detectEndpointVersion = /* @__PURE__ */ __name((path) => {
  const versionMatch = path.match(/\/api\/[^/]+\/(v\d+)\//);
  if (versionMatch && versionMatch[1]) {
    return versionMatch[1];
  }
  return "v1";
}, "detectEndpointVersion");
var isEndpointInVersion = /* @__PURE__ */ __name((endpoint, version) => {
  const endpointVersion = detectEndpointVersion(endpoint.path);
  return endpointVersion === version;
}, "isEndpointInVersion");
var deduplicateEndpoints = /* @__PURE__ */ __name((endpoints, preferredVersion) => {
  const endpointMap = /* @__PURE__ */ new Map();
  const groupedEndpoints = /* @__PURE__ */ new Map();
  endpoints.forEach((endpoint) => {
    const normalizedPath = normalizeEndpointPath(endpoint.path);
    if (!groupedEndpoints.has(normalizedPath)) {
      groupedEndpoints.set(normalizedPath, []);
    }
    groupedEndpoints.get(normalizedPath).push(endpoint);
  });
  groupedEndpoints.forEach((endpointGroup, normalizedPath) => {
    let selectedEndpoint = null;
    const versionEndpoint = endpointGroup.find((ep) => isEndpointInVersion(ep, preferredVersion));
    if (versionEndpoint) {
      selectedEndpoint = versionEndpoint;
    } else if (endpointGroup.length > 0) {
      selectedEndpoint = endpointGroup[0] || null;
    }
    if (selectedEndpoint) {
      endpointMap.set(normalizedPath, selectedEndpoint);
    }
  });
  return Array.from(endpointMap.values());
}, "deduplicateEndpoints");
var normalizeEndpointPath = /* @__PURE__ */ __name((path) => {
  return path.replace(/\/v\d+\//, "/");
}, "normalizeEndpointPath");
var getDefaultVersion = /* @__PURE__ */ __name(() => {
  const defaultVersion = API_VERSIONS.find((v) => v.isDefault);
  if (defaultVersion) {
    return defaultVersion;
  }
  if (API_VERSIONS.length > 0 && API_VERSIONS[0]) {
    return API_VERSIONS[0];
  }
  throw new Error("No API versions defined");
}, "getDefaultVersion");

// src/tools/OpenapiViewer/utils/formatters.ts
var isValidJson = /* @__PURE__ */ __name((str) => {
  if (!str || typeof str !== "string") return false;
  try {
    JSON.parse(str);
    return true;
  } catch {
    return false;
  }
}, "isValidJson");
var parseRequestHeaders = /* @__PURE__ */ __name((headersString) => {
  if (!headersString || typeof headersString !== "string") {
    return { "Content-Type": "application/json" };
  }
  try {
    const parsed = JSON.parse(headersString);
    return typeof parsed === "object" && parsed !== null ? parsed : { "Content-Type": "application/json" };
  } catch {
    return { "Content-Type": "application/json" };
  }
}, "parseRequestHeaders");
function sampleSchema(schema, options = {}, spec) {
  if (!schema) return null;
  try {
    return sample(schema, options, spec);
  } catch (err) {
    consola2.warn("[OpenapiViewer] sampleSchema failed:", err, { schema });
    return null;
  }
}
__name(sampleSchema, "sampleSchema");
function sampleSchemaJson(schema, options = {}, spec) {
  const value = sampleSchema(schema, options, spec);
  if (value === null || value === void 0) return void 0;
  try {
    return JSON.stringify(value, null, 2);
  } catch {
    return void 0;
  }
}
__name(sampleSchemaJson, "sampleSchemaJson");

// src/tools/OpenapiViewer/utils/url.ts
var PATH_PARAM_RE = /\{([^{}]+)\}/g;
function extractPathPlaceholders(template) {
  const seen = /* @__PURE__ */ new Set();
  const out = [];
  for (const match of template.matchAll(PATH_PARAM_RE)) {
    const name = match[1];
    if (!seen.has(name)) {
      seen.add(name);
      out.push(name);
    }
  }
  return out;
}
__name(extractPathPlaceholders, "extractPathPlaceholders");
function substitutePath(template, values) {
  return template.replace(PATH_PARAM_RE, (whole, name) => {
    const v = values[name];
    return v && v.trim() !== "" ? encodeURIComponent(v) : whole;
  });
}
__name(substitutePath, "substitutePath");
function unfilledPlaceholders(template, values) {
  return extractPathPlaceholders(template).filter(
    (name) => !(values[name] ?? "").trim()
  );
}
__name(unfilledPlaceholders, "unfilledPlaceholders");
function appendQuery(url, values, pathTemplate) {
  const pathNames = new Set(extractPathPlaceholders(pathTemplate));
  const queryEntries = Object.entries(values).filter(
    ([name, value]) => !pathNames.has(name) && value && value.trim() !== ""
  );
  if (queryEntries.length === 0) return url;
  const [base, existingQuery = ""] = url.split("?", 2);
  const params = new URLSearchParams(existingQuery);
  for (const [k, v] of queryEntries) params.set(k, v);
  const qs = params.toString();
  return qs ? `${base}?${qs}` : base ?? url;
}
__name(appendQuery, "appendQuery");
function resolveAbsolute(url) {
  if (!url) return url;
  if (/^https?:\/\//i.test(url)) return url;
  if (typeof window === "undefined") return url;
  if (url.startsWith("//")) return `${window.location.protocol}${url}`;
  if (url.startsWith("/")) return `${window.location.origin}${url}`;
  return url;
}
__name(resolveAbsolute, "resolveAbsolute");
function relativePath(url) {
  try {
    return new URL(url).pathname;
  } catch {
    return url;
  }
}
__name(relativePath, "relativePath");
function joinUrl(...parts) {
  return parts.filter((p) => p !== void 0 && p !== null && p !== "").map((p, i) => {
    if (i === 0) return String(p).replace(/\/+$/, "");
    return String(p).replace(/^\/+|\/+$/g, "");
  }).filter((p) => p !== "").join("/");
}
__name(joinUrl, "joinUrl");
function resolveBaseUrl(sources) {
  return sources.schemaSource || sources.config || sources.fromServers || "";
}
__name(resolveBaseUrl, "resolveBaseUrl");
var _UrlBuilder = class _UrlBuilder {
  constructor(endpoint, parameters) {
    this.endpoint = endpoint;
    this.parameters = parameters;
  }
  /** What ``fetch()`` receives: substituted path + query string. Origin
   *  is whatever the endpoint template already had (relative paths
   *  stay relative so the browser resolves them against the page). */
  build() {
    const substituted = substitutePath(this.endpoint.path, this.parameters);
    return appendQuery(substituted, this.parameters, this.endpoint.path);
  }
  /** Same as ``build()`` but guaranteed absolute — for curl snippets
   *  and anywhere the URL leaves the browser context. */
  buildAbsolute() {
    return resolveAbsolute(this.build());
  }
  /** Names of required path/query params still empty. */
  missingRequired() {
    if (!this.endpoint.parameters) return [];
    return this.endpoint.parameters.filter((p) => p.required && !(this.parameters[p.name] ?? "").trim()).map((p) => p.name);
  }
  /** Placeholders in the template with no matching value. Usually a
   *  superset of ``missingRequired`` — catches schemas that forgot
   *  to flag a path param as required. */
  unfilledPlaceholders() {
    return unfilledPlaceholders(this.endpoint.path, this.parameters);
  }
};
__name(_UrlBuilder, "UrlBuilder");
var UrlBuilder = _UrlBuilder;

// src/tools/OpenapiViewer/utils/schemaExport.ts
function dereferenceSchema(schema, maxDepth = 10) {
  const root = schema;
  function resolveRef(ref) {
    if (!ref.startsWith("#/")) return null;
    const parts = ref.slice(2).split("/");
    let node = root;
    for (const part of parts) {
      if (node && typeof node === "object" && part in node) {
        node = node[part];
      } else {
        return null;
      }
    }
    return node;
  }
  __name(resolveRef, "resolveRef");
  function walk(value, depth) {
    if (depth > maxDepth) return value;
    if (Array.isArray(value)) return value.map((v) => walk(v, depth + 1));
    if (value && typeof value === "object") {
      const obj = value;
      if (typeof obj.$ref === "string") {
        const resolved = resolveRef(obj.$ref);
        if (resolved === null) return obj;
        return walk(resolved, depth + 1);
      }
      const out = {};
      for (const [k, v] of Object.entries(obj)) out[k] = walk(v, depth + 1);
      return out;
    }
    return value;
  }
  __name(walk, "walk");
  return walk(schema, 0);
}
__name(dereferenceSchema, "dereferenceSchema");
var NOISY_KEYS = /* @__PURE__ */ new Set(["xml", "example", "examples", "externalDocs"]);
var MAX_DESCRIPTION_LEN = 500;
function pruneForCompact(value) {
  if (Array.isArray(value)) return value.map(pruneForCompact);
  if (value && typeof value === "object") {
    const out = {};
    for (const [k, v] of Object.entries(value)) {
      if (NOISY_KEYS.has(k)) continue;
      if (k === "description" && typeof v === "string" && v.length > MAX_DESCRIPTION_LEN) {
        out[k] = v.slice(0, MAX_DESCRIPTION_LEN).trimEnd() + "\u2026";
        continue;
      }
      out[k] = pruneForCompact(v);
    }
    return out;
  }
  return value;
}
__name(pruneForCompact, "pruneForCompact");
function withResolvedBaseUrl(schema, baseUrl) {
  if (!baseUrl) return schema;
  return { ...schema, servers: [{ url: baseUrl }] };
}
__name(withResolvedBaseUrl, "withResolvedBaseUrl");
function toRawJson(schema, baseUrl) {
  return JSON.stringify(withResolvedBaseUrl(schema, baseUrl), null, 2);
}
__name(toRawJson, "toRawJson");
function toCompactJson(schema, baseUrl) {
  const resolved = dereferenceSchema(withResolvedBaseUrl(schema, baseUrl));
  const pruned = pruneForCompact(resolved);
  return JSON.stringify(pruned);
}
__name(toCompactJson, "toCompactJson");
function endpointToMarkdown(ep) {
  const lines = [];
  lines.push(`### ${ep.method} ${ep.path}`);
  if (ep.description) lines.push(ep.description);
  const pathParams = ep.parameters?.filter((p) => ep.path.includes(`{${p.name}}`)) ?? [];
  const queryParams = ep.parameters?.filter((p) => !ep.path.includes(`{${p.name}}`)) ?? [];
  if (pathParams.length > 0) {
    lines.push("", "**Path parameters**");
    for (const p of pathParams) {
      const req = p.required ? " (required)" : "";
      const desc = p.description ? ` \u2014 ${p.description}` : "";
      lines.push(`- \`${p.name}\`: ${p.type}${req}${desc}`);
    }
  }
  if (queryParams.length > 0) {
    lines.push("", "**Query parameters**");
    for (const p of queryParams) {
      const req = p.required ? " (required)" : "";
      const desc = p.description ? ` \u2014 ${p.description}` : "";
      lines.push(`- \`${p.name}\`: ${p.type}${req}${desc}`);
    }
  }
  if (ep.requestBody) {
    const desc = ep.requestBody.description ? ` \u2014 ${ep.requestBody.description}` : "";
    lines.push("", `**Request body:** ${ep.requestBody.type}${desc}`);
  }
  if (ep.responses && ep.responses.length > 0) {
    lines.push("", "**Responses**");
    for (const r of ep.responses) {
      lines.push(`- \`${r.code}\` \u2014 ${r.description}`);
    }
  }
  return lines.join("\n");
}
__name(endpointToMarkdown, "endpointToMarkdown");
function toMarkdown(schema, endpoints, baseUrl) {
  const lines = [];
  const info = schema.info;
  const resolvedBase = resolveBaseUrl({
    config: baseUrl,
    fromServers: schema.servers?.[0]?.url
  });
  lines.push(`# ${info?.title ?? "API"}${info?.version ? ` (v${info.version})` : ""}`);
  if (resolvedBase) lines.push("", `**Base URL:** \`${resolvedBase}\``);
  if (info?.description) lines.push("", info.description.trim());
  const grouped = /* @__PURE__ */ new Map();
  for (const ep of endpoints) {
    const arr = grouped.get(ep.category) ?? [];
    arr.push(ep);
    grouped.set(ep.category, arr);
  }
  const categories = Array.from(grouped.keys()).sort((a, b) => {
    if (a === "Other") return 1;
    if (b === "Other") return -1;
    return a.localeCompare(b);
  });
  for (const category of categories) {
    lines.push("", `## ${category}`);
    const list = grouped.get(category);
    for (const ep of list) {
      const displayPath = relativePath(ep.path);
      const sub = endpointToMarkdown({ ...ep, path: displayPath });
      lines.push("", sub);
    }
  }
  return lines.join("\n");
}
__name(toMarkdown, "toMarkdown");
function formatBytes(s) {
  const bytes = new Blob([s]).size;
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
}
__name(formatBytes, "formatBytes");
var AUTH_KEY_STORAGE = "openapi-playground:auth:apiKeyId";
var AUTH_BEARER_STORAGE = "openapi-playground:auth:bearer";
var createInitialState = /* @__PURE__ */ __name(() => ({
  currentStep: "endpoints",
  steps: ["endpoints", "request", "response"],
  selectedEndpoint: null,
  selectedCategory: "All",
  searchTerm: "",
  selectedVersion: getDefaultVersion().id,
  requestUrl: "",
  requestMethod: "GET",
  requestHeaders: '{\n  "Content-Type": "application/json"\n}',
  requestBody: "",
  selectedApiKey: null,
  manualApiToken: "",
  parameters: {},
  response: null,
  loading: false,
  sidebarOpen: false,
  activeSchemaId: null
}), "createInitialState");
function reducer(state, action) {
  switch (action.type) {
    case "SET_STEP":
      return { ...state, currentStep: action.step };
    case "NEXT_STEP": {
      const i = state.steps.indexOf(state.currentStep);
      return i < state.steps.length - 1 ? { ...state, currentStep: state.steps[i + 1] } : state;
    }
    case "PREV_STEP": {
      const i = state.steps.indexOf(state.currentStep);
      return i > 0 ? { ...state, currentStep: state.steps[i - 1] } : state;
    }
    case "SELECT_ENDPOINT": {
      if (!action.endpoint) return { ...state, selectedEndpoint: null };
      const same = state.selectedEndpoint?.method === action.endpoint.method && state.selectedEndpoint?.path === action.endpoint.path;
      const exampleBody = action.endpoint.requestBody?.example ?? "";
      return {
        ...state,
        selectedEndpoint: action.endpoint,
        requestMethod: action.endpoint.method,
        requestUrl: action.endpoint.path,
        parameters: same ? state.parameters : {},
        requestBody: same ? state.requestBody : exampleBody,
        // Switching to a different endpoint: the previous response no
        // longer belongs here. Clear it so the playground panel collapses
        // back to single-column until the user sends a new request.
        response: same ? state.response : null,
        currentStep: "request"
      };
    }
    case "SET_CATEGORY":
      return { ...state, selectedCategory: action.category };
    case "SET_SEARCH":
      return { ...state, searchTerm: action.term };
    case "SET_VERSION":
      return { ...state, selectedVersion: action.version };
    case "SET_REQUEST_URL":
      return { ...state, requestUrl: action.url };
    case "SET_REQUEST_METHOD":
      return { ...state, requestMethod: action.method };
    case "SET_REQUEST_HEADERS":
      return { ...state, requestHeaders: action.headers };
    case "SET_REQUEST_BODY":
      return { ...state, requestBody: action.body };
    case "SET_API_KEY":
      return { ...state, selectedApiKey: action.apiKeyId };
    case "SET_MANUAL_TOKEN":
      return { ...state, manualApiToken: action.token };
    case "SET_PARAMETERS":
      return { ...state, parameters: action.parameters };
    case "SET_RESPONSE":
      return { ...state, response: action.response };
    case "SET_LOADING":
      return { ...state, loading: action.loading };
    case "REQUEST_START":
      return { ...state, loading: true, response: null };
    case "REQUEST_SUCCESS":
      return { ...state, loading: false, response: action.response, currentStep: "response" };
    case "REQUEST_ERROR":
      return { ...state, loading: false, response: action.response };
    case "SET_SIDEBAR":
      return { ...state, sidebarOpen: action.open };
    case "SET_ACTIVE_SCHEMA_ID":
      return { ...state, activeSchemaId: action.id };
    case "SYNC_API_KEY_HEADER":
      return { ...state, requestHeaders: action.headers };
    case "CLEAR_API_KEY_SELECTION":
      return { ...state, selectedApiKey: null };
    case "SYNC_URL":
      return { ...state, requestUrl: action.url };
    case "RESET":
      return createInitialState();
    default:
      return state;
  }
}
__name(reducer, "reducer");
var PlaygroundContext = createContext(void 0);
var usePlaygroundContext = /* @__PURE__ */ __name(() => {
  const context = useContext(PlaygroundContext);
  if (!context) throw new Error("usePlaygroundContext must be used within a PlaygroundProvider");
  return context;
}, "usePlaygroundContext");
var PlaygroundProvider = /* @__PURE__ */ __name(({ children, config }) => {
  const [state, dispatch] = useReducer(reducer, void 0, createInitialState);
  const abortControllerRef = useRef(null);
  const apiKeys = React6.useMemo(
    () => config.apiKeys ?? [],
    [config.apiKeys]
  );
  const isLoadingApiKeys = config.apiKeysLoading ?? false;
  const [storedApiKeyId, setStoredApiKeyId] = useSessionStorage(
    AUTH_KEY_STORAGE,
    null
  );
  const [storedBearer, setStoredBearer] = useSessionStorage(
    AUTH_BEARER_STORAGE,
    ""
  );
  const hasHydratedAuthRef = useRef(false);
  useEffect(() => {
    if (hasHydratedAuthRef.current) return;
    hasHydratedAuthRef.current = true;
    if (storedApiKeyId) dispatch({ type: "SET_API_KEY", apiKeyId: storedApiKeyId });
    if (storedBearer) dispatch({ type: "SET_MANUAL_TOKEN", token: storedBearer });
  }, []);
  useEffect(() => {
    if (!hasHydratedAuthRef.current) return;
    setStoredApiKeyId(state.selectedApiKey);
  }, [state.selectedApiKey, setStoredApiKeyId]);
  useEffect(() => {
    if (!hasHydratedAuthRef.current) return;
    setStoredBearer(state.manualApiToken);
  }, [state.manualApiToken, setStoredBearer]);
  useEffect(() => {
    if (!hasHydratedAuthRef.current) return;
    if (!isLoadingApiKeys && apiKeys.length > 0 && !state.selectedApiKey && !storedApiKeyId) {
      dispatch({ type: "SET_API_KEY", apiKeyId: apiKeys[0]?.id || null });
    }
  }, [apiKeys, isLoadingApiKeys, state.selectedApiKey, storedApiKeyId]);
  useEffect(() => {
    try {
      const headers = parseRequestHeaders(state.requestHeaders);
      if (state.selectedApiKey) {
        const apiKey = apiKeys.find((k) => k.id === state.selectedApiKey);
        if (!apiKey) {
          dispatch({ type: "CLEAR_API_KEY_SELECTION" });
          return;
        }
        const keyValue = apiKey.secret || apiKey.id;
        if (headers["X-API-Key"] !== keyValue) {
          headers["X-API-Key"] = keyValue;
          dispatch({ type: "SYNC_API_KEY_HEADER", headers: JSON.stringify(headers, null, 2) });
        }
      } else if (headers["X-API-Key"]) {
        delete headers["X-API-Key"];
        dispatch({ type: "SYNC_API_KEY_HEADER", headers: JSON.stringify(headers, null, 2) });
      }
    } catch (error) {
      consola2.error("Error updating headers:", error);
    }
  }, [state.selectedApiKey, apiKeys]);
  useEffect(() => {
    if (!state.selectedEndpoint) return;
    const updated = new UrlBuilder(state.selectedEndpoint, state.parameters).build();
    if (updated !== state.requestUrl) {
      dispatch({ type: "SYNC_URL", url: updated });
    }
  }, [state.parameters, state.selectedEndpoint]);
  const setCurrentStep = useCallback((step) => dispatch({ type: "SET_STEP", step }), []);
  const goToNextStep = useCallback(() => dispatch({ type: "NEXT_STEP" }), []);
  const goToPreviousStep = useCallback(() => dispatch({ type: "PREV_STEP" }), []);
  const setSelectedEndpoint = useCallback((endpoint) => dispatch({ type: "SELECT_ENDPOINT", endpoint }), []);
  const setSelectedCategory = useCallback((category) => dispatch({ type: "SET_CATEGORY", category }), []);
  const setSearchTerm = useCallback((term) => dispatch({ type: "SET_SEARCH", term }), []);
  const setSelectedVersion = useCallback((version) => dispatch({ type: "SET_VERSION", version }), []);
  const setRequestUrl = useCallback((url) => dispatch({ type: "SET_REQUEST_URL", url }), []);
  const setRequestMethod = useCallback((method) => dispatch({ type: "SET_REQUEST_METHOD", method }), []);
  const setRequestHeaders = useCallback((headers) => dispatch({ type: "SET_REQUEST_HEADERS", headers }), []);
  const setRequestBody = useCallback((body) => dispatch({ type: "SET_REQUEST_BODY", body }), []);
  const setSelectedApiKey = useCallback((apiKeyId) => dispatch({ type: "SET_API_KEY", apiKeyId }), []);
  const setManualApiToken = useCallback((token) => dispatch({ type: "SET_MANUAL_TOKEN", token }), []);
  const setParameters = useCallback((parameters) => dispatch({ type: "SET_PARAMETERS", parameters }), []);
  const setResponse = useCallback((response) => dispatch({ type: "SET_RESPONSE", response }), []);
  const setLoading = useCallback((loading) => dispatch({ type: "SET_LOADING", loading }), []);
  const setSidebarOpen = useCallback((open) => dispatch({ type: "SET_SIDEBAR", open }), []);
  const setActiveSchemaId = useCallback((id) => dispatch({ type: "SET_ACTIVE_SCHEMA_ID", id }), []);
  const clearAll = useCallback(() => dispatch({ type: "RESET" }), []);
  const sendRequest = useCallback(async () => {
    if (!state.requestUrl) {
      consola2.error("No URL provided");
      return;
    }
    abortControllerRef.current?.abort();
    const controller = new AbortController();
    abortControllerRef.current = controller;
    dispatch({ type: "REQUEST_START" });
    const startTime = Date.now();
    try {
      const headers = parseRequestHeaders(state.requestHeaders);
      let bearerToken = null;
      if (state.manualApiToken) {
        bearerToken = state.manualApiToken;
      } else if (typeof window !== "undefined") {
        bearerToken = window.localStorage.getItem("auth_token");
      }
      if (bearerToken) headers["Authorization"] = `Bearer ${bearerToken}`;
      const requestOptions = {
        method: state.requestMethod,
        headers,
        signal: controller.signal
      };
      if (state.requestBody && state.requestMethod !== "GET") {
        requestOptions.body = state.requestBody;
      }
      const response = await fetch(state.requestUrl, requestOptions);
      const duration = Date.now() - startTime;
      const responseText = await response.text();
      let responseData;
      try {
        responseData = JSON.parse(responseText);
      } catch {
        responseData = responseText;
      }
      dispatch({
        type: "REQUEST_SUCCESS",
        response: {
          status: response.status,
          statusText: response.statusText,
          headers: Object.fromEntries(response.headers.entries()),
          data: responseData,
          duration
        }
      });
      consola2.success(`${state.requestMethod} ${state.requestUrl} \u2192 ${response.status} (${duration}ms)`);
    } catch (error) {
      if (error instanceof DOMException && error.name === "AbortError") return;
      consola2.error("Request failed:", error);
      dispatch({
        type: "REQUEST_ERROR",
        response: {
          error: error instanceof Error ? error.message : "Request failed",
          duration: Date.now() - startTime
        }
      });
    }
  }, [state.requestUrl, state.requestHeaders, state.manualApiToken, state.requestMethod, state.requestBody]);
  const contextValue = {
    state,
    config,
    apiKeys,
    apiKeysLoading: isLoadingApiKeys,
    setCurrentStep,
    goToNextStep,
    goToPreviousStep,
    setSelectedEndpoint,
    setSelectedCategory,
    setSearchTerm,
    setSelectedVersion,
    setRequestUrl,
    setRequestMethod,
    setRequestHeaders,
    setRequestBody,
    setSelectedApiKey,
    setManualApiToken,
    setParameters,
    setResponse,
    setLoading,
    setSidebarOpen,
    setActiveSchemaId,
    clearAll,
    sendRequest
  };
  return /* @__PURE__ */ jsx(PlaygroundContext.Provider, { value: contextValue, children });
}, "PlaygroundProvider");

export { CODE_SAMPLE_TARGETS, MarkdownMessage, Mermaid_default, PlaygroundProvider, PrettyCode_default, UrlBuilder, buildHarRequest, deduplicateEndpoints, dereferenceSchema, endpointToMarkdown, extractTextFromChildren, findApiKeyById, formatBytes, isValidJson, joinUrl, parseRequestHeaders, relativePath, renderSnippet, resolveAbsolute, resolveBaseUrl, sampleSchemaJson, toCompactJson, toMarkdown, toRawJson, useCollapsibleContent, usePlaygroundContext };
//# sourceMappingURL=chunk-6HNAPVZ2.mjs.map
//# sourceMappingURL=chunk-6HNAPVZ2.mjs.map