Skip to main content

TEMPLATE_HTML

Constant TEMPLATE_HTML 

Source
pub const TEMPLATE_HTML: &str = "<!doctype html>\n<html lang=\"en\" data-theme=\"{{theme}}\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title></title>\n    <link rel=\"icon\" href=\"{{favicon}}\" />\n    <link\n      rel=\"stylesheet\"\n      href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css\"\n    />\n    <link\n      id=\"hljs-dark\"\n      rel=\"stylesheet\"\n      href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css\"\n    />\n    <link\n      id=\"hljs-light\"\n      rel=\"stylesheet\"\n      href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-light.min.css\"\n      disabled\n    />\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/bash.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/javascript.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/php.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/python.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/go.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/rust.min.js\"></script>\n    <link\n      rel=\"stylesheet\"\n      href=\"https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.15/codemirror.min.css\"\n    />\n    <link\n      rel=\"stylesheet\"\n      href=\"https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.15/theme/atom-one-dark.min.css\"\n    />\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.15/codemirror.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.15/mode/javascript/javascript.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.15/mode/xml/xml.min.js\"></script>\n    <style>\n      *,\n      *::before,\n      *::after {\n          box-sizing: border-box;\n          margin: 0;\n          padding: 0;\n      }\n\n      {{light}}\n\n      {{dark}}\n\n      :root,\n      [data-theme=\"dark\"] {\n          /* nav active state */\n          --nav-active-border: var(--accent);\n          --nav-active-bg: var(--accent-bg);\n\n          /* method badge border colours map to their method var */\n          --m-get: var(--green);\n          --m-post: var(--blue);\n          --m-put: var(--orange);\n          --m-patch: var(--purple);\n          --m-delete: var(--red);\n          --m-head: var(--cyan);\n      }\n\n      /* \u{2500}\u{2500} Element resets that depend on theme \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500} */\n      body {\n          background: var(--bg);\n          color: var(--t1);\n      }\n\n      ::-webkit-scrollbar-track {\n          background: var(--sb-t);\n      }\n      ::-webkit-scrollbar-thumb {\n          background: var(--sb-th);\n      }\n\n      /* \u{2500}\u{2500} Sidebar \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500} */\n      .sidebar {\n          background: var(--bg-sub);\n          border-right-color: var(--bd);\n      }\n      .topbar {\n          background: var(--bg-sub);\n          border-bottom-color: var(--bd);\n      }\n\n      .sb-title {\n          color: var(--t1);\n      }\n      .sb-ver {\n          color: var(--t3);\n          border-color: var(--bd);\n      }\n      .sb-oa {\n          color: var(--t4);\n      }\n\n      .sb-srch input {\n          background: var(--bg-card);\n          border-color: var(--bd);\n          color: var(--t1);\n      }\n      .sb-srch input::placeholder {\n          color: var(--t4);\n      }\n      .sb-srch input:focus {\n          border-color: var(--accent);\n      }\n      .sb-srch i {\n          color: var(--t4);\n      }\n\n      .sb-grp-hd {\n          color: var(--t3);\n      }\n      .sb-grp-hd .ln {\n          background: var(--bd);\n      }\n\n      .ep-btn:hover {\n          background: color-mix(in srgb, var(--t1) 4%, transparent);\n      }\n      .ep-btn.on {\n          border-left-color: var(--accent);\n          background: var(--accent-bg);\n      }\n      .ep-m {\n      }\n      .ep-p {\n          color: var(--t2);\n      }\n\n      .sb-foot {\n          color: var(--t3);\n          border-top-color: var(--bd);\n      }\n      .sb-foot i {\n          color: var(--green);\n      }\n\n      /* \u{2500}\u{2500} Theme toggle buttons \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500} */\n      .theme-btn {\n          background: transparent;\n          color: var(--t3);\n      }\n      .theme-btn:hover {\n          color: var(--t1);\n      }\n      .theme-btn.on {\n          color: var(--accent);\n          background: var(--accent-bg);\n      }\n\n      /* \u{2500}\u{2500} Topbar \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500} */\n      .server-pill {\n          background: var(--bg-card);\n          border-color: var(--bd);\n          color: var(--t2);\n      }\n      .server-url {\n          color: var(--t2);\n      }\n      .auth-badge {\n          color: var(--orange);\n          border-color: color-mix(\n              in srgb,\n              var(--orange) 40%,\n              transparent\n          );\n      }\n      .import-btn {\n          background: var(--bg-card);\n          border-color: var(--bd);\n          color: var(--t2);\n      }\n      .import-btn:hover {\n          border-color: var(--accent);\n          color: var(--accent);\n      }\n\n      /* \u{2500}\u{2500} Endpoint header \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500} */\n      .ep-summary {\n          color: var(--t1);\n      }\n      .ep-opid {\n          color: var(--t3);\n      }\n      .ep-path-pill {\n          color: var(--t1);\n      }\n      .auth-pill {\n          color: var(--orange);\n          border-color: var(--orange);\n      }\n\n      .mbg-get {\n          background: color-mix(in srgb, var(--green) 8%, transparent);\n      }\n      .mbg-post {\n          background: color-mix(in srgb, var(--blue) 8%, transparent);\n      }\n      .mbg-put {\n          background: color-mix(in srgb, var(--orange) 8%, transparent);\n      }\n      .mbg-patch {\n          background: color-mix(in srgb, var(--purple) 8%, transparent);\n      }\n      .mbg-delete {\n          background: color-mix(in srgb, var(--red) 8%, transparent);\n      }\n\n      .ep-hdr {\n          border-bottom-color: var(--bd);\n      }\n      .ep-body {\n      }\n\n      /* \u{2500}\u{2500} Section label \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500} */\n      .sec-lbl {\n          color: var(--t3);\n      }\n\n      /* \u{2500}\u{2500} Params / body tables \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500} */\n      .params-tbl {\n          border-color: var(--bd);\n      }\n      .p-row {\n          border-bottom-color: var(--bd-sub);\n      }\n      .p-name {\n          color: var(--t1);\n      }\n      .p-in {\n          color: var(--t3);\n          border-color: var(--bd);\n      }\n      .p-type {\n          color: var(--cyan);\n      }\n      .p-desc {\n          color: var(--t2);\n      }\n      .p-req {\n          color: var(--red);\n      }\n      .enum-chip {\n          background: var(--bg-code);\n          border-color: var(--bd);\n          color: var(--t2);\n      }\n\n      .rb-req {\n          color: var(--red);\n          border-color: var(--red);\n      }\n      .mp-note {\n          border-color: var(--bd);\n          color: var(--t2);\n      }\n      .mp-note i {\n          color: var(--orange);\n      }\n\n      /* \u{2500}\u{2500} Code blocks \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500} */\n      .code-wrap {\n          border-color: var(--bd);\n      }\n      .code-hd {\n          background: var(--bg-code);\n          border-bottom-color: var(--bd);\n      }\n      .code-hd-lbl {\n          color: var(--t3);\n      }\n      .code-wrap pre {\n          background: var(--bg-code) !important;\n      }\n\n      /* \u{2500}\u{2500} Language tabs \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500} */\n      .lang-tabs {\n          background: var(--bg-code);\n          border-bottom-color: var(--bd);\n      }\n      .lang-tab {\n          color: var(--t3);\n          border-bottom-color: transparent;\n      }\n      .lang-tab:hover {\n          color: var(--t1);\n      }\n      .lang-tab.on {\n          color: var(--accent);\n          border-bottom-color: var(--accent);\n      }\n\n      /* \u{2500}\u{2500} Response tabs \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500} */\n      .resp-tab {\n          border-color: var(--bd);\n          color: var(--t3);\n      }\n      .resp-tab:hover {\n          border-color: var(--t3);\n          color: var(--t1);\n      }\n      .resp-tab.on {\n          background: var(--bg-card);\n          color: var(--t1);\n          border-color: var(--t2);\n      }\n      .resp-box {\n          border-color: var(--bd);\n      }\n\n      .s2 {\n          color: var(--s2xx);\n      }\n      .s3 {\n          color: var(--s3xx);\n      }\n      .s4 {\n          color: var(--s4xx);\n      }\n      .s5 {\n          color: var(--s5xx);\n      }\n\n      /* \u{2500}\u{2500} Copy button \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500} */\n      .copy-btn {\n          background: var(--bg-sub);\n          border-color: var(--bd);\n          color: var(--t3);\n      }\n      .copy-btn:hover {\n          color: var(--t1);\n          border-color: var(--t3);\n      }\n      .copy-btn.ok {\n          color: var(--green);\n          border-color: var(--green);\n      }\n\n      /* \u{2500}\u{2500} Method badge border colours \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500} */\n      .mb-get {\n          border-color: var(--green);\n      }\n      .mb-post {\n          border-color: var(--blue);\n      }\n      .mb-put {\n          border-color: var(--orange);\n      }\n      .mb-patch {\n          border-color: var(--purple);\n      }\n      .mb-delete {\n          border-color: var(--red);\n      }\n\n      /* \u{2500}\u{2500} Method text colours \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500} */\n      .m-get {\n          color: var(--green);\n      }\n      .m-post {\n          color: var(--blue);\n      }\n      .m-put {\n          color: var(--orange);\n      }\n      .m-patch {\n          color: var(--purple);\n      }\n      .m-delete {\n          color: var(--red);\n      }\n      .m-head {\n          color: var(--cyan);\n      }\n\n      /* \u{2500}\u{2500} Sidebar logo \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500} */\n      .sb-logo {\n          background: var(--accent);\n      }\n\n      /* \u{2500}\u{2500} Empty state \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500} */\n      .empty {\n          color: var(--t3);\n      }\n\n      /* \u{2500}\u{2500} Import modal \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500} */\n      .modal-ov {\n          background: rgba(0, 0, 0, 0.55);\n      }\n      .modal-box {\n          background: var(--bg-card);\n          border-color: var(--bd);\n          box-shadow: var(--shadow-md);\n      }\n      .modal-hd {\n          border-bottom-color: var(--bd);\n      }\n      .modal-ttl {\n          color: var(--t1);\n      }\n      .modal-sub {\n          color: var(--t3);\n      }\n      .modal-cls {\n          color: var(--t3);\n      }\n      .modal-cls:hover {\n          color: var(--t1);\n      }\n      .modal-ta {\n          background: var(--bg-code);\n          border-color: var(--bd);\n          color: var(--t1);\n      }\n      .modal-ta:focus {\n          border-color: var(--accent);\n      }\n      .modal-err {\n          color: var(--red);\n      }\n\n      /* \u{2500}\u{2500} Buttons \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500} */\n      .btn-p {\n          background: var(--accent);\n          color: #fff;\n      }\n      .btn-p:hover {\n          background: var(--accent-h);\n          opacity: 1;\n      }\n      .btn-s {\n          border-color: var(--bd);\n          color: var(--t2);\n      }\n      .btn-s:hover {\n          border-color: var(--t2);\n          color: var(--t1);\n      }\n      html,\n      body,\n      #root {\n          height: 100%;\n      }\n      body {\n          font-family:\n              -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto,\n              \"Helvetica Neue\", Arial, sans-serif;\n          font-size: 14px;\n          line-height: 1.5;\n          background: var(--bg);\n          color: var(--t1);\n          transition:\n              background 0.2s,\n              color 0.2s;\n      }\n      ::-webkit-scrollbar {\n          width: 5px;\n          height: 5px;\n      }\n      ::-webkit-scrollbar-track {\n          background: var(--sb-t);\n      }\n      ::-webkit-scrollbar-thumb {\n          background: var(--sb-th);\n          border-radius: 3px;\n      }\n      button {\n          font-family: inherit;\n          cursor: pointer;\n      }\n      input,\n      textarea {\n          font-family: inherit;\n      }\n      a {\n          text-decoration: none;\n      }\n      .app {\n          display: flex;\n          height: 100vh;\n          overflow: hidden;\n      }\n      .sidebar {\n          width: 265px;\n          flex-shrink: 0;\n          display: flex;\n          flex-direction: column;\n          background: var(--bg-sub);\n          border-right: 1px solid var(--bd);\n          overflow: hidden;\n      }\n      .main {\n          flex: 1;\n          display: flex;\n          flex-direction: column;\n          overflow: hidden;\n          min-width: 0;\n      }\n      .topbar {\n          display: flex;\n          align-items: center;\n          justify-content: space-between;\n          padding: 0 24px;\n          height: 48px;\n          flex-shrink: 0;\n          background: var(--bg-sub);\n          border-bottom: 1px solid var(--bd);\n          gap: 12px;\n      }\n      .detail {\n          flex: 1;\n          overflow-y: auto;\n      }\n      .sb-hdr {\n          padding: 14px 16px;\n          border-bottom: 1px solid var(--bd);\n          flex-shrink: 0;\n          display: flex;\n          align-items: center;\n          justify-content: space-between;\n      }\n      .sb-brand {\n          display: flex;\n          align-items: center;\n          gap: 10px;\n          flex: 1;\n      }\n      .sb-logo {\n          width: auto;\n          height: 32px;\n          border-radius: 8px;\n          overflow: hidden;\n          flex-shrink: 0;\n      }\n      .sb-logo.sb-logo-img {\n          background: transparent;\n      }\n      .sb-logo img {\n          width: 100%;\n          height: 100%;\n          object-fit: contain;\n          background: transparent;\n      }\n      .sb-title {\n          font-size: 14px;\n          font-weight: 700;\n          color: var(--t1);\n      }\n      .sb-meta {\n          display: flex;\n          align-items: center;\n          gap: 6px;\n          margin-top: 3px;\n      }\n      .sb-ver {\n          font-size: 10px;\n          font-family: monospace;\n          color: var(--t3);\n          border: 1px solid var(--bd);\n          border-radius: 3px;\n          padding: 1px 5px;\n      }\n      .sb-oa {\n          font-size: 10px;\n          color: var(--t4);\n      }\n      .sb-themes {\n          display: flex;\n          gap: 4px;\n          padding: 8px 12px;\n          flex-shrink: 0;\n          margin-left: auto;\n      }\n      .theme-btn {\n          width: 32px;\n          height: 32px;\n          display: flex;\n          align-items: center;\n          justify-content: center;\n          border-radius: 5px;\n          font-size: 14px;\n          background: transparent;\n          border: 1px solid transparent;\n          color: var(--t3);\n          transition: all 0.15s;\n      }\n      .theme-btn:hover {\n          color: var(--t1);\n      }\n      .theme-btn.on {\n          color: var(--accent);\n          background: color-mix(in srgb, var(--accent) 8%, transparent);\n      }\n      .theme-btn:hover {\n          color: var(--t1);\n      }\n      .theme-btn.on {\n          color: var(--accent);\n          border-color: var(--accent);\n          background: color-mix(in srgb, var(--accent) 8%, transparent);\n      }\n      .sb-srch {\n          padding: 8px 12px;\n          border-bottom: 1px solid var(--bd);\n          flex-shrink: 0;\n          position: relative;\n      }\n      .sb-srch i {\n          position: absolute;\n          left: 23px;\n          top: 50%;\n          transform: translateY(-50%);\n          color: var(--t4);\n          font-size: 11px;\n          pointer-events: none;\n      }\n      .sb-srch input {\n          width: 100%;\n          padding: 7px 10px 7px 30px;\n          border-radius: 6px;\n          border: 1px solid var(--bd);\n          background: var(--bg-card);\n          color: var(--t1);\n          font-size: 12px;\n          transition: border-color 0.15s;\n      }\n      .sb-srch input::placeholder {\n          color: var(--t4);\n      }\n      .sb-srch input:focus {\n          outline: none;\n          border-color: var(--accent);\n      }\n      .sb-nav {\n          flex: 1;\n          overflow-y: auto;\n          padding: 4px 0 8px;\n      }\n      .sb-grp-hd {\n          display: flex;\n          align-items: center;\n          gap: 8px;\n          padding: 8px 16px 3px;\n          font-size: 11px;\n          font-weight: 600;\n          color: var(--t3);\n          text-transform: uppercase;\n          letter-spacing: 0.06em;\n          cursor: pointer;\n          user-select: none;\n          transition: all 0.15s;\n      }\n      .sb-grp-hd:hover {\n          color: var(--t1);\n      }\n      .ep-btn {\n          width: 100%;\n          text-align: left;\n          padding: 7px 16px;\n          display: flex;\n          align-items: center;\n          gap: 10px;\n          background: none;\n          border: none;\n          border-left: 2px solid transparent;\n          transition:\n              background 0.12s,\n              border-color 0.12s;\n      }\n\n      .ep-btn:hover {\n          background: color-mix(in srgb, var(--t1) 4%, transparent);\n      }\n      .ep-btn.on {\n          border-left-color: var(--accent);\n          background: color-mix(in srgb, var(--accent) 6%, transparent);\n      }\n      .ep-m {\n          font-family: monospace;\n          font-size: 10px;\n          font-weight: 700;\n          width: 46px;\n          text-align: right;\n          flex-shrink: 0;\n      }\n      .ep-p {\n          font-size: 12px;\n          color: var(--t2);\n          white-space: nowrap;\n          overflow: hidden;\n          text-overflow: ellipsis;\n          font-family: monospace;\n      }\n      .sb-foot {\n          padding: 10px 16px;\n          border-top: 1px solid var(--bd);\n          flex-shrink: 0;\n          display: flex;\n          align-items: center;\n          justify-content: space-between;\n          font-size: 11px;\n          color: var(--t3);\n      }\n      .m-get {\n          color: var(--green);\n      }\n      .m-post {\n          color: var(--accent);\n      }\n      .m-put {\n          color: var(--orange);\n      }\n      .m-patch {\n          color: var(--purple);\n      }\n      .m-delete {\n          color: var(--red);\n      }\n      .m-head {\n          color: var(--cyan);\n      }\n      .mb-get {\n          border-color: var(--green);\n      }\n      .mb-post {\n          border-color: var(--accent);\n      }\n      .mb-put {\n          border-color: var(--orange);\n      }\n      .mb-patch {\n          border-color: var(--purple);\n      }\n      .mb-delete {\n          border-color: var(--red);\n      }\n      .mbg-get {\n          background: color-mix(in srgb, var(--green) 8%, transparent);\n      }\n      .mbg-post {\n          background: color-mix(in srgb, var(--accent) 8%, transparent);\n      }\n      .mbg-put {\n          background: color-mix(in srgb, var(--orange) 8%, transparent);\n      }\n      .mbg-patch {\n          background: color-mix(in srgb, var(--purple) 8%, transparent);\n      }\n      .mbg-delete {\n          background: color-mix(in srgb, var(--red) 8%, transparent);\n      }\n      .method-badge {\n          font-family: monospace;\n          font-size: 10px;\n          font-weight: 700;\n          letter-spacing: 0.08em;\n          text-transform: uppercase;\n          padding: 2px 8px;\n          border-radius: 4px;\n          border: 1px solid;\n      }\n      .server-pill {\n          display: flex;\n          align-items: center;\n          gap: 6px;\n          font-size: 11px;\n          background: var(--bg-card);\n          border: 1px solid var(--bd);\n          border-radius: 5px;\n          padding: 4px 10px;\n          color: var(--t2);\n      }\n      .server-url {\n          font-family: monospace;\n      }\n      .auth-badge {\n          display: flex;\n          align-items: center;\n          gap: 5px;\n          font-size: 11px;\n          color: var(--orange);\n          border: 1px solid\n              color-mix(in srgb, var(--orange) 40%, transparent);\n          border-radius: 5px;\n          padding: 4px 10px;\n      }\n      .import-btn {\n          display: flex;\n          align-items: center;\n          gap: 6px;\n          font-size: 12px;\n          background: var(--bg-card);\n          border: 1px solid var(--bd);\n          color: var(--t2);\n          border-radius: 6px;\n          padding: 6px 12px;\n          transition: all 0.15s;\n      }\n      .import-btn:hover {\n          border-color: var(--accent);\n          color: var(--accent);\n      }\n      .ep-hdr {\n          padding: 24px 32px;\n          border-bottom: 1px solid var(--bd);\n      }\n      .ep-hdr-row {\n          display: flex;\n          align-items: flex-start;\n          gap: 16px;\n      }\n      .ep-icon {\n          width: 40px;\n          height: 40px;\n          border-radius: 8px;\n          border: 1px solid;\n          display: flex;\n          align-items: center;\n          justify-content: center;\n          font-size: 15px;\n          flex-shrink: 0;\n      }\n      .ep-summary {\n          font-size: 20px;\n          font-weight: 700;\n          color: var(--t1);\n          margin-top: 6px;\n      }\n      .ep-opid {\n          font-family: monospace;\n          font-size: 10px;\n          color: var(--t3);\n          margin-top: 4px;\n      }\n      .ep-path-pill {\n          font-family: monospace;\n          font-size: 13px;\n          color: var(--t1);\n      }\n      .auth-pill {\n          display: inline-flex;\n          align-items: center;\n          gap: 4px;\n          font-size: 10px;\n          color: var(--orange);\n          border: 1px solid var(--orange);\n          border-radius: 4px;\n          padding: 2px 7px;\n      }\n      .ep-body {\n          padding: 24px 32px;\n      }\n      .sec-lbl {\n          font-size: 11px;\n          font-weight: 600;\n          color: var(--t3);\n          text-transform: uppercase;\n          letter-spacing: 0.08em;\n          margin-bottom: 10px;\n      }\n      .params-tbl {\n          border: 1px solid var(--bd);\n          border-radius: 8px;\n          overflow: hidden;\n          margin-bottom: 20px;\n      }\n      .p-row {\n          display: flex;\n          align-items: flex-start;\n          gap: 14px;\n          padding: 11px 16px;\n          font-size: 13px;\n          border-bottom: 1px solid var(--bd-sub);\n      }\n      .p-row:last-child {\n          border-bottom: none;\n      }\n      .p-name {\n          width: 130px;\n          flex-shrink: 0;\n          font-family: monospace;\n          font-weight: 600;\n          font-size: 12px;\n      }\n      .p-meta {\n          width: 90px;\n          flex-shrink: 0;\n          display: flex;\n          flex-direction: column;\n          gap: 3px;\n      }\n      .p-in {\n          font-size: 10px;\n          font-family: monospace;\n          border: 1px solid var(--bd);\n          border-radius: 3px;\n          padding: 1px 5px;\n          color: var(--t3);\n          width: fit-content;\n      }\n      .p-type {\n          font-size: 10px;\n          color: var(--cyan);\n          font-family: monospace;\n      }\n      .p-desc {\n          flex: 1;\n          color: var(--t2);\n          line-height: 1.5;\n      }\n      .p-req {\n          color: var(--red);\n          font-size: 10px;\n          margin-left: 2px;\n      }\n      .enum-chip {\n          display: inline-block;\n          font-family: monospace;\n          font-size: 10px;\n          background: var(--bg-code);\n          border: 1px solid var(--bd);\n          border-radius: 3px;\n          padding: 1px 6px;\n          color: var(--t2);\n          margin: 2px 2px 0 0;\n      }\n      .code-wrap {\n          border: 1px solid var(--bd);\n          border-radius: 8px;\n          overflow: hidden;\n          margin-bottom: 16px;\n      }\n      .code-hd {\n          display: flex;\n          align-items: center;\n          justify-content: space-between;\n          padding: 7px 14px;\n          background: var(--bg-code);\n          border-bottom: 1px solid var(--bd);\n      }\n      .code-hd-lbl {\n          font-size: 10px;\n          font-weight: 600;\n          text-transform: uppercase;\n          letter-spacing: 0.08em;\n          color: var(--t3);\n      }\n      .code-wrap pre {\n          padding: 16px;\n          overflow-x: auto;\n          background: var(--bg-code) !important;\n          margin: 0;\n      }\n      .hljs {\n          background: transparent !important;\n          padding: 0 !important;\n          font-size: 12px !important;\n          line-height: 1.65 !important;\n          font-family:\n              ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,\n              monospace !important;\n      }\n      .copy-btn {\n          display: inline-flex;\n          align-items: center;\n          gap: 5px;\n          font-size: 11px;\n          background: var(--bg-sub);\n          border: 1px solid var(--bd);\n          color: var(--t3);\n          border-radius: 4px;\n          padding: 3px 9px;\n          transition: all 0.15s;\n      }\n      .copy-btn:hover {\n          color: var(--t1);\n          border-color: var(--t3);\n      }\n      .copy-btn.ok {\n          color: var(--green);\n          border-color: var(--green);\n      }\n      .lang-tabs {\n          display: flex;\n          overflow-x: auto;\n          background: var(--bg-code);\n          border-bottom: 1px solid var(--bd);\n      }\n      .lang-tab {\n          padding: 8px 14px;\n          font-size: 12px;\n          white-space: nowrap;\n          background: none;\n          border: none;\n          border-bottom: 2px solid transparent;\n          color: var(--t3);\n          transition: all 0.15s;\n          display: flex;\n          align-items: center;\n          gap: 6px;\n      }\n      .lang-tab:hover {\n          color: var(--t1);\n      }\n      .lang-tab.on {\n          color: var(--accent);\n          border-bottom-color: var(--accent);\n      }\n      .resp-tabs {\n          display: flex;\n          flex-wrap: wrap;\n          gap: 6px;\n          margin-bottom: 12px;\n      }\n      .resp-tab {\n          font-family: monospace;\n          font-size: 12px;\n          border: 1px solid var(--bd);\n          border-radius: 5px;\n          padding: 4px 12px;\n          background: transparent;\n          color: var(--t3);\n          transition: all 0.15s;\n      }\n      .resp-tab:hover {\n          border-color: var(--t3);\n          color: var(--t1);\n      }\n      .resp-tab.on {\n          background: var(--bg-card);\n          color: var(--t1);\n          border-color: var(--t2);\n      }\n      .resp-box {\n          border: 1px solid var(--bd);\n          border-radius: 8px;\n          padding: 14px 16px;\n      }\n      .s2 {\n          color: var(--green);\n      }\n      .s3 {\n          color: var(--cyan);\n      }\n      .s4 {\n          color: var(--orange);\n      }\n      .s5 {\n          color: var(--red);\n      }\n      .rb-hd {\n          display: flex;\n          align-items: center;\n          justify-content: space-between;\n          margin-bottom: 10px;\n      }\n      .rb-req {\n          font-size: 10px;\n          color: var(--red);\n          border: 1px solid var(--red);\n          border-radius: 3px;\n          padding: 1px 7px;\n      }\n      .mp-note {\n          padding: 10px 14px;\n          border: 1px solid var(--bd);\n          border-radius: 6px;\n          font-size: 12px;\n          color: var(--t2);\n          margin-bottom: 12px;\n      }\n      .empty {\n          height: 100%;\n          display: flex;\n          align-items: center;\n          justify-content: center;\n          flex-direction: column;\n          gap: 12px;\n          color: var(--t3);\n      }\n      .empty i {\n          font-size: 36px;\n          opacity: 0.2;\n      }\n      .empty p {\n          font-size: 13px;\n      }\n      .modal-ov {\n          position: fixed;\n          inset: 0;\n          background: rgba(0, 0, 0, 0.55);\n          backdrop-filter: blur(4px);\n          display: flex;\n          align-items: center;\n          justify-content: center;\n          z-index: 200;\n          padding: 16px;\n      }\n      .modal-box {\n          background: var(--bg-card);\n          border: 1px solid var(--bd);\n          border-radius: 12px;\n          width: 100%;\n          max-width: 620px;\n          box-shadow: 0 24px 64px rgba(0, 0, 0, 0.3);\n      }\n      .modal-hd {\n          display: flex;\n          align-items: center;\n          justify-content: space-between;\n          padding: 18px 24px;\n          border-bottom: 1px solid var(--bd);\n      }\n      .modal-ttl {\n          font-size: 16px;\n          font-weight: 700;\n          color: var(--t1);\n      }\n      .modal-sub {\n          font-size: 11px;\n          color: var(--t3);\n          margin-top: 2px;\n      }\n      .modal-cls {\n          background: none;\n          border: none;\n          color: var(--t3);\n          font-size: 18px;\n          padding: 4px;\n          border-radius: 4px;\n          transition: color 0.15s;\n      }\n      .modal-cls:hover {\n          color: var(--t1);\n      }\n      .modal-bdy {\n          padding: 20px 24px;\n      }\n      .modal-ta {\n          width: 100%;\n          height: 240px;\n          resize: none;\n          padding: 12px;\n          background: var(--bg-code);\n          border: 1px solid var(--bd);\n          border-radius: 6px;\n          color: var(--t1);\n          font-family: monospace;\n          font-size: 12px;\n      }\n      .modal-ta:focus {\n          outline: none;\n          border-color: var(--accent);\n      }\n      .modal-err {\n          font-size: 12px;\n          color: var(--red);\n          margin-top: 8px;\n          display: flex;\n          align-items: center;\n          gap: 6px;\n      }\n      .modal-btns {\n          display: grid;\n          grid-template-columns: 1fr 1fr;\n          gap: 10px;\n          margin-top: 16px;\n      }\n      .btn-p {\n          background: var(--accent);\n          color: #fff;\n          border: none;\n          border-radius: 6px;\n          padding: 9px 0;\n          font-size: 13px;\n          font-weight: 600;\n          transition: opacity 0.15s;\n          width: 100%;\n      }\n      .btn-p:hover {\n          opacity: 0.88;\n      }\n      .btn-s {\n          background: transparent;\n          border: 1px solid var(--bd);\n          color: var(--t2);\n          border-radius: 6px;\n          padding: 9px 0;\n          font-size: 13px;\n          transition: all 0.15s;\n          width: 100%;\n      }\n      .btn-s:hover {\n          border-color: var(--t2);\n          color: var(--t1);\n      }\n      @keyframes fadeUp {\n          from {\n              opacity: 0;\n              transform: translateY(6px);\n          }\n          to {\n              opacity: 1;\n              transform: translateY(0);\n          }\n      }\n      .fu {\n          animation: fadeUp 0.25s ease forwards;\n      }\n\n      /* \u{2500}\u{2500} API Testing UI \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500} */\n      .test-panel {\n          background: var(--bg-card);\n          border: 1px solid var(--bd);\n          border-radius: 8px;\n          padding: 16px;\n          margin-bottom: 20px;\n      }\n      .test-panel-hd {\n          display: flex;\n          align-items: center;\n          justify-content: space-between;\n          margin-bottom: 12px;\n      }\n      .test-panel-ttl {\n          font-size: 13px;\n          font-weight: 700;\n          color: var(--t1);\n          display: flex;\n          align-items: center;\n          gap: 8px;\n      }\n      .auth-input-grp {\n          display: flex;\n          gap: 8px;\n          align-items: center;\n          margin-bottom: 12px;\n      }\n      .auth-input {\n          flex: 1;\n          padding: 8px 12px;\n          border-radius: 6px;\n          border: 1px solid var(--bd);\n          background: var(--bg-code);\n          color: var(--t1);\n          font-size: 12px;\n          font-family: monospace;\n          transition: border-color 0.15s;\n      }\n      .auth-input:focus {\n          outline: none;\n          border-color: var(--accent);\n      }\n      .auth-input::placeholder {\n          color: var(--t3);\n      }\n      .btn-test {\n          background: var(--accent);\n          color: #fff;\n          border: none;\n          border-radius: 6px;\n          padding: 8px 16px;\n          font-size: 12px;\n          font-weight: 600;\n          cursor: pointer;\n          transition: all 0.15s;\n          display: flex;\n          align-items: center;\n          gap: 6px;\n      }\n      .btn-test:hover {\n          background: var(--accent-h);\n          transform: translateY(-1px);\n      }\n      .btn-test:disabled {\n          opacity: 0.5;\n          cursor: not-allowed;\n          transform: none;\n      }\n      .btn-test.loading {\n          animation: pulse 1.5s ease-in-out infinite;\n      }\n      @keyframes pulse {\n          0%,\n          100% {\n              opacity: 1;\n          }\n          50% {\n              opacity: 0.6;\n          }\n      }\n      .param-input-row {\n          display: flex;\n          gap: 10px;\n          align-items: center;\n          margin-bottom: 10px;\n      }\n      .param-input-row:last-child {\n          margin-bottom: 0;\n      }\n      .param-label {\n          width: 120px;\n          flex-shrink: 0;\n          font-family: monospace;\n          font-size: 12px;\n          color: var(--t2);\n      }\n      .param-input {\n          flex: 1;\n          padding: 8px 12px;\n          border-radius: 6px;\n          border: 1px solid var(--bd);\n          background: var(--bg-code);\n          color: var(--t1);\n          font-size: 12px;\n          font-family: monospace;\n          transition: border-color 0.15s;\n      }\n      .param-input:focus {\n          outline: none;\n          border-color: var(--accent);\n      }\n      .param-input::placeholder {\n          color: var(--t3);\n      }\n      .param-type {\n          width: 100px;\n          flex-shrink: 0;\n          font-size: 10px;\n          color: var(--cyan);\n          font-family: monospace;\n          text-align: right;\n      }\n      .req-body-ta {\n          width: 100%;\n          min-height: 150px;\n          padding: 12px;\n          border-radius: 6px;\n          border: 1px solid var(--bd);\n          background: var(--bg-code);\n          color: var(--t1);\n          font-size: 12px;\n          font-family:\n              ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,\n              monospace;\n          resize: vertical;\n          transition: border-color 0.15s;\n      }\n      .req-body-ta:focus {\n          outline: none;\n          border-color: var(--accent);\n      }\n      .code-editor-wrapper {\n          border: 1px solid var(--bd);\n          border-radius: 6px;\n          overflow: hidden;\n      }\n      .code-editor-wrapper .CodeMirror {\n          height: auto;\n          min-height: 70px;\n          font-family:\n              ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,\n              monospace;\n          font-size: 12px;\n          line-height: 1.5;\n          color: var(--t1);\n          background: var(--bg-code);\n      }\n      .code-editor-wrapper .CodeMirror-scroll {\n          height: auto;\n          overflow-y: hidden;\n          overflow-x: auto;\n          min-height: 70px;\n      }\n      .code-editor-wrapper .CodeMirror-sizer {\n          min-height: 70px !important;\n      }\n      .code-editor-wrapper .CodeMirror-gutters {\n          background: var(--bg-code);\n          border-right: 1px solid var(--bd);\n          min-height: 70px;\n      }\n      .code-editor-wrapper .CodeMirror-linenumber {\n          color: var(--t3);\n      }\n      .code-editor-wrapper .error-gutter {\n          width: 20px;\n      }\n      .code-editor-wrapper .CodeMirror-cursor {\n          border-color: var(--accent);\n      }\n      .code-editor-wrapper .CodeMirror-selected {\n          background: color-mix(\n              in srgb,\n              var(--accent) 20%,\n              transparent\n          ) !important;\n      }\n      .code-editor-wrapper .cm-property {\n          color: #d19a66 !important;\n      }\n      .code-editor-wrapper .cm-attribute {\n          color: #d19a66 !important;\n      }\n      .code-editor-wrapper .cm-string {\n          color: #98c379;\n      }\n      .code-editor-wrapper .cm-number {\n          color: #d19a66;\n      }\n      .code-editor-wrapper .cm-atom {\n          color: #c678dd;\n      }\n      .code-editor-wrapper .cm-bool {\n          color: #d19a66;\n      }\n      .code-editor-wrapper .cm-bracket {\n          color: #abb2bf;\n      }\n      .code-editor-wrapper .cm-punctuation {\n          color: #abb2bf;\n      }\n      .code-editor-wrapper .cm-json-error {\n          background: rgba(224, 92, 75, 0.2);\n          border-bottom: 2px wavy #e05c4b;\n      }\n      .json-error-hint {\n          display: flex;\n          align-items: center;\n          gap: 6px;\n          font-size: 11px;\n          color: #e05c4b;\n          background: rgba(224, 92, 75, 0.1);\n          padding: 6px 10px;\n          border-radius: 4px;\n          margin-top: 8px;\n      }\n      .auth-header-input {\n          margin-bottom: 12px;\n      }\n      .auth-header-label {\n          font-size: 11px;\n          font-weight: 600;\n          color: var(--t3);\n          text-transform: uppercase;\n          letter-spacing: 0.08em;\n          margin-bottom: 6px;\n          display: flex;\n          align-items: center;\n          gap: 6px;\n      }\n      .auth-header-input input {\n          width: 100%;\n          padding: 8px 12px;\n          border-radius: 6px;\n          border: 1px solid var(--bd);\n          background: var(--bg-code);\n          color: var(--t1);\n          font-size: 12px;\n          font-family: monospace;\n          transition: border-color 0.15s;\n      }\n      .auth-header-input input:focus {\n          outline: none;\n          border-color: var(--accent);\n      }\n      .auth-header-input input::placeholder {\n          color: var(--t3);\n      }\n      .response-area {\n          margin-top: 16px;\n      }\n      .response-hd {\n          display: flex;\n          align-items: center;\n          justify-content: space-between;\n          margin-bottom: 10px;\n      }\n      .response-ttl {\n          font-size: 12px;\n          font-weight: 700;\n          color: var(--t1);\n          display: flex;\n          align-items: center;\n          gap: 8px;\n      }\n      .response-status {\n          font-family: monospace;\n          font-size: 11px;\n          padding: 3px 8px;\n          border-radius: 4px;\n          border: 1px solid;\n      }\n      .response-status.success {\n          color: var(--green);\n          border-color: var(--green);\n          background: color-mix(in srgb, var(--green) 8%, transparent);\n      }\n      .response-status.error {\n          color: var(--red);\n          border-color: var(--red);\n          background: color-mix(in srgb, var(--red) 8%, transparent);\n      }\n      .response-status.info {\n          color: var(--cyan);\n          border-color: var(--cyan);\n          background: color-mix(in srgb, var(--cyan) 8%, transparent);\n      }\n      .response-box {\n          border: 1px solid var(--bd);\n          border-radius: 6px;\n          overflow: hidden;\n      }\n      .response-pre {\n          margin: 0;\n          padding: 14px;\n          background: var(--bg-code);\n          overflow-x: auto;\n          max-height: 400px;\n          overflow-y: auto;\n      }\n      .response-code {\n          font-family:\n              ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,\n              monospace;\n          font-size: 11px;\n          line-height: 1.6;\n          color: var(--t1);\n      }\n      .empty-response {\n          padding: 24px;\n          text-align: center;\n          color: var(--t3);\n          font-size: 12px;\n      }\n      .empty-response i {\n          font-size: 24px;\n          margin-bottom: 8px;\n          opacity: 0.3;\n      }\n      .test-url {\n          font-family: monospace;\n          font-size: 11px;\n          color: var(--t2);\n          background: var(--bg-code);\n          padding: 6px 10px;\n          border-radius: 4px;\n          margin-bottom: 12px;\n          overflow-x: auto;\n          white-space: nowrap;\n      }\n      .test-method {\n          font-family: monospace;\n          font-size: 10px;\n          font-weight: 700;\n          padding: 2px 6px;\n          border-radius: 3px;\n          border: 1px solid;\n          margin-right: 6px;\n      }\n      .auth-status {\n          display: flex;\n          align-items: center;\n          gap: 6px;\n          font-size: 11px;\n          color: var(--t3);\n          padding: 6px 10px;\n          background: var(--bg-code);\n          border-radius: 6px;\n      }\n      .auth-status.set {\n          color: var(--green);\n          background: color-mix(in srgb, var(--green) 8%, transparent);\n      }\n      .auth-status i {\n          font-size: 10px;\n      }\n      .test-tabs {\n          display: flex;\n          gap: 4px;\n          margin-bottom: 12px;\n          border-bottom: 1px solid var(--bd);\n          padding-bottom: 8px;\n      }\n      .test-tab {\n          padding: 6px 12px;\n          font-size: 11px;\n          font-weight: 600;\n          color: var(--t3);\n          background: transparent;\n          border: none;\n          border-radius: 4px;\n          cursor: pointer;\n          transition: all 0.15s;\n      }\n      .test-tab:hover {\n          color: var(--t1);\n          background: var(--bg-code);\n      }\n      .test-tab.on {\n          color: var(--accent);\n          background: var(--accent-bg);\n      }\n      .test-tab-icon {\n          margin-right: 4px;\n          font-size: 10px;\n      }\n    </style>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\">\n      import {\n        createElement as h,\n        useState,\n        useEffect,\n        useRef,\n        useCallback,\n        useMemo,\n        Fragment,\n      } from \"https://esm.sh/react@19\";\n      import { createRoot } from \"https://esm.sh/react-dom@19/client\";\n\n      // Injected OpenAPI spec - replaced by Rust template engine\n      const INJECTED_SPEC = /* SPEC_JSON_PLACEHOLDER */ null;\n      // Embedded sample data for \"Try Sample API\" button\n      const SAMPLE_DATA = /* SAMPLE_DATA_PLACEHOLDER */ null;\n\n      const METHOD_ORDER = [\n        \"get\",\n        \"post\",\n        \"put\",\n        \"patch\",\n        \"delete\",\n        \"head\",\n        \"options\",\n      ];\n      const METHOD_ICON = {\n        get: \"fa-download\",\n        post: \"fa-plus\",\n        put: \"fa-rotate-right\",\n        patch: \"fa-pen\",\n        delete: \"fa-trash\",\n        head: \"fa-eye\",\n        options: \"fa-gear\",\n      };\n      const LANGS = [\n        { id: \"curl\", label: \"cURL\", fa: \"fa fa-terminal\", hl: \"bash\" },\n        {\n          id: \"javascript\",\n          label: \"JavaScript\",\n          fa: \"fab fa-js\",\n          hl: \"javascript\",\n        },\n        {\n          id: \"python\",\n          label: \"Python\",\n          fa: \"fab fa-python\",\n          hl: \"python\",\n        },\n        { id: \"php\", label: \"PHP\", fa: \"fab fa-php\", hl: \"php\" },\n        { id: \"go\", label: \"Go\", fa: \"fab fa-golang\", hl: \"go\" },\n        { id: \"rust\", label: \"Rust\", fa: \"fab fa-rust\", hl: \"rust\" },\n      ];\n\n      function sCls(c) {\n        const n = parseInt(c);\n        return n < 300 ? \"s2\" : n < 400 ? \"s3\" : n < 500 ? \"s4\" : \"s5\";\n      }\n\n      function resolveRef(ref, spec) {\n        if (!ref || !ref.startsWith(\"#/\")) return null;\n        return (\n          ref\n            .slice(2)\n            .split(\"/\")\n            .reduce((o, k) => o?.[k], spec) ?? null\n        );\n      }\n\n      function schemaEx(schema, spec, d = 0, fieldName = \"\") {\n        if (!schema || d > 4) return null;\n        if (schema.$ref) schema = resolveRef(schema.$ref, spec) || schema;\n        if (schema.example !== undefined) return schema.example;\n        if (schema.properties) {\n          const o = {};\n          for (const [k, v] of Object.entries(schema.properties)) {\n            const r = v.$ref ? resolveRef(v.$ref, spec) || v : v;\n            const t = Array.isArray(r.type) ? r.type[0] : r.type;\n            o[k] =\n              r.example ??\n              schemaEx(r, spec, d + 1, k) ??\n              (t === \"string\"\n                ? r.format === \"email\"\n                  ? \"user@example.com\"\n                  : r.format === \"date-time\"\n                    ? new Date().toISOString()\n                    : r.format === \"uuid\"\n                      ? \"550e8400-e29b-41d4-a716-446655440000\"\n                      : (r.enum?.[0] ?? \"\")\n                : t === \"integer\" || t === \"number\"\n                  ? (r.enum?.[0] ?? 0)\n                  : t === \"boolean\"\n                    ? false\n                    : null);\n          }\n          return o;\n        }\n        if (schema.enum) return schema.enum[0];\n        if (schema.type === \"string\") {\n          if (schema.format === \"email\") return \"user@example.com\";\n          if (schema.format === \"date-time\") return new Date().toISOString();\n          if (schema.format === \"uuid\")\n            return \"550e8400-e29b-41d4-a716-446655440000\";\n          if (schema.format === \"date\")\n            return new Date().toISOString().split(\"T\")[0];\n          const name = fieldName || schema.name || \"\";\n          const lower = name.toLowerCase();\n          if (lower.includes(\"email\")) return \"user@example.com\";\n          if (lower.includes(\"phone\")) return \"+1234567890\";\n          if (lower.includes(\"name\")) return \"Buddy\";\n          if (lower.includes(\"password\")) return \"SecurePass123!\";\n          if (lower.includes(\"token\")) return \"tok_\" + \"x\".repeat(24);\n          if (lower.includes(\"id\") || lower.includes(\"_id\"))\n            return \"usr_\" + Math.random().toString(36).slice(2, 10);\n          if (lower.includes(\"code\")) return \"CODE123\";\n          if (lower.includes(\"url\") || lower.includes(\"uri\"))\n            return \"https://example.com\";\n          if (lower.includes(\"currency\")) return \"USD\";\n          if (lower.includes(\"status\")) return \"active\";\n          if (lower.includes(\"title\")) return \"Sample Title\";\n          if (lower.includes(\"description\") || lower.includes(\"narration\"))\n            return \"Sample description\";\n          if (lower.includes(\"reference\"))\n            return \"ref_\" + Math.random().toString(36).slice(2, 10);\n          if (lower.includes(\"account\")) return \"1234567890\";\n          if (lower.includes(\"bank\")) return \"bank_code_123\";\n          if (lower.includes(\"amount\")) return 100;\n          if (lower.includes(\"platform\")) return \"ios\";\n          if (lower.includes(\"device\")) return \"iPhone 15 Pro\";\n          return \"\";\n        }\n        if (schema.type === \"integer\" || schema.type === \"number\") {\n          const name = (fieldName || schema.name || \"\").toLowerCase();\n          if (\n            name.includes(\"amount\") ||\n            name.includes(\"price\") ||\n            name.includes(\"total\")\n          )\n            return 100;\n          if (name.includes(\"age\")) return 30;\n          if (name.includes(\"count\") || name.includes(\"limit\")) return 10;\n          if (name.includes(\"page\")) return 1;\n          if (name.includes(\"port\")) return 8080;\n          return schema.enum?.[0] ?? 0;\n        }\n        if (schema.type === \"boolean\") return false;\n        if (schema.type === \"array\")\n          return [schemaEx(schema.items, spec, d + 1, fieldName)].filter(\n            (x) => x != null,\n          );\n        return null;\n      }\n\n      function getBodyEx(op, spec) {\n        const content = op.requestBody?.content;\n        if (!content) return null;\n        const ct =\n          content[\"application/json\"] || content[Object.keys(content)[0]];\n        if (!ct) return null;\n        if (ct.example) return ct.example;\n        if (ct.schema) {\n          const s = ct.schema.$ref\n            ? resolveRef(ct.schema.$ref, spec)\n            : ct.schema;\n          return schemaEx(s, spec);\n        }\n        return null;\n      }\n\n      function buildEps(spec) {\n        const out = [];\n        if (!spec?.paths) return out;\n        for (const [rawPath, methods] of Object.entries(spec.paths)) {\n          for (const [key, val] of Object.entries(methods)) {\n            if (METHOD_ORDER.includes(key)) {\n              out.push({\n                path: rawPath,\n                method: key,\n                op: val,\n                id: `${key}::${rawPath}`,\n              });\n            } else if (\n              typeof val === \"object\" &&\n              val !== null &&\n              !Array.isArray(val)\n            ) {\n              const np = rawPath + key;\n              for (const [m, op] of Object.entries(val)) {\n                if (\n                  METHOD_ORDER.includes(m) &&\n                  !out.find((e) => e.id === `${m}::${np}`)\n                )\n                  out.push({\n                    path: np,\n                    method: m,\n                    op,\n                    id: `${m}::${np}`,\n                  });\n              }\n            }\n          }\n        }\n        return out;\n      }\n\n      function groupByTag(eps, tags) {\n        const g = {};\n        for (const ep of eps) {\n          const tgs = ep.op.tags?.length ? ep.op.tags : [\"other\"];\n          for (const t of tgs) {\n            if (t) (g[t] = g[t] || []).push(ep);\n          }\n        }\n\n        const o = {};\n        const tagList = tags || [];\n        // Handle both [{name: \'tag\'}] and [\'tag\'] formats\n        for (const t of tagList) {\n          const name = typeof t === \"string\" ? t : t?.name;\n          if (name && g[name]) {\n            o[name] = g[name];\n          }\n        }\n\n        // Add tags that were not in the explicit tags list\n        for (const t of Object.keys(g)) {\n          if (!o[t]) {\n            o[t] = g[t];\n          }\n        }\n        return o;\n      }\n\n      function genCode(lang, ep, spec) {\n        const base = spec.servers?.[0]?.url || \"\";\n        const { path, method, op } = ep;\n        const url = base + path;\n        const body = getBodyEx(op, spec);\n        const bodyStr = body ? JSON.stringify(body, null, 2) : null;\n        const params = (op.parameters || []).filter((p) => p.in === \"query\");\n        const qStr = params.length\n          ? \"?\" +\n            params\n              .map((p) => `${p.name}=${p.example || \"<\" + p.name + \">\"}`)\n              .join(\"&\")\n          : \"\";\n        const isMP = !!op.requestBody?.content?.[\"multipart/form-data\"];\n        const hasBody = !!bodyStr && !isMP;\n        const M = method.toUpperCase();\n\n        if (lang === \"curl\") {\n          const lines = [\n            `curl -X ${M} \'${url}${qStr}\'`,\n            `  -H \'X-API-Key: YOUR_API_KEY\'`,\n          ];\n          if (hasBody) {\n            lines.push(`  -H \'Content-Type: application/json\'`);\n            lines.push(`  -d \'${JSON.stringify(body)}\'`);\n          }\n          if (isMP) lines.push(`  -F \'file=@/path/to/file\'`);\n          return lines.join(\" \\\\\\n\");\n        }\n        if (lang === \"javascript\") {\n          const hdrs = `\'X-API-Key\': \'YOUR_API_KEY\'${hasBody ? \",\\n    \'Content-Type\': \'application/json\'\" : \"\"}`;\n          const opts = [`  method: \'${M}\'`, `  headers: {\\n    ${hdrs}\\n  }`];\n          if (hasBody)\n            opts.push(\n              `  body: JSON.stringify(${bodyStr.split(\"\\n\").join(\"\\n  \")})`,\n            );\n          return `const res = await fetch(\'${url}${qStr}\', {\\n${opts.join(\",\\n\")}\\n});\\nconst data = await res.json();\\nconsole.log(data);`;\n        }\n        if (lang === \"python\") {\n          const pyBody = bodyStr\n            ? bodyStr\n                .replace(/null/g, \"None\")\n                .replace(/true/g, \"True\")\n                .replace(/false/g, \"False\")\n            : null;\n          return [\n            `import requests`,\n            ``,\n            `url = \"${url}${qStr}\"`,\n            `headers = {\\n    \"X-API-Key\": \"YOUR_API_KEY\",${hasBody ? \'\\n    \"Content-Type\": \"application/json\",\' : \"\"}`,\n            `}`,\n            ...(pyBody ? [``, `payload = ${pyBody}`, ``] : [``]),\n            `response = requests.${method}(url, headers=headers${hasBody ? \", json=payload\" : isMP ? \', files={\"file\": open(\"/path/to/file\",\"rb\")}\' : \"\"})`,\n            `print(response.json())`,\n          ].join(\"\\n\");\n        }\n        if (lang === \"php\") {\n          const hdrs = `\"X-API-Key: \" . $apiKey${hasBody ? \',\\n    \"Content-Type: application/json\"\' : \"\"}`;\n          return [\n            `<?php`,\n            ``,\n            `$url    = \"${url}${qStr}\";`,\n            `$apiKey = \"YOUR_API_KEY\";`,\n            ``,\n            `$ch = curl_init($url);`,\n            `curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);`,\n            `curl_setopt($ch, CURLOPT_CUSTOMREQUEST, \"${M}\");`,\n            `curl_setopt($ch, CURLOPT_HTTPHEADER, [\\n    ${hdrs}\\n]);`,\n            ...(hasBody\n              ? [\n                  ``,\n                  `$payload = json_encode(${bodyStr});`,\n                  `curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);`,\n                ]\n              : []),\n            ``,\n            `$response = curl_exec($ch);`,\n            `curl_close($ch);`,\n            ``,\n            `$data = json_decode($response, true);`,\n            `print_r($data);`,\n          ].join(\"\\n\");\n        }\n        if (lang === \"go\") {\n          const imp = hasBody\n            ? `\"bytes\"\\n\\t\"encoding/json\"\\n\\t\"fmt\"\\n\\t\"io\"\\n\\t\"net/http\"`\n            : `\"fmt\"\\n\\t\"io\"\\n\\t\"net/http\"`;\n          const bLines = hasBody\n            ? [\n                `\\tpayload := map[string]interface{}{`,\n                ...Object.entries(body || {}).map(\n                  ([k, v]) => `\\t\\t\"${k}\": ${JSON.stringify(v)},`,\n                ),\n                `\\t}`,\n                `\\tjsonData, _ := json.Marshal(payload)`,\n                `\\treq, _ := http.NewRequest(\"${M}\", url, bytes.NewBuffer(jsonData))`,\n              ]\n            : [`\\treq, _ := http.NewRequest(\"${M}\", url, nil)`];\n          return [\n            `package main`,\n            ``,\n            `import (\\n\\t${imp}\\n)`,\n            ``,\n            `func main() {`,\n            `\\turl := \"${url}${qStr}\"`,\n            ``,\n            ...bLines,\n            `\\treq.Header.Set(\"X-API-Key\", \"YOUR_API_KEY\")`,\n            ...(hasBody\n              ? [`\\treq.Header.Set(\"Content-Type\", \"application/json\")`]\n              : []),\n            ``,\n            `\\tclient := &http.Client{}`,\n            `\\tresp, err := client.Do(req)`,\n            `\\tif err != nil { panic(err) }`,\n            `\\tdefer resp.Body.Close()`,\n            ``,\n            `\\tbody, _ := io.ReadAll(resp.Body)`,\n            `\\tfmt.Println(string(body))`,\n            `}`,\n          ].join(\"\\n\");\n        }\n        if (lang === \"rust\") {\n          const bLines = hasBody\n            ? [\n                `    let payload = serde_json::json!(${bodyStr});`,\n                ``,\n                `    let res = client.${method}(&url)`,\n                `        .header(\"X-API-Key\", \"YOUR_API_KEY\")`,\n                `        .json(&payload)`,\n                `        .send().await?;`,\n              ]\n            : [\n                `    let res = client.${method}(&url)`,\n                `        .header(\"X-API-Key\", \"YOUR_API_KEY\")`,\n                `        .send().await?;`,\n              ];\n          return [\n            `use reqwest;`,\n            ``,\n            `#[tokio::main]`,\n            `async fn main() -> Result<(), Box<dyn std::error::Error>> {`,\n            `    let client = reqwest::Client::new();`,\n            `    let url = \"${url}${qStr}\";`,\n            ``,\n            ...bLines,\n            ``,\n            `    let data: serde_json::Value = res.json().await?;`,\n            `    println!(\"{:#?}\", data);`,\n            ``,\n            `    Ok(())`,\n            `}`,\n          ].join(\"\\n\");\n        }\n        return \"\";\n      }\n\n      function CopyBtn({ text }) {\n        const [ok, setOk] = useState(false);\n        const copy = () => {\n          navigator.clipboard.writeText(text).catch(() => {});\n          setOk(true);\n          setTimeout(() => setOk(false), 1800);\n        };\n        return h(\n          \"button\",\n          { onClick: copy, className: `copy-btn${ok ? \" ok\" : \"\"}` },\n          h(\"i\", { className: `fa ${ok ? \"fa-check\" : \"fa-copy\"}` }),\n          ok ? \"Copied!\" : \"Copy\",\n        );\n      }\n\n      function CodeEditor({ value, onChange }) {\n        const editorRef = useRef(null);\n        const codeMirrorRef = useRef(null);\n        const [error, setError] = useState(null);\n\n        const validateJson = (text) => {\n          if (!text || text.trim() === \"\") {\n            setError(null);\n            return null;\n          }\n          try {\n            JSON.parse(text);\n            setError(null);\n            return null;\n          } catch (e) {\n            const match = e.message.match(/position (\\d+)/);\n            const pos = match ? parseInt(match[1]) : null;\n            const errorInfo = {\n              message: e.message,\n              position: pos,\n            };\n            setError(errorInfo);\n            return errorInfo;\n          }\n        };\n\n        const markError = (err) => {\n          if (!codeMirrorRef.current) return;\n          const cm = codeMirrorRef.current;\n          cm.clearGutter(\"error-gutter\");\n          cm.getAllMarks().forEach((m) => m.clear());\n\n          if (err && err.position !== null) {\n            const pos = cm.posFromIndex(err.position);\n            if (pos) {\n              cm.markText(\n                { line: pos.line, ch: pos.ch },\n                { line: pos.line, ch: pos.ch + 1 },\n                {\n                  className: \"cm-json-error\",\n                  attributes: { title: err.message },\n                },\n              );\n              cm.setGutterMarker(\n                pos.line,\n                \"error-gutter\",\n                (() => {\n                  const marker = document.createElement(\"div\");\n                  marker.style.color = \"#e05c4b\";\n                  marker.innerHTML = \"\u{25cf}\";\n                  marker.title = err.message;\n                  return marker;\n                })(),\n              );\n            }\n          }\n        };\n\n        useEffect(() => {\n          if (!editorRef.current) return;\n\n          codeMirrorRef.current = window.CodeMirror.fromTextArea(\n            editorRef.current,\n            {\n              mode: { name: \"javascript\", json: true },\n              theme: \"atom-one-dark\",\n              lineNumbers: false,\n              gutters: [\"error-gutter\"],\n              autoCloseBrackets: true,\n              matchBrackets: true,\n              indentUnit: 2,\n              tabSize: 2,\n              lineWrapping: true,\n              viewportMargin: Infinity,\n              extraKeys: {\n                Tab: (cm) => {\n                  if (cm.somethingSelected()) {\n                    cm.indentSelection(\"add\");\n                  } else {\n                    cm.replaceSelection(\"  \", \"end\");\n                  }\n                },\n              },\n            },\n          );\n\n          codeMirrorRef.current.on(\"change\", (instance) => {\n            const newValue = instance.getValue();\n            onChange(newValue);\n            const err = validateJson(newValue);\n            markError(err);\n          });\n\n          return () => {\n            if (codeMirrorRef.current) {\n              codeMirrorRef.current.toTextArea();\n              codeMirrorRef.current = null;\n            }\n          };\n        }, []);\n\n        useEffect(() => {\n          if (\n            codeMirrorRef.current &&\n            value !== codeMirrorRef.current.getValue()\n          ) {\n            codeMirrorRef.current.setValue(value);\n          }\n        }, [value]);\n\n        return h(\n          \"div\",\n          null,\n          h(\n            \"div\",\n            { className: \"code-editor-wrapper\" },\n            h(\"textarea\", { ref: editorRef, defaultValue: value }),\n          ),\n          error &&\n            h(\n              \"div\",\n              { className: \"json-error-hint\" },\n              h(\"i\", {\n                className: \"fa fa-circle-exclamation\",\n              }),\n              `Invalid JSON: ${error.message}`,\n            ),\n        );\n      }\n\n      function HljsCode({ code, lang }) {\n        const ref = useRef(null);\n        useEffect(() => {\n          if (!ref.current || !window.hljs) return;\n          ref.current.removeAttribute(\"data-highlighted\");\n          ref.current.textContent = code;\n          window.hljs.highlightElement(ref.current);\n        }, [code, lang]);\n        return h(\"code\", { ref, className: `language-${lang}` });\n      }\n\n      function CodeBlock({ code, lang, label }) {\n        return h(\n          \"div\",\n          { className: \"code-wrap\" },\n          h(\n            \"div\",\n            { className: \"code-hd\" },\n            h(\"span\", { className: \"code-hd-lbl\" }, label),\n            h(CopyBtn, { text: code }),\n          ),\n          h(\"pre\", null, h(HljsCode, { code, lang })),\n        );\n      }\n\n      function MethodBadge({ method }) {\n        return h(\n          \"span\",\n          { className: `method-badge m-${method} mb-${method}` },\n          method.toUpperCase(),\n        );\n      }\n\n      function ParamsTable({ params }) {\n        if (!params?.length) return null;\n        return h(\n          \"div\",\n          { style: { marginBottom: 20 } },\n          h(\"div\", { className: \"sec-lbl\" }, \"Parameters\"),\n          h(\n            \"div\",\n            { className: \"params-tbl\" },\n            params.map((p, i) =>\n              h(\n                \"div\",\n                { key: i, className: \"p-row\" },\n                h(\n                  \"div\",\n                  { className: \"p-name\" },\n                  p.name,\n                  p.required && h(\"span\", { className: \"p-req\" }, \" *\"),\n                ),\n                h(\n                  \"div\",\n                  { className: \"p-meta\" },\n                  h(\"span\", { className: \"p-in\" }, p.in),\n                  p.schema?.type &&\n                    h(\n                      \"span\",\n                      { className: \"p-type\" },\n                      Array.isArray(p.schema.type)\n                        ? p.schema.type[0]\n                        : p.schema.type,\n                    ),\n                ),\n                h(\n                  \"div\",\n                  { className: \"p-desc\" },\n                  p.description || \"\",\n                  p.example !== undefined &&\n                    h(\n                      \"div\",\n                      {\n                        style: {\n                          fontSize: 11,\n                          color: \"var(--t3)\",\n                          marginTop: 2,\n                        },\n                      },\n                      `e.g. \"${p.example}\"`,\n                    ),\n                  p.schema?.enum &&\n                    h(\n                      \"div\",\n                      { style: { marginTop: 5 } },\n                      p.schema.enum.map((v) =>\n                        h(\n                          \"span\",\n                          {\n                            key: v,\n                            className: \"enum-chip\",\n                          },\n                          v,\n                        ),\n                      ),\n                    ),\n                  p.schema?.default !== undefined &&\n                    h(\n                      \"div\",\n                      {\n                        style: {\n                          fontSize: 11,\n                          color: \"var(--t3)\",\n                          marginTop: 2,\n                        },\n                      },\n                      `default: ${p.schema.default}`,\n                    ),\n                ),\n              ),\n            ),\n          ),\n        );\n      }\n\n      function RequestBodyPanel({ op, spec }) {\n        if (!op.requestBody) return null;\n\n        // Handle requestBody $ref at the top level\n        let requestBody = op.requestBody;\n        if (op.requestBody.$ref) {\n          requestBody = resolveRef(op.requestBody.$ref, spec) || op.requestBody;\n        }\n\n        const content = requestBody.content || {};\n        const isMP = !!content[\"multipart/form-data\"];\n        const jsonCt = content[\"application/json\"];\n\n        // Get schema - could be directly on jsonCt or nested\n        let schema = null;\n        if (jsonCt) {\n          if (jsonCt.schema?.$ref) {\n            schema = resolveRef(jsonCt.schema.$ref, spec);\n          } else if (jsonCt.schema) {\n            schema = jsonCt.schema;\n          }\n        }\n\n        const example = getBodyEx(op, spec);\n\n        // Handle allOf composition by merging properties\n        if (schema?.allOf && Array.isArray(schema.allOf)) {\n          const merged = { properties: {}, required: [] };\n          schema.allOf.forEach((s) => {\n            const resolved = s.$ref ? resolveRef(s.$ref, spec) : s;\n            if (resolved?.properties) {\n              Object.assign(merged.properties, resolved.properties);\n            }\n            if (resolved?.required) {\n              merged.required.push(...resolved.required);\n            }\n          });\n          schema = { ...schema, ...merged };\n        }\n\n        // Handle oneOf/anyOf by using the first schema as a hint\n        if (!schema?.properties && (schema?.oneOf || schema?.anyOf)) {\n          const first = schema.oneOf?.[0] || schema.anyOf?.[0];\n          if (first) {\n            schema = first.$ref ? resolveRef(first.$ref, spec) : first;\n          }\n        }\n\n        // If schema is still just a $ref, resolve it\n        if (schema && !schema.properties && schema.$ref) {\n          schema = resolveRef(schema.$ref, spec) || schema;\n        }\n\n        return h(\n          \"div\",\n          { style: { marginBottom: 20 } },\n          h(\n            \"div\",\n            { className: \"rb-hd\" },\n            h(\n              \"div\",\n              {\n                className: \"sec-lbl\",\n                style: { marginBottom: 0 },\n              },\n              \"Request Body\",\n            ),\n            requestBody.required &&\n              h(\"span\", { className: \"rb-req\" }, \"required\"),\n          ),\n          isMP &&\n            h(\n              \"div\",\n              { className: \"mp-note\" },\n              h(\"i\", {\n                className: \"fa fa-paperclip\",\n                style: {\n                  color: \"var(--orange)\",\n                  marginRight: 6,\n                },\n              }),\n              \"multipart/form-data \u{2014} send files as form data\",\n            ),\n          schema?.properties &&\n            h(\n              \"div\",\n              {\n                className: \"params-tbl\",\n                style: { marginBottom: 12 },\n              },\n              Object.entries(schema.properties).map(([name, prop]) => {\n                const r = prop.$ref\n                  ? resolveRef(prop.$ref, spec) || prop\n                  : prop;\n                const t = Array.isArray(r.type) ? r.type[0] : r.type;\n                return h(\n                  \"div\",\n                  { key: name, className: \"p-row\" },\n                  h(\n                    \"div\",\n                    { className: \"p-name\" },\n                    name,\n                    (schema.required || []).includes(name) &&\n                      h(\"span\", { className: \"p-req\" }, \" *\"),\n                  ),\n                  h(\n                    \"div\",\n                    { className: \"p-meta\" },\n                    h(\"span\", { className: \"p-type\" }, t || \"\u{2014}\"),\n                  ),\n                  h(\n                    \"div\",\n                    { className: \"p-desc\" },\n                    r.description || \"\",\n                    r.enum &&\n                      h(\n                        \"div\",\n                        { style: { marginTop: 5 } },\n                        r.enum.map((v) =>\n                          h(\n                            \"span\",\n                            {\n                              key: v,\n                              className: \"enum-chip\",\n                            },\n                            v,\n                          ),\n                        ),\n                      ),\n                  ),\n                );\n              }),\n            ),\n          example &&\n            h(CodeBlock, {\n              code: JSON.stringify(example, null, 2),\n              lang: \"json\",\n              label: \"Example Request\",\n            }),\n        );\n      }\n\n      function ResponsesPanel({ responses }) {\n        const codes = Object.keys(responses || {});\n        const [active, setActive] = useState(codes[0]);\n        if (!codes.length) return null;\n        const resp = responses[active];\n        const example = resp?.content?.[\"application/json\"]?.example;\n        return h(\n          \"div\",\n          { style: { marginBottom: 20 } },\n          h(\"div\", { className: \"sec-lbl\" }, \"Responses\"),\n          h(\n            \"div\",\n            { className: \"resp-tabs\" },\n            codes.map((c) =>\n              h(\n                \"button\",\n                {\n                  key: c,\n                  onClick: () => setActive(c),\n                  className: `resp-tab ${active === c ? \"on\" : \"\"} ${sCls(c)}`,\n                },\n                c,\n              ),\n            ),\n          ),\n          h(\n            \"div\",\n            { className: \"resp-box\" },\n            h(\n              \"div\",\n              {\n                style: {\n                  display: \"flex\",\n                  alignItems: \"center\",\n                  gap: 10,\n                  marginBottom: example ? 12 : 0,\n                },\n              },\n              h(\n                \"span\",\n                {\n                  className: sCls(active),\n                  style: {\n                    fontFamily: \"monospace\",\n                    fontWeight: 700,\n                    fontSize: 15,\n                  },\n                },\n                active,\n              ),\n              h(\"span\", { style: { color: \"var(--t2)\" } }, resp?.description),\n            ),\n            example &&\n              h(CodeBlock, {\n                code: JSON.stringify(example, null, 2),\n                lang: \"json\",\n                label: \"Example Response\",\n              }),\n          ),\n        );\n      }\n\n      function ResponseDisplay({ response, loading }) {\n        const [respTab, setRespTab] = useState(\"body\");\n        if (loading) {\n          return h(\n            \"div\",\n            { className: \"response-area\" },\n            h(\n              \"div\",\n              { className: \"response-hd\" },\n              h(\n                \"div\",\n                { className: \"response-ttl\" },\n                h(\"i\", { className: \"fa fa-server\" }),\n                \"Response\",\n              ),\n            ),\n            h(\n              \"div\",\n              {\n                style: {\n                  padding: \"40px 0\",\n                  textAlign: \"center\",\n                  color: \"var(--t3)\",\n                },\n              },\n              h(\"i\", {\n                className: \"fa fa-spinner fa-spin\",\n                style: { marginRight: 8 },\n              }),\n              \"Awaiting response...\",\n            ),\n          );\n        }\n        if (!response) {\n          return h(\n            \"div\",\n            { className: \"response-area\" },\n            h(\n              \"div\",\n              { className: \"response-hd\" },\n              h(\n                \"div\",\n                { className: \"response-ttl\" },\n                h(\"i\", { className: \"fa fa-server\" }),\n                \"Response\",\n              ),\n            ),\n            h(\n              \"div\",\n              {\n                style: {\n                  padding: \"40px 0\",\n                  textAlign: \"center\",\n                  color: \"var(--t3)\",\n                  background: \"var(--bg-card)\",\n                  borderRadius: \"8px\",\n                  border: \"1px dashed var(--bd)\",\n                  fontSize: \"13px\",\n                },\n              },\n              h(\n                \"div\",\n                { className: \"empty-response\" },\n                h(\"i\", { className: \"fa fa-wifi\", style: { marginRight: 8 } }),\n                \"Click \'Send Request\' to test the API\",\n              ),\n            ),\n          );\n        }\n\n        const statusClass =\n          response.status >= 200 && response.status < 300\n            ? \"success\"\n            : response.status >= 400\n              ? \"error\"\n              : \"info\";\n\n        const responseJson = JSON.stringify(response.data, null, 2);\n\n        return h(\n          \"div\",\n          { className: \"response-area\" },\n          h(\n            \"div\",\n            { className: \"response-hd\" },\n            h(\n              \"div\",\n              { className: \"response-ttl\" },\n              h(\"i\", { className: \"fa fa-server\" }),\n              \"Response\",\n            ),\n            h(\n              \"span\",\n              {\n                className: `response-status ${statusClass}`,\n              },\n              `${response.status} ${response.statusText || \"\"}`,\n            ),\n          ),\n          h(\n            \"div\",\n            {\n              className: \"resp-tabs\",\n              style: {\n                margin: \"12px 0\",\n                padding: \"0 4px\",\n                borderBottom: \"1px solid var(--bd-sub)\",\n              },\n            },\n            h(\n              \"button\",\n              {\n                className: `resp-tab ${respTab === \"body\" ? \"on\" : \"\"}`,\n                onClick: () => setRespTab(\"body\"),\n                style: {\n                  fontSize: \"11px\",\n                  padding: \"6px 12px\",\n                },\n              },\n              \"Body\",\n            ),\n            h(\n              \"button\",\n              {\n                className: `resp-tab ${respTab === \"header\" ? \"on\" : \"\"}`,\n                onClick: () => setRespTab(\"header\"),\n                style: {\n                  fontSize: \"11px\",\n                  padding: \"6px 12px\",\n                },\n              },\n              \"Headers\",\n            ),\n          ),\n          respTab === \"body\"\n            ? h(\n                \"div\",\n                { className: \"code-wrap\", style: { marginBottom: 0 } },\n                h(\n                  \"div\",\n                  { className: \"code-hd\" },\n                  h(\"span\", { className: \"code-hd-lbl\" }, \"JSON Response\"),\n                  h(CopyBtn, { text: responseJson }),\n                ),\n                h(\n                  \"pre\",\n                  { className: \"response-pre\" },\n                  h(HljsCode, { code: responseJson, lang: \"json\" }),\n                ),\n              )\n            : h(\n                \"div\",\n                { className: \"code-wrap\", style: { marginBottom: 0 } },\n                h(\n                  \"div\",\n                  { className: \"code-hd\" },\n                  h(\"span\", { className: \"code-hd-lbl\" }, \"Response Headers\"),\n                  h(CopyBtn, {\n                    text: JSON.stringify(response.headers, null, 2),\n                  }),\n                ),\n                h(\n                  \"pre\",\n                  { className: \"response-pre\" },\n                  h(HljsCode, {\n                    code: JSON.stringify(response.headers, null, 2),\n                    lang: \"json\",\n                  }),\n                ),\n              ),\n        );\n      }\n\n      function CodeSamples({\n        ep,\n        spec,\n        apiKey,\n        bearerToken,\n        authType,\n        baseUrl,\n        setApiKey,\n        setBearerToken,\n        setAuthType,\n        showApiKey,\n        setShowApiKey,\n        showBearer,\n        setShowBearer,\n      }) {\n        const [lang, setLang] = useState(\"curl\");\n        const [activeTab, setActiveTab] = useState(\"samples\");\n        const code = useMemo(() => genCode(lang, ep, spec), [lang, ep, spec]);\n        const info = LANGS.find((l) => l.id === lang);\n        const [params, setParams] = useState({});\n        const [bodyText, setBodyText] = useState(\"\");\n        const [selectedFiles, setSelectedFiles] = useState({});\n        const [response, setResponse] = useState(null);\n        const [loading, setLoading] = useState(false);\n\n        const queryParams = (ep.op.parameters || []).filter(\n          (p) => p.in === \"query\",\n        );\n        const pathParams = (ep.op.parameters || []).filter(\n          (p) => p.in === \"path\",\n        );\n        const bodyExample = getBodyEx(ep.op, spec);\n\n        const securityRequirements = ep.op.security || spec.security || [];\n        const securitySchemes = spec.components?.securitySchemes || {};\n\n        const requiredSchemes = useMemo(() => {\n          const schemes = [];\n          securityRequirements.forEach((req) => {\n            Object.keys(req).forEach((name) => {\n              const scheme = securitySchemes[name];\n              if (scheme) {\n                schemes.push({ name, ...scheme });\n              } else if (name === \"bearer_auth\") {\n                // Fallback for common naming if not in components\n                schemes.push({\n                  name,\n                  type: \"http\",\n                  scheme: \"bearer\",\n                });\n              } else if (name === \"api_key\") {\n                schemes.push({\n                  name,\n                  type: \"apiKey\",\n                  in: \"header\",\n                  name: \"X-API-Key\",\n                });\n              }\n            });\n          });\n          return schemes;\n        }, [securityRequirements, securitySchemes]);\n\n        const hasApiKey = requiredSchemes.some(\n          (s) =>\n            s.type === \"apiKey\" || (s.type === \"http\" && s.scheme === \"basic\"),\n        );\n        const hasBearer = requiredSchemes.some(\n          (s) =>\n            (s.type === \"http\" && s.scheme === \"bearer\") ||\n            s.type === \"oauth2\" ||\n            s.type === \"openIdConnect\",\n        );\n        const isSecurityRequired = requiredSchemes.length > 0;\n\n        useEffect(() => {\n          if (hasApiKey && !hasBearer) setAuthType(\"api_key\");\n          else if (hasBearer && !hasApiKey) setAuthType(\"bearer\");\n        }, [hasApiKey, hasBearer]);\n\n        const multipartContent =\n          ep.op.requestBody?.content?.[\"multipart/form-data\"];\n        const isMultipart = !!multipartContent;\n        const multipartSchema = multipartContent?.schema;\n        const fileFields =\n          isMultipart && multipartSchema?.properties\n            ? Object.entries(multipartSchema.properties).filter(\n                ([, prop]) =>\n                  prop.type === \"string\" && prop.format === \"binary\",\n              )\n            : [];\n\n        useEffect(() => {\n          if (bodyExample) {\n            setBodyText(JSON.stringify(bodyExample, null, 2));\n          }\n        }, [ep.id]);\n\n        useEffect(() => {\n          setParams({});\n          setResponse(null);\n          setSelectedFiles({});\n        }, [ep.id]);\n\n        const buildUrl = () => {\n          let url = baseUrl + ep.path;\n          pathParams.forEach((p) => {\n            const val = params[p.name] || `{${p.name}}`;\n            url = url.replace(`{${p.name}}`, val);\n          });\n          const qParams = Object.entries(params).filter(\n            ([key]) => !pathParams.find((p) => p.name === key),\n          );\n          if (qParams.length) {\n            url +=\n              \"?\" +\n              qParams\n                .map(([k, v]) => `${k}=${encodeURIComponent(v)}`)\n                .join(\"&\");\n          }\n          return url;\n        };\n\n        const sendRequest = async () => {\n          setLoading(true);\n          setResponse(null);\n          const url = buildUrl();\n\n          if (isMultipart && fileFields.length > 0) {\n            const formData = new FormData();\n\n            fileFields.forEach(([fieldName]) => {\n              const file = selectedFiles[fieldName];\n              if (file) {\n                formData.append(fieldName, file);\n              }\n            });\n\n            const headers = {};\n            if (isSecurityRequired) {\n              if (authType === \"api_key\" && apiKey) {\n                const scheme = requiredSchemes.find(\n                  (s) =>\n                    s.type === \"apiKey\" ||\n                    (s.type === \"http\" && s.scheme === \"basic\"),\n                );\n                const headerName = scheme?.name || \"X-API-Key\";\n                headers[headerName] = apiKey;\n              } else if (authType === \"bearer\" && bearerToken) {\n                headers[\"Authorization\"] = `Bearer ${bearerToken}`;\n              }\n            }\n            // Note: Don\'t set Content-Type for FormData, browser sets it with boundary\n\n            const options = {\n              method: ep.method.toUpperCase(),\n              headers,\n              body: formData,\n            };\n\n            try {\n              const res = await fetch(url, options);\n              const data = await res.json().catch(() => res.text());\n              const headers = {};\n              res.headers.forEach((v, k) => {\n                headers[k] = v;\n              });\n              setResponse({\n                status: res.status,\n                statusText: res.statusText,\n                data: typeof data === \"string\" ? { raw: data } : data,\n                headers,\n              });\n            } catch (e) {\n              setResponse({\n                status: 0,\n                statusText: \"Network Error\",\n                data: {\n                  error: \"Failed to send request\",\n                  details: e.message,\n                },\n              });\n            } finally {\n              setLoading(false);\n            }\n            return;\n          }\n\n          const headers = {\n            \"Content-Type\": \"application/json\",\n          };\n          if (isSecurityRequired) {\n            if (authType === \"api_key\" && apiKey) {\n              const scheme = requiredSchemes.find(\n                (s) =>\n                  s.type === \"apiKey\" ||\n                  (s.type === \"http\" && s.scheme === \"basic\"),\n              );\n              const headerName = scheme?.name || \"X-API-Key\";\n              headers[headerName] = apiKey;\n            } else if (authType === \"bearer\" && bearerToken) {\n              headers[\"Authorization\"] = `Bearer ${bearerToken}`;\n            }\n          }\n          const options = {\n            method: ep.method.toUpperCase(),\n            headers,\n          };\n          if (\n            ep.method.toLowerCase() !== \"get\" &&\n            ep.method.toLowerCase() !== \"head\" &&\n            bodyText\n          ) {\n            try {\n              options.body = JSON.parse(bodyText);\n            } catch (e) {\n              setLoading(false);\n              setResponse({\n                status: 400,\n                statusText: \"Invalid JSON\",\n                data: {\n                  error: \"Request body is not valid JSON\",\n                  details: e.message,\n                },\n              });\n              return;\n            }\n          }\n          try {\n            const res = await fetch(url, options);\n            const data = await res.json().catch(() => res.text());\n            const headers = {};\n            res.headers.forEach((v, k) => {\n              headers[k] = v;\n            });\n            setResponse({\n              status: res.status,\n              statusText: res.statusText,\n              data: typeof data === \"string\" ? { raw: data } : data,\n              headers,\n            });\n          } catch (e) {\n            setResponse({\n              status: 0,\n              statusText: \"Network Error\",\n              data: {\n                error: \"Failed to send request\",\n                details: e.message,\n              },\n            });\n          } finally {\n            setLoading(false);\n          }\n        };\n\n        const updateParam = (name, value) => {\n          setParams((prev) => ({ ...prev, [name]: value }));\n        };\n\n        const statusClass = response\n          ? response.status >= 200 && response.status < 300\n            ? \"success\"\n            : response.status >= 400\n              ? \"error\"\n              : \"info\"\n          : \"\";\n        return h(\n          \"div\",\n          { style: { marginBottom: 20 } },\n          h(\"div\", { className: \"sec-lbl\" }, \"Code Samples\"),\n          h(\n            \"div\",\n            { className: \"test-tabs\", style: { marginBottom: 12 } },\n            h(\n              \"button\",\n              {\n                className: `test-tab ${activeTab === \"samples\" ? \"on\" : \"\"}`,\n                onClick: () => setActiveTab(\"samples\"),\n              },\n              h(\"i\", {\n                className: \"fa fa-code test-tab-icon\",\n              }),\n              \"Samples\",\n            ),\n            h(\n              \"button\",\n              {\n                className: `test-tab ${activeTab === \"tryit\" ? \"on\" : \"\"}`,\n                onClick: () => setActiveTab(\"tryit\"),\n              },\n              h(\"i\", {\n                className: \"fa fa-flask test-tab-icon\",\n              }),\n              \"Try It\",\n            ),\n          ),\n          activeTab === \"samples\" &&\n            h(\n              \"div\",\n              null,\n              h(\n                \"div\",\n                { className: \"code-wrap\" },\n                h(\n                  \"div\",\n                  { className: \"lang-tabs\" },\n                  LANGS.map((l) =>\n                    h(\n                      \"button\",\n                      {\n                        key: l.id,\n                        onClick: () => setLang(l.id),\n                        className: `lang-tab ${lang === l.id ? \"on\" : \"\"}`,\n                      },\n                      h(\"i\", {\n                        className: l.fa,\n                        style: { fontSize: 13 },\n                      }),\n                      l.label,\n                    ),\n                  ),\n                ),\n                h(\n                  \"div\",\n                  { className: \"code-hd\" },\n                  h(\n                    \"span\",\n                    { className: \"code-hd-lbl\" },\n                    `${info?.label} example`,\n                  ),\n                  h(CopyBtn, { text: code }),\n                ),\n                h(\n                  \"pre\",\n                  null,\n                  h(HljsCode, {\n                    code,\n                    lang: info?.hl || \"bash\",\n                  }),\n                ),\n              ),\n            ),\n          activeTab === \"tryit\" &&\n            h(\n              \"div\",\n              { className: \"test-panel\" },\n              h(\n                \"div\",\n                { className: \"test-panel-hd\" },\n                h(\n                  \"div\",\n                  { className: \"test-panel-ttl\" },\n                  h(\"i\", { className: \"fa fa-flask\" }),\n                  \"Test This Endpoint\",\n                ),\n                h(\n                  \"button\",\n                  {\n                    className: `btn-test ${loading ? \"loading\" : \"\"}`,\n                    onClick: sendRequest,\n                    disabled: loading,\n                  },\n                  h(\"i\", {\n                    className: loading\n                      ? \"fa fa-spinner fa-spin\"\n                      : \"fa fa-paper-plane\",\n                  }),\n                  loading ? \"Sending...\" : \"Send Request\",\n                ),\n              ),\n              h(\n                \"div\",\n                { className: \"test-url\" },\n                h(\n                  \"span\",\n                  {\n                    className: `test-method m-${ep.method} mb-${ep.method}`,\n                  },\n                  ep.method.toUpperCase(),\n                ),\n                buildUrl(),\n              ),\n              h(\n                \"div\",\n                { style: { marginBottom: 12 } },\n                [...pathParams, ...queryParams].length > 0\n                  ? [...pathParams, ...queryParams].map((p) =>\n                      h(\n                        \"div\",\n                        {\n                          key: p.name,\n                          className: \"param-input-row\",\n                        },\n                        h(\n                          \"div\",\n                          { className: \"param-label\" },\n                          p.name,\n                          p.required &&\n                            h(\n                              \"span\",\n                              {\n                                style: {\n                                  color: \"var(--red)\",\n                                },\n                              },\n                              \" *\",\n                            ),\n                        ),\n                        h(\"input\", {\n                          className: \"param-input\",\n                          type: \"text\",\n                          placeholder: p.example || p.description || \"\",\n                          value: params[p.name] || \"\",\n                          onChange: (e) => updateParam(p.name, e.target.value),\n                        }),\n                        h(\n                          \"div\",\n                          { className: \"param-type\" },\n                          Array.isArray(p.schema?.type)\n                            ? p.schema.type.join(\" | \")\n                            : p.schema?.type || \"string\",\n                        ),\n                      ),\n                    )\n                  : h(\n                      \"div\",\n                      {\n                        style: {\n                          color: \"var(--t3)\",\n                          fontSize: 12,\n                          textAlign: \"center\",\n                          padding: \"20px 0\",\n                        },\n                      },\n                      h(\"i\", {\n                        className: \"fa fa-info-circle\",\n                        style: { marginRight: 6 },\n                      }),\n                      \"No parameters for this endpoint\",\n                    ),\n              ),\n              isSecurityRequired &&\n                h(\n                  \"div\",\n                  { className: \"auth-header-input\" },\n                  h(\n                    \"div\",\n                    {\n                      className: \"auth-header-label\",\n                      style: {\n                        display: \"flex\",\n                        alignItems: \"center\",\n                        justifyContent: \"space-between\",\n                        width: \"100%\",\n                      },\n                    },\n                    h(\n                      \"div\",\n                      {\n                        style: {\n                          display: \"flex\",\n                          alignItems: \"center\",\n                          gap: 6,\n                        },\n                      },\n                      h(\"i\", { className: \"fa fa-key\" }),\n                      \"Authorization\",\n                    ),\n                    hasApiKey &&\n                      hasBearer &&\n                      h(\n                        \"div\",\n                        {\n                          style: {\n                            display: \"flex\",\n                            gap: 4,\n                          },\n                        },\n                        h(\n                          \"button\",\n                          {\n                            className: `theme-btn ${authType === \"api_key\" ? \"on\" : \"\"}`,\n                            style: {\n                              width: \"auto\",\n                              padding: \"4px 10px\",\n                              fontSize: \"11px\",\n                            },\n                            onClick: () => setAuthType(\"api_key\"),\n                            title: \"API Key authentication\",\n                          },\n                          \"API Key\",\n                        ),\n                        h(\n                          \"button\",\n                          {\n                            className: `theme-btn ${authType === \"bearer\" ? \"on\" : \"\"}`,\n                            style: {\n                              width: \"auto\",\n                              padding: \"4px 10px\",\n                              fontSize: \"11px\",\n                            },\n                            onClick: () => setAuthType(\"bearer\"),\n                            title: \"Bearer Token authentication\",\n                          },\n                          \"Bearer\",\n                        ),\n                      ),\n                  ),\n                  authType === \"api_key\" && hasApiKey\n                    ? h(\n                        \"div\",\n                        {\n                          style: { marginTop: \"8px\", position: \"relative\" },\n                        },\n                        h(\"input\", {\n                          type: showApiKey ? \"text\" : \"password\",\n                          placeholder: apiKey ? `API key set` : \"Enter API key\",\n                          value: apiKey,\n                          onChange: (e) => setApiKey(e.target.value),\n                          style: {\n                            width: \"100%\",\n                            paddingRight: \"30px\",\n                            marginBottom: apiKey ? \"4px\" : \"0\",\n                          },\n                        }),\n                        h(\"i\", {\n                          className: `fa ${showApiKey ? \"fa-eye-slash\" : \"fa-eye\"}`,\n                          onClick: () => setShowApiKey(!showApiKey),\n                          style: {\n                            position: \"absolute\",\n                            right: \"10px\",\n                            top: \"10px\",\n                            fontSize: \"12px\",\n                            color: \"var(--t3)\",\n                            cursor: \"pointer\",\n                          },\n                        }),\n                        apiKey &&\n                          h(\n                            \"div\",\n                            {\n                              style: {\n                                fontSize: \"10px\",\n                                color: \"var(--green)\",\n                                display: \"flex\",\n                                alignItems: \"center\",\n                                gap: \"4px\",\n                              },\n                            },\n                            h(\"i\", {\n                              className: \"fa fa-check-circle\",\n                            }),\n                            \"API key stored in memory\",\n                          ),\n                      )\n                    : authType === \"bearer\" && hasBearer\n                      ? h(\n                          \"div\",\n                          {\n                            style: { marginTop: \"8px\", position: \"relative\" },\n                          },\n                          h(\"input\", {\n                            type: showBearer ? \"text\" : \"password\",\n                            placeholder: bearerToken\n                              ? `Token set`\n                              : \"Enter Bearer token\",\n                            value: bearerToken,\n                            onChange: (e) => setBearerToken(e.target.value),\n                            style: {\n                              width: \"100%\",\n                              paddingRight: \"30px\",\n                              marginBottom: bearerToken ? \"4px\" : \"0\",\n                            },\n                          }),\n                          h(\"i\", {\n                            className: `fa ${showBearer ? \"fa-eye-slash\" : \"fa-eye\"}`,\n                            onClick: () => setShowBearer(!showBearer),\n                            style: {\n                              position: \"absolute\",\n                              right: \"10px\",\n                              top: \"10px\",\n                              fontSize: \"12px\",\n                              color: \"var(--t3)\",\n                              cursor: \"pointer\",\n                            },\n                          }),\n                          bearerToken &&\n                            h(\n                              \"div\",\n                              {\n                                style: {\n                                  fontSize: \"10px\",\n                                  color: \"var(--green)\",\n                                  display: \"flex\",\n                                  alignItems: \"center\",\n                                  gap: \"4px\",\n                                },\n                              },\n                              h(\"i\", {\n                                className: \"fa fa-check-circle\",\n                              }),\n                              \"Token stored in memory\",\n                            ),\n                        )\n                      : null,\n                ),\n              // Show file inputs for multipart/form-data\n              isMultipart && fileFields.length > 0\n                ? h(\n                    \"div\",\n                    { style: { marginBottom: 12 } },\n                    h(\n                      \"div\",\n                      {\n                        style: {\n                          fontSize: 11,\n                          fontWeight: 600,\n                          color: \"var(--t3)\",\n                          textTransform: \"uppercase\",\n                          letterSpacing: \"0.08em\",\n                          marginBottom: 8,\n                        },\n                      },\n                      \"File Upload\",\n                    ),\n                    h(\n                      \"div\",\n                      {\n                        style: {\n                          display: \"flex\",\n                          flexDirection: \"column\",\n                          gap: \"10px\",\n                        },\n                      },\n                      fileFields.map(([fieldName, prop]) =>\n                        h(\n                          \"div\",\n                          {\n                            key: fieldName,\n                            style: {\n                              display: \"flex\",\n                              flexDirection: \"column\",\n                              gap: \"4px\",\n                            },\n                          },\n                          h(\n                            \"label\",\n                            {\n                              style: {\n                                fontSize: 12,\n                                color: \"var(--t2)\",\n                                fontWeight: 500,\n                              },\n                            },\n                            fieldName,\n                            prop.description &&\n                              h(\n                                \"span\",\n                                {\n                                  style: {\n                                    color: \"var(--t3)\",\n                                    fontWeight: 400,\n                                    marginLeft: \"4px\",\n                                  },\n                                },\n                                \" - \" + prop.description,\n                              ),\n                          ),\n                          h(\"input\", {\n                            type: \"file\",\n                            className: \"param-input\",\n                            style: {\n                              padding: \"6px 10px\",\n                              cursor: \"pointer\",\n                            },\n                            onChange: (e) => {\n                              const file = e.target.files?.[0];\n                              if (file) {\n                                setSelectedFiles((prev) => ({\n                                  ...prev,\n                                  [fieldName]: file,\n                                }));\n                              }\n                            },\n                          }),\n                          selectedFiles[fieldName] &&\n                            h(\n                              \"div\",\n                              {\n                                style: {\n                                  fontSize: 11,\n                                  color: \"var(--green)\",\n                                },\n                              },\n                              h(\"i\", {\n                                className: \"fa fa-check-circle\",\n                                style: {\n                                  marginRight: 4,\n                                },\n                              }),\n                              selectedFiles[fieldName].name +\n                                \" (\" +\n                                (selectedFiles[fieldName].size / 1024).toFixed(\n                                  2,\n                                ) +\n                                \" KB)\",\n                            ),\n                        ),\n                      ),\n                    ),\n                  )\n                : // Show JSON editor for non-multipart requests\n                  (ep.method.toLowerCase() === \"post\" ||\n                    ep.method.toLowerCase() === \"put\" ||\n                    ep.method.toLowerCase() === \"patch\") &&\n                    bodyExample &&\n                    h(\n                      \"div\",\n                      { style: { marginBottom: 12 } },\n                      h(\n                        \"div\",\n                        {\n                          style: {\n                            fontSize: 11,\n                            fontWeight: 600,\n                            color: \"var(--t3)\",\n                            textTransform: \"uppercase\",\n                            letterSpacing: \"0.08em\",\n                            marginBottom: 8,\n                          },\n                        },\n                        \"Request Body\",\n                      ),\n                      h(CodeEditor, {\n                        value: bodyText,\n                        onChange: setBodyText,\n                      }),\n                    ),\n              h(ResponseDisplay, { response, loading }),\n            ),\n        );\n      }\n\n      function EndpointDetail({\n        ep,\n        spec,\n        apiKey,\n        bearerToken,\n        authType,\n        baseUrl,\n        setApiKey,\n        setBearerToken,\n        setAuthType,\n        showApiKey,\n        setShowApiKey,\n        showBearer,\n        setShowBearer,\n      }) {\n        if (!ep)\n          return h(\n            \"div\",\n            { className: \"empty\" },\n            h(\"i\", { className: \"fa fa-book-open\" }),\n            h(\"p\", null, \"Select an endpoint to view details\"),\n          );\n        const { path, method, op } = ep;\n        return h(\n          \"div\",\n          { className: \"fu\", key: ep.id },\n          h(\n            \"div\",\n            { className: `ep-hdr` },\n            h(\n              \"div\",\n              { className: \"ep-hdr-row\" },\n\n              h(\n                \"div\",\n                { style: { flex: 1, minWidth: 0 } },\n                h(\"div\", { className: \"ep-summary\" }, op.summary),\n                op.description &&\n                  h(\n                    \"p\",\n                    {\n                      style: {\n                        fontSize: 13,\n                        color: \"var(--t2)\",\n                        marginTop: 4,\n                      },\n                    },\n                    op.description,\n                  ),\n                h(\"code\", { className: \"ep-path-pill\" }, path),\n                h(\n                  \"div\",\n                  {\n                    style: {\n                      display: \"flex\",\n                      alignItems: \"center\",\n                      gap: 10,\n                      flexWrap: \"wrap\",\n                      marginTop: 10,\n                    },\n                  },\n                  h(MethodBadge, { method }),\n\n                  (op.security || spec.security) &&\n                    h(\n                      \"span\",\n                      { className: \"auth-pill\" },\n                      h(\"i\", {\n                        className: \"fa fa-key\",\n                        style: { fontSize: 9 },\n                      }),\n                      \" Auth required\",\n                    ),\n                ),\n              ),\n            ),\n          ),\n          h(\n            \"div\",\n            { className: \"ep-body\" },\n            h(ParamsTable, { params: op.parameters }),\n            h(RequestBodyPanel, { op, spec }),\n            h(CodeSamples, {\n              ep,\n              spec,\n              apiKey,\n              bearerToken,\n              authType,\n              baseUrl,\n              setApiKey,\n              setBearerToken,\n              setAuthType,\n              showApiKey,\n              setShowApiKey,\n              showBearer,\n              setShowBearer,\n            }),\n            h(ResponsesPanel, { responses: op.responses }),\n          ),\n        );\n      }\n\n      function Sidebar({\n        spec,\n        endpoints,\n        activeId,\n        onSelect,\n        search,\n        onSearch,\n        theme,\n        onTheme,\n      }) {\n        const grouped = useMemo(\n          () => groupByTag(endpoints, spec.tags),\n          [endpoints, spec.tags],\n        );\n        const [expandedTag, setExpandedTag] = useState(null);\n        const [showThemeMenu, setShowThemeMenu] = useState(false);\n\n        useEffect(() => {\n          if (!showThemeMenu) return;\n          const handleClickOutside = () => setShowThemeMenu(false);\n          document.addEventListener(\"click\", handleClickOutside);\n          return () =>\n            document.removeEventListener(\"click\", handleClickOutside);\n        }, [showThemeMenu]);\n\n        useEffect(() => {\n          if (activeId) {\n            for (const [tag, eps] of Object.entries(grouped)) {\n              if (eps.find((ep) => ep.id === activeId)) {\n                setExpandedTag(tag);\n                break;\n              }\n            }\n          } else if (Object.keys(grouped).length > 0) {\n            setExpandedTag(Object.keys(grouped)[0]);\n          }\n        }, [activeId, grouped]);\n\n        const toggleTag = (tag) => {\n          setExpandedTag(expandedTag === tag ? null : tag);\n        };\n\n        const themeOptions = [\n          { value: \"light\", icon: \"fa-sun\", label: \"Light\" },\n          { value: \"dark\", icon: \"fa-moon\", label: \"Dark\" },\n          {\n            value: \"system\",\n            icon: \"fa-circle-half-stroke\",\n            label: \"System\",\n          },\n        ];\n\n        const currentTheme =\n          themeOptions.find((t) => t.value === theme) || themeOptions[0];\n        return h(\n          Fragment,\n          null,\n          h(\n            \"div\",\n            { className: \"sb-hdr\" },\n            h(\n              \"div\",\n              { className: \"sb-brand\" },\n              spec.info?.[\"x-logo\"]\n                ? h(\"img\", {\n                    className: \"sb-logo sb-logo-img\",\n                    src: spec.info[\"x-logo\"],\n                    alt: spec.info?.title || \"Logo\",\n                    style: {\n                      objectFit: \"contain\",\n                      padding: \"0\",\n                      background: \"transparent\",\n                    },\n                  })\n                : h(\n                    \"div\",\n                    { className: \"sb-title\" },\n                    spec.info?.title || \"API Docs\",\n                  ),\n            ),\n            h(\n              \"div\",\n              {\n                className: \"sb-themes\",\n                style: { position: \"relative\" },\n              },\n              h(\n                \"button\",\n                {\n                  className: \"theme-btn\",\n                  onClick: (e) => {\n                    e.stopPropagation();\n                    setShowThemeMenu(!showThemeMenu);\n                  },\n                  title: `${currentTheme.label} theme (click to change)`,\n                  style: {\n                    display: \"flex\",\n                    alignItems: \"center\",\n                    gap: \"1px\",\n                  },\n                },\n                h(\"i\", {\n                  className: `fa ${currentTheme.icon}`,\n                }),\n              ),\n              showThemeMenu &&\n                h(\n                  \"div\",\n                  {\n                    className: \"theme-dropdown\",\n                    style: {\n                      position: \"absolute\",\n                      top: \"100%\",\n                      right: 0,\n                      marginTop: \"4px\",\n                      background: \"var(--bg-card)\",\n                      border: \"1px solid var(--bd)\",\n                      borderRadius: \"6px\",\n                      boxShadow: \"var(--shadow-md)\",\n                      zIndex: 100,\n                      minWidth: \"120px\",\n                      overflow: \"hidden\",\n                    },\n                    onClick: (e) => e.stopPropagation(),\n                  },\n                  themeOptions.map((opt) =>\n                    h(\n                      \"button\",\n                      {\n                        key: opt.value,\n                        className: \"theme-dropdown-item\",\n                        onClick: () => {\n                          onTheme(opt.value);\n                          setShowThemeMenu(false);\n                        },\n                        style: {\n                          display: \"flex\",\n                          alignItems: \"center\",\n                          gap: \"8px\",\n                          padding: \"8px 12px\",\n                          width: \"100%\",\n                          background:\n                            theme === opt.value\n                              ? \"var(--accent-bg)\"\n                              : \"transparent\",\n                          border: \"none\",\n                          borderBottom:\n                            theme === opt.value\n                              ? \"none\"\n                              : \"1px solid var(--bd-sub)\",\n                          color:\n                            theme === opt.value ? \"var(--accent)\" : \"var(--t2)\",\n                          fontSize: \"12px\",\n                          cursor: \"pointer\",\n                          transition: \"all 0.15s\",\n                          textAlign: \"left\",\n                        },\n                      },\n                      h(\"i\", {\n                        className: `fa ${opt.icon}`,\n                        style: {\n                          fontSize: \"12px\",\n                          width: \"14px\",\n                        },\n                      }),\n                      h(\n                        \"span\",\n                        {\n                          style: {\n                            fontSize: \"12px\",\n                            fontWeight: theme === opt.value ? 600 : 400,\n                          },\n                        },\n                        opt.label,\n                      ),\n                      theme === opt.value &&\n                        h(\"i\", {\n                          className: \"fa fa-check\",\n                          style: {\n                            marginLeft: \"auto\",\n                            fontSize: \"11px\",\n                          },\n                        }),\n                    ),\n                  ),\n                ),\n            ),\n          ),\n          h(\n            \"div\",\n            { className: \"sb-srch\" },\n            h(\"i\", { className: \"fa fa-magnifying-glass\" }),\n            h(\"input\", {\n              type: \"text\",\n              placeholder: \"Search....\",\n              value: search,\n              onChange: (e) => onSearch(e.target.value),\n            }),\n          ),\n          h(\n            \"div\",\n            { className: \"sb-nav\" },\n            Object.entries(grouped).map(([tag, eps]) => {\n              const filtered = eps.filter(\n                (ep) =>\n                  !search ||\n                  ep.path.toLowerCase().includes(search.toLowerCase()) ||\n                  (ep.op.summary || \"\")\n                    .toLowerCase()\n                    .includes(search.toLowerCase()),\n              );\n              if (!filtered.length) return null;\n              const isExpanded = expandedTag === tag;\n              return h(\n                \"div\",\n                { key: tag },\n                h(\n                  \"div\",\n                  {\n                    className: \"sb-grp-hd\",\n                    onClick: () => toggleTag(tag),\n                  },\n                  h(\n                    \"span\",\n                    {\n                      style: {\n                        flex: 1,\n                        textTransform: \"capitalize\",\n                        fontSize: 16,\n                      },\n                    },\n                    tag,\n                  ),\n                  h(\"i\", {\n                    className: isExpanded\n                      ? \"fa fa-chevron-down\"\n                      : \"fa fa-chevron-right\",\n                    style: {\n                      fontSize: \"10px\",\n                      color: \"var(--t3)\",\n                    },\n                  }),\n                ),\n                isExpanded &&\n                  filtered.map((ep) =>\n                    h(\n                      \"a\",\n                      {\n                        key: ep.id,\n                        href: `#${ep.id}`,\n                        className: `ep-btn ${activeId === ep.id ? \"on\" : \"\"}`,\n                        onClick: (e) => {\n                          e.preventDefault();\n                          onSelect(ep);\n                        },\n                      },\n                      h(\n                        \"span\",\n                        {\n                          className: `ep-m m-${ep.method}`,\n                        },\n                        ep.method.toUpperCase(),\n                      ),\n                      h(\n                        \"span\",\n                        { className: \"ep-p\" },\n                        ep.op.summary || ep.path,\n                      ),\n                    ),\n                  ),\n              );\n            }),\n          ),\n          h(\n            \"div\",\n            { className: \"sb-foot\" },\n            h(\"span\", null, `${endpoints.length} endpoints`),\n            h(\n              \"span\",\n              {\n                style: {\n                  display: \"flex\",\n                  alignItems: \"center\",\n                  gap: 5,\n                },\n              },\n              h(\"i\", {\n                className: \"fa fa-circle-dot\",\n                style: { color: \"var(--green)\", fontSize: 9 },\n              }),\n              \"OpenAPI \",\n              spec.openapi,\n            ),\n          ),\n        );\n      }\n\n      function EmptyState({ onImportClick }) {\n        const loadSampleData = () => {\n          try {\n            // Use embedded sample data if available\n            if (typeof SAMPLE_DATA !== \"undefined\" && SAMPLE_DATA) {\n              const spec =\n                typeof SAMPLE_DATA === \"string\"\n                  ? JSON.parse(SAMPLE_DATA)\n                  : SAMPLE_DATA;\n              onImportClick(spec);\n            } else {\n              console.error(\"No sample data available\");\n            }\n          } catch (error) {\n            console.error(\"Failed to load sample data:\", error);\n          }\n        };\n\n        return h(\n          \"div\",\n          {\n            className: \"empty\",\n            style: {\n              display: \"flex\",\n              flexDirection: \"column\",\n              alignItems: \"center\",\n              justifyContent: \"center\",\n              minHeight: \"100%\",\n              gap: \"16px\",\n            },\n          },\n          h(\"i\", {\n            className: \"fa fa-book-open\",\n            style: { fontSize: \"48px\", opacity: \"0.2\" },\n          }),\n          h(\n            \"div\",\n            { style: { textAlign: \"center\", maxWidth: \"400px\" } },\n            h(\n              \"h2\",\n              {\n                style: {\n                  fontSize: \"18px\",\n                  fontWeight: \"700\",\n                  color: \"var(--t1)\",\n                  marginBottom: \"8px\",\n                },\n              },\n              \"No API Specification Loaded\",\n            ),\n            h(\n              \"p\",\n              {\n                style: {\n                  fontSize: \"13px\",\n                  color: \"var(--t2)\",\n                  lineHeight: \"1.5\",\n                },\n              },\n              \"Click the button below to load a sample API and explore the documentation features.\",\n            ),\n          ),\n          h(\n            \"button\",\n            {\n              className: \"btn-p\",\n              style: {\n                display: \"flex\",\n                alignItems: \"center\",\n                gap: \"8px\",\n                padding: \"10px 20px\",\n                width: \"auto\",\n              },\n              onClick: loadSampleData,\n            },\n            h(\"i\", { className: \"fa fa-flask\" }),\n            \"Try Sample API\",\n          ),\n        );\n      }\n\n      function App() {\n        // Initialize spec from injected value if available\n        let initialSpec = null;\n        if (typeof INJECTED_SPEC !== \"undefined\" && INJECTED_SPEC) {\n          try {\n            initialSpec =\n              typeof INJECTED_SPEC === \"string\"\n                ? JSON.parse(INJECTED_SPEC)\n                : INJECTED_SPEC;\n          } catch (e) {\n            console.error(\"Failed to parse injected spec:\", e);\n          }\n        }\n\n        const [spec, setSpec] = useState(initialSpec);\n        const [search, setSearch] = useState(\"\");\n        const endpoints = useMemo(() => (spec ? buildEps(spec) : []), [spec]);\n\n        const [activeEp, setActive] = useState(() => {\n          const eps = initialSpec ? buildEps(initialSpec) : [];\n          const hash = window.location.hash.slice(1);\n          if (hash) {\n            const ep = eps.find((e) => e.id === hash);\n            if (ep) return ep;\n          }\n          if (eps.length > 0) {\n            const grouped = groupByTag(eps, initialSpec?.tags);\n            const tagKeys = Object.keys(grouped);\n            if (tagKeys.length > 0) {\n              return grouped[tagKeys[0]][0];\n            }\n            return eps[0];\n          }\n          return null;\n        });\n\n        const [theme, setTheme] = useState(() => {\n          try {\n            return localStorage.getItem(\"openapi-ui-theme\") || \"system\";\n          } catch {\n            return \"system\";\n          }\n        });\n        const [apiKey, setApiKey] = useState(\"\");\n        const [showApiKey, setShowApiKey] = useState(false);\n        const [bearerToken, setBearerToken] = useState(\"\");\n        const [showBearer, setShowBearer] = useState(false);\n        const [authType, setAuthType] = useState(\"api_key\");\n        const [baseUrl, setBaseUrl] = useState(\"\");\n\n        // Initialize baseUrl from spec.servers when spec changes\n        useEffect(() => {\n          const serverUrl = spec?.servers?.[0]?.url || \"\";\n          setBaseUrl(serverUrl);\n        }, [spec]);\n\n        // Sync active endpoint when endpoints change (e.g. after manual import)\n        useEffect(() => {\n          if (!endpoints.length) {\n            setActive(null);\n            return;\n          }\n\n          const hash = window.location.hash.slice(1);\n          if (hash) {\n            const ep = endpoints.find((e) => e.id === hash);\n            if (ep) {\n              setActive(ep);\n              return;\n            }\n          }\n\n          // Default to visually first if not already set to a valid endpoint in the current spec\n          if (!activeEp || !endpoints.find((e) => e.id === activeEp.id)) {\n            const grouped = groupByTag(endpoints, spec?.tags);\n            const tagKeys = Object.keys(grouped);\n            if (tagKeys.length > 0) {\n              setActive(grouped[tagKeys[0]][0]);\n            } else {\n              setActive(endpoints[0]);\n            }\n          }\n        }, [endpoints]);\n\n        // Handle hash navigation\n        useEffect(() => {\n          const handleHashChange = () => {\n            const hash = window.location.hash.slice(1);\n            if (hash) {\n              const ep = endpoints.find((e) => e.id === hash);\n              if (ep && ep.id !== activeEp?.id) {\n                setActive(ep);\n              }\n            } else if (endpoints.length > 0) {\n              const grouped = groupByTag(endpoints, spec.tags);\n              const firstTag = Object.keys(grouped)[0];\n              const firstEp =\n                firstTag && grouped[firstTag].length > 0\n                  ? grouped[firstTag][0]\n                  : endpoints[0];\n\n              if (activeEp?.id !== firstEp.id) {\n                setActive(firstEp);\n              }\n            }\n          };\n\n          window.addEventListener(\"hashchange\", handleHashChange);\n          return () =>\n            window.removeEventListener(\"hashchange\", handleHashChange);\n        }, [endpoints, activeEp, spec.tags]);\n\n        const applyTheme = useCallback((t) => {\n          const r =\n            t === \"system\"\n              ? window.matchMedia(\"(prefers-color-scheme: dark)\").matches\n                ? \"dark\"\n                : \"light\"\n              : t;\n          document.documentElement.setAttribute(\"data-theme\", r);\n          const d = document.getElementById(\"hljs-dark\");\n          const l = document.getElementById(\"hljs-light\");\n          if (d) d.disabled = r !== \"dark\";\n          if (l) l.disabled = r !== \"light\";\n        }, []);\n\n        const changeTheme = useCallback(\n          (t) => {\n            setTheme(t);\n            applyTheme(t);\n            try {\n              localStorage.setItem(\"openapi-ui-theme\", t);\n            } catch {}\n          },\n          [applyTheme],\n        );\n\n        useEffect(() => {\n          // Apply current theme\n          applyTheme(theme);\n\n          // Setup listener for system changes\n          const mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\n          const handler = (e) => {\n            if (theme === \"system\") {\n              applyTheme(\"system\");\n            }\n          };\n\n          // Initial check isn\'t needed here as applyTheme(theme) runs on every \'theme\' change\n          // and theme is initialized correctly.\n\n          mq.addEventListener(\"change\", handler);\n          return () => mq.removeEventListener(\"change\", handler);\n        }, [applyTheme, theme]);\n\n        // Clear sensitive auth data on page unload (security measure)\n        useEffect(() => {\n          const handleBeforeUnload = () => {\n            // Credentials are in-memory only, cleared automatically\n            console.log(\"Page unloaded - credentials cleared\");\n          };\n          window.addEventListener(\"beforeunload\", handleBeforeUnload);\n          return () =>\n            window.removeEventListener(\"beforeunload\", handleBeforeUnload);\n        }, []);\n\n        // Update hash when active endpoint changes\n        useEffect(() => {\n          if (activeEp) {\n            window.location.hash = activeEp.id;\n          }\n        }, [activeEp]);\n\n        // Update page title based on spec and active endpoint\n        useEffect(() => {\n          const specTitle = spec?.info?.title || \"API Documentation\";\n          if (activeEp) {\n            const endpointTitle = activeEp.op.summary || activeEp.path;\n            document.title = `${endpointTitle} - ${specTitle}`;\n          } else {\n            document.title = specTitle;\n          }\n        }, [activeEp, spec]);\n\n        // Show empty state if no spec is loaded\n        if (!spec) {\n          return h(EmptyState, {\n            onImportClick: (sampleSpec) => {\n              setSpec(sampleSpec);\n            },\n          });\n        }\n\n        return h(\n          \"div\",\n          { className: \"app\" },\n          h(\n            \"div\",\n            { className: \"sidebar\" },\n            h(Sidebar, {\n              spec,\n              endpoints,\n              activeId: activeEp?.id,\n              onSelect: setActive,\n              search,\n              onSearch: setSearch,\n              theme,\n              onTheme: changeTheme,\n            }),\n          ),\n          h(\n            \"div\",\n            { className: \"main\" },\n            h(\n              \"div\",\n              { className: \"topbar\" },\n              h(\n                \"div\",\n                {\n                  style: {\n                    display: \"flex\",\n                    alignItems: \"center\",\n                    gap: 8,\n                    minWidth: 0,\n                    overflow: \"hidden\",\n                  },\n                },\n                (spec.servers || []).map((s, i) =>\n                  h(\n                    \"div\",\n                    { key: i, className: \"server-pill\" },\n                    h(\"i\", {\n                      className: \"fa fa-circle\",\n                      style: {\n                        fontSize: 6,\n                        color: i === 0 ? \"var(--green)\" : \"var(--t4)\",\n                      },\n                    }),\n                    h(\"span\", { className: \"server-url\" }, s.url),\n                    s.description &&\n                      h(\n                        \"span\",\n                        {\n                          style: {\n                            color: \"var(--t3)\",\n                            fontSize: 11,\n                          },\n                        },\n                        s.description,\n                      ),\n                  ),\n                ),\n              ),\n              h(\n                \"div\",\n                {\n                  style: {\n                    display: \"flex\",\n                    alignItems: \"center\",\n                    gap: 8,\n                    flexShrink: 0,\n                  },\n                },\n                h(\n                  \"div\",\n                  {\n                    className: `auth-status ${apiKey ? \"set\" : \"\"}`,\n                    style: { marginRight: 8 },\n                  },\n                  h(\n                    \"span\",\n                    { style: { marginLeft: 4 } },\n                    \"v\" + spec.info?.version,\n                  ),\n                ),\n              ),\n            ),\n            h(\n              \"div\",\n              { className: \"detail\" },\n              h(EndpointDetail, {\n                ep: activeEp,\n                spec,\n                apiKey,\n                bearerToken,\n                authType,\n                baseUrl,\n                setApiKey,\n                setBearerToken,\n                setAuthType,\n                showApiKey,\n                setShowApiKey,\n                showBearer,\n                setShowBearer,\n              }),\n            ),\n          ),\n        );\n      }\n\n      createRoot(document.getElementById(\"root\")).render(h(App));\n    </script>\n  </body>\n</html>\n";
Expand description

The raw HTML template used for rendering.