rapidoc 11.0.1 → 12.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +46 -31
  2. package/dist/rapidoc-min.js +1424 -213
  3. package/dist/rapidoc-static-min.js +13542 -0
  4. package/package.json +47 -35
  5. package/src/components/api-request.ts +869 -0
  6. package/src/components/api-response.ts +509 -0
  7. package/src/components/json-tree.ts +171 -0
  8. package/src/components/schema-base-element.ts +315 -0
  9. package/src/components/schema-table.ts +430 -0
  10. package/src/components/schema-tree.ts +386 -0
  11. package/src/components/tag-input.ts +184 -0
  12. package/src/index.js +6 -0
  13. package/src/oauth-receiver.ts +29 -0
  14. package/src/rapidoc-mini.ts +582 -0
  15. package/src/rapidoc.ts +1808 -0
  16. package/src/static/index.d.ts +149 -0
  17. package/src/static/index.js +62 -0
  18. package/src/static/navigation.js +497 -0
  19. package/src/static/render.js +490 -0
  20. package/src/static/serializer.js +216 -0
  21. package/src/styles/auth-styles.js +318 -0
  22. package/src/styles/border-styles.js +24 -0
  23. package/src/styles/css-bundle.js +129 -0
  24. package/src/styles/custom-styles.js +8 -0
  25. package/src/styles/dialog-box-styles.js +102 -0
  26. package/src/styles/endpoint-styles.js +559 -0
  27. package/src/styles/flex-styles.js +16 -0
  28. package/src/styles/font-styles.js +432 -0
  29. package/src/styles/info-styles.js +36 -0
  30. package/src/styles/input-styles.js +555 -0
  31. package/src/styles/layout-styles.js +396 -0
  32. package/src/styles/microlighter-styles.js +99 -0
  33. package/src/styles/nav-styles.js +609 -0
  34. package/src/styles/param-list-styles.js +261 -0
  35. package/src/styles/schema-styles.js +288 -0
  36. package/src/styles/tab-styles.js +106 -0
  37. package/src/styles/theme-tokens.js +582 -0
  38. package/src/templates/advanced-search-dialog-template.js +94 -0
  39. package/src/templates/api-response-template.js +219 -0
  40. package/src/templates/callback-template.js +86 -0
  41. package/src/templates/code-samples-template.js +52 -0
  42. package/src/templates/components-template.js +86 -0
  43. package/src/templates/downloads-template.js +85 -0
  44. package/src/templates/endpoint-template.js +386 -0
  45. package/src/templates/expanded-endpoint-template.js +266 -0
  46. package/src/templates/focused-endpoint-template.js +145 -0
  47. package/src/templates/header-template.js +92 -0
  48. package/src/templates/logo-template.js +21 -0
  49. package/src/templates/main-body-template.js +102 -0
  50. package/src/templates/navbar-template.js +489 -0
  51. package/src/templates/overview-template.js +104 -0
  52. package/src/templates/request-body-template.js +651 -0
  53. package/src/templates/request-params-template.js +474 -0
  54. package/src/templates/security-scheme-template.js +1263 -0
  55. package/src/templates/server-template.js +152 -0
  56. package/src/utils/auto-color-scheme.js +99 -0
  57. package/src/utils/bind-navbar.js +175 -0
  58. package/src/utils/color-utils.js +59 -0
  59. package/src/utils/common-utils.js +191 -0
  60. package/src/utils/curl-utils.js +123 -0
  61. package/src/utils/highlighter.js +416 -0
  62. package/src/utils/markdown-utils.js +110 -0
  63. package/src/utils/mcp/common.js +467 -0
  64. package/src/utils/mcp/generators/csharp.js +242 -0
  65. package/src/utils/mcp/generators/go.js +298 -0
  66. package/src/utils/mcp/generators/java.js +330 -0
  67. package/src/utils/mcp/generators/python.js +248 -0
  68. package/src/utils/mcp/generators/rust.js +281 -0
  69. package/src/utils/mcp/generators/typescript.js +290 -0
  70. package/src/utils/mcp-generator.js +143 -0
  71. package/src/utils/mock-interceptor.js +508 -0
  72. package/src/utils/oauth-utils.js +87 -0
  73. package/src/utils/request-utils.js +203 -0
  74. package/src/utils/response-utils.js +203 -0
  75. package/src/utils/sanitize.js +57 -0
  76. package/src/utils/schema-utils.js +1927 -0
  77. package/src/utils/slugger.js +189 -0
  78. package/src/utils/spec-parser.js +915 -0
  79. package/src/utils/url-utils.js +211 -0
  80. package/src/utils/xml-utils.js +61 -0
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Renders the interactive API response panel (status, headers, body, blob actions),
3
+ * live cURL preview/copy, and the execute/Try action controls for <api-request>.
4
+ */
5
+ import { html } from "lit";
6
+ import { ifDefined } from "lit/directives/if-defined.js";
7
+ import { copyToClipboard, downloadResource, viewResource } from "~/utils/common-utils";
8
+
9
+ export function curlSyntaxTemplate(display = "flex") {
10
+ return html`
11
+ <div class="col m-markdown" style="flex:1; display:${display}; position:relative; max-width: 100%;">
12
+ <button
13
+ class="btn btn-sm btn-outline toolbar-btn"
14
+ style="position:absolute; top:12px; right:8px"
15
+ @click="${(e) => {
16
+ copyToClipboard(this.curlSyntax.trim().replace(/\\$/, ""), e);
17
+ }}"
18
+ part="btn btn-outline btn-copy"
19
+ >
20
+ Copy
21
+ </button>
22
+ <pre style="white-space:pre"><code class="language-shell">${this.curlSyntax.trim().replace(/\\$/, "")}</code></pre>
23
+ </div>
24
+ `;
25
+ }
26
+
27
+ export function apiResponseTabTemplate() {
28
+ let responseFormat = "";
29
+ let responseContent = "";
30
+ if (!this.responseIsBlob) {
31
+ if (
32
+ this.responseHeaders.includes("application/x-ndjson") ||
33
+ this.responseHeaders.includes("json")
34
+ ) {
35
+ responseFormat = "json";
36
+ } else if (this.responseHeaders.includes("html") || this.responseHeaders.includes("xml")) {
37
+ responseFormat = "html";
38
+ } else {
39
+ responseFormat = "text";
40
+ }
41
+ responseContent = html`<code class="language-${responseFormat}">${this.responseText}</code>`;
42
+ }
43
+ return html` <div class="row" style="font-size:var(--font-size-small); margin:5px 0; justify-content:space-between; align-items:center;">
44
+ <div class="response-message ${this.responseStatus}">Response Status: ${this.responseMessage}</div>
45
+ <button class="btn btn-outline" part="btn btn-outline btn-clear-response" @click="${this.clearResponseData}">CLEAR RESPONSE</button>
46
+ </div>
47
+ <div part="tab-panel" class="tab-panel col">
48
+ <div
49
+ id="tab_buttons"
50
+ part="tab-btn-row"
51
+ class="tab-buttons row"
52
+ @click="${(e) => {
53
+ if (
54
+ e.target.classList.contains("tab-btn") === false &&
55
+ e.target.classList.contains("tab") === false
56
+ ) {
57
+ return;
58
+ }
59
+ this.activeResponseTab = e.target.dataset.tab;
60
+ }}"
61
+ >
62
+ <button part="tab-btn" class="tab-btn ${this.activeResponseTab === "response" ? "active" : ""}" data-tab="response">
63
+ RESPONSE
64
+ </button>
65
+ <button part="tab-btn" class="tab-btn ${this.activeResponseTab === "headers" ? "active" : ""}" data-tab="headers">
66
+ RESPONSE HEADERS
67
+ </button>
68
+ ${
69
+ this.showCurlBeforeTry === "true"
70
+ ? ""
71
+ : html`<button part="tab-btn" class="tab-btn ${this.activeResponseTab === "curl" ? "active" : ""}" data-tab="curl">
72
+ CURL
73
+ </button>`
74
+ }
75
+ </div>
76
+ ${
77
+ this.responseIsBlob
78
+ ? html`<div
79
+ part="tab-content"
80
+ class="tab-content col"
81
+ style="flex:1; display:${this.activeResponseTab === "response" ? "flex" : "none"};"
82
+ >
83
+ ${
84
+ this.responseBlobType === "image"
85
+ ? html`<img style="max-height:var(--resp-area-height); object-fit:contain;" class="mar-top-8" src="${ifDefined(this.responseBlobUrl)}"></img>`
86
+ : ""
87
+ }
88
+ <button
89
+ class="btn btn-outline mar-top-8"
90
+ style="width:135px"
91
+ @click="${() => {
92
+ downloadResource(this.responseBlobUrl, this.respContentDisposition);
93
+ }}"
94
+ part="btn btn-outline"
95
+ >
96
+ DOWNLOAD
97
+ </button>
98
+ ${
99
+ this.responseBlobType === "view" || this.responseBlobType === "image"
100
+ ? html`<button
101
+ class="btn btn-outline mar-top-8"
102
+ style="width:135px"
103
+ @click="${() => {
104
+ viewResource(this.responseBlobUrl);
105
+ }}"
106
+ part="btn btn-outline"
107
+ >
108
+ VIEW (NEW TAB)
109
+ </button>`
110
+ : ""
111
+ }
112
+ </div>`
113
+ : html`<div
114
+ part="tab-content"
115
+ class="tab-content col m-markdown"
116
+ style="flex:1; display:${this.activeResponseTab === "response" ? "flex" : "none"};"
117
+ >
118
+ <button
119
+ class="btn btn-sm btn-outline toolbar-btn"
120
+ @click="${(e) => {
121
+ copyToClipboard(this.responseText, e);
122
+ }}"
123
+ part="btn btn-outline btn-copy"
124
+ >
125
+ Copy
126
+ </button>
127
+ <pre style="white-space:pre; min-height:50px; height:var(--resp-area-height); resize:vertical; overflow:auto">
128
+ ${responseContent}</pre>
129
+ </div>`
130
+ }
131
+ <div
132
+ part="tab-content"
133
+ class="tab-content col m-markdown"
134
+ style="flex:1; display:${this.activeResponseTab === "headers" ? "flex" : "none"};"
135
+ >
136
+ <button
137
+ class="btn btn-sm btn-outline toolbar-btn"
138
+ @click="${(e) => {
139
+ copyToClipboard(this.responseHeaders, e);
140
+ }}"
141
+ part="btn btn-outline btn-copy"
142
+ >
143
+ Copy
144
+ </button>
145
+ <pre style="white-space:pre"><code class="language-css">${this.responseHeaders}</code></pre>
146
+ </div>
147
+ ${this.showCurlBeforeTry === "true" ? "" : curlSyntaxTemplate.call(this, this.activeResponseTab === "curl" ? "flex" : "none")}
148
+ </div>`;
149
+ }
150
+
151
+ export function apiCallTemplate() {
152
+ let authHtml;
153
+ if (this.security?.length > 0 && !this.security.every((s) => !s || Object.keys(s).length === 0)) {
154
+ if (this.api_keys.length > 0) {
155
+ authHtml = html`<span class="blue-text">
156
+ ${this.api_keys.length === 1 ? `${this.api_keys[0]?.typeDisplay} in ${this.api_keys[0].in}` : `${this.api_keys.length} API keys applied`}
157
+ </span>`;
158
+ } else if (this.security.some((s) => !s || Object.keys(s).length === 0)) {
159
+ authHtml = html`Optional (None Applied)`;
160
+ } else {
161
+ authHtml = html`Required <span class="red-text">(None Applied)</span>`;
162
+ }
163
+ } else {
164
+ authHtml = html`Not Required`;
165
+ }
166
+
167
+ return html`<div class="try-bar" part="wrap-request-btn">
168
+ <div class="try-layout">
169
+ <dl class="try-info">
170
+ ${
171
+ this.serverUrl
172
+ ? html`<dt>API Server</dt>
173
+ <dd class="try-server-url">${this.serverUrl}</dd>`
174
+ : ""
175
+ }
176
+ <dt>Authentication</dt>
177
+ <dd>${authHtml}</dd>
178
+ </dl>
179
+ <div class="try-actions">
180
+ ${
181
+ this.parameters.length > 0 || this.request_body
182
+ ? html` <button
183
+ class="btn btn-outline"
184
+ part="btn btn-outline btn-fill"
185
+ ?disabled="${this.loading}"
186
+ @click="${this.onFillRequestData}"
187
+ title="Fills with example data (if provided)"
188
+ >
189
+ FILL EXAMPLE
190
+ </button>
191
+ <button
192
+ class="btn btn-outline"
193
+ part="btn btn-outline btn-clear"
194
+ ?disabled="${this.loading}"
195
+ @click="${this.onClearRequestData}"
196
+ >
197
+ CLEAR
198
+ </button>`
199
+ : ""
200
+ }
201
+ <button
202
+ class="btn btn-primary"
203
+ part="btn btn-try"
204
+ ?disabled="${this.loading}"
205
+ aria-busy="${this.loading}"
206
+ @click="${this.onTryClick}"
207
+ >
208
+ ${this.loading ? "TRYING..." : "TRY"}
209
+ </button>
210
+ </div>
211
+ </div>
212
+ </div>
213
+ <div class="row" style="font-size:var(--font-size-small); margin:5px 0">
214
+ ${this.showCurlBeforeTry === "true" ? curlSyntaxTemplate.call(this) : ""}
215
+ </div>
216
+ ${this.responseMessage === "" ? "" : apiResponseTabTemplate.call(this)} `;
217
+ }
218
+
219
+ export default apiCallTemplate;
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Renders OpenAPI callback requests and nested response definitions within an endpoint.
3
+ */
4
+ import { html } from "lit";
5
+
6
+ export default function callbackTemplate(callbacks) {
7
+ return html`
8
+ <h4 class="rapidoc-heading label-heading req-res-title" part="heading heading-4">Callbacks</h4>
9
+ ${Object.entries(callbacks).map(
10
+ (kv) => html`
11
+ <div class="tiny-title" style="padding: 12px; border:1px solid var(--border)">
12
+ <h5 class="rapidoc-heading callback-name" part="heading heading-5">${kv[0]}</h5>
13
+ ${Object.entries(kv[1]).map(
14
+ (pathObj) => html`
15
+ <div class="callback-operation-list mono-font small-font-size">
16
+ ${Object.entries(pathObj[1]).map(
17
+ (method) => html`
18
+ <div class="callback-operation-item">
19
+ <div class="callback-operation-header">
20
+ <div class="method method-fg ${method[0]} callback-method">
21
+ <span style="font-size:20px;"> &#x2944; </span>
22
+ ${method[0]}
23
+ </div>
24
+ <span style="line-height:20px; vertical-align: baseline;">${pathObj[0]}</span>
25
+ </div>
26
+ <div class="expanded-req-resp-container">
27
+ <api-request
28
+ .config="${this.config}"
29
+ class="${this.renderStyle}-mode callback"
30
+ style="width:100%;"
31
+ callback="true"
32
+ method="${method[0] || ""}"
33
+ path="${pathObj[0] || ""}"
34
+ .parameters="${method[1]?.parameters || ""}"
35
+ .request_body="${method[1]?.requestBody || ""}"
36
+ fill-request-fields-with-example="${this.fillRequestFieldsWithExample}"
37
+ allow-try="false"
38
+ render-style="${this.renderStyle}"
39
+ schema-style="${this.schemaStyle}"
40
+ active-schema-tab="${this.defaultSchemaTab}"
41
+ schema-expand-level="${this.schemaExpandLevel}"
42
+ schema-description-expanded="${this.schemaDescriptionExpanded}"
43
+ allow-schema-description-expand-toggle="${this.allowSchemaDescriptionExpandToggle}"
44
+ schema-hide-read-only="false"
45
+ schema-hide-write-only="${this.schemaHideWriteOnly === "never" ? "false" : "true"}"
46
+ fetch-credentials="${this.fetchCredentials}"
47
+ exportparts="section-request-title:section-request-title, heading:heading, heading-4:heading-4, heading-5:heading-5, heading-6:heading-6,
48
+ wrap-request-btn:wrap-request-btn, btn:btn, btn-fill:btn-fill, btn-outline:btn-outline, btn-try:btn-try, btn-clear:btn-clear, btn-clear-resp:btn-clear-resp,
49
+ tab-panel:tab-panel, tab-btn:tab-btn, tab-btn-row:tab-btn-row, tab-content:tab-content,
50
+ file-input:file-input, textbox:textbox, textbox-param:textbox-param, textarea:textarea, textarea-param:textarea-param,
51
+ anchor:anchor, anchor-param-example:anchor-param-example, anchor-param-constraint:anchor-param-constraint, param-list:param-list, param:param, param-label:param-label, param-name:param-name, param-type:param-type, param-input:param-input, param-constraint:param-constraint, param-constraint-label:param-constraint-label, param-details:param-details, param-example-list:param-example-list, value-list:value-list, value-list-label:value-list-label, value-list-items:value-list-items, schema-description:schema-description, schema-multiline-toggle:schema-multiline-toggle"
52
+ >
53
+ </api-request>
54
+
55
+ <api-response
56
+ .config="${this.config}"
57
+ style="width:100%;"
58
+ class="${this.renderStyle}-mode"
59
+ callback="true"
60
+ .responses="${method[1]?.responses}"
61
+ render-style="${this.renderStyle}"
62
+ schema-style="${this.schemaStyle}"
63
+ active-schema-tab="${this.defaultSchemaTab}"
64
+ schema-expand-level="${this.schemaExpandLevel}"
65
+ schema-description-expanded="${this.schemaDescriptionExpanded}"
66
+ allow-schema-description-expand-toggle="${this.allowSchemaDescriptionExpandToggle}"
67
+ schema-hide-read-only="${this.schemaHideReadOnly === "never" ? "false" : "true"}"
68
+ schema-hide-write-only="false"
69
+ exportparts="section-response-title:section-response-title, heading:heading, heading-4:heading-4, heading-5:heading-5, heading-6:heading-6,
70
+ btn:btn, btn-response-status:btn-response-status, btn-selected-response-status:btn-selected-response-status, btn-fill:btn-fill, btn-outline:btn-outline, btn-copy:btn-copy,
71
+ tab-panel:tab-panel, tab-btn:tab-btn, tab-btn-row:tab-btn-row, tab-content:tab-content,
72
+ schema-description:schema-description, schema-multiline-toggle:schema-multiline-toggle, param-list:param-list, param:param, param-label:param-label, param-name:param-name, param-type:param-type, param-constraint:param-constraint, param-constraint-label:param-constraint-label, param-details:param-details, param-example-list:param-example-list, value-list:value-list, value-list-label:value-list-label, value-list-items:value-list-items"
73
+ >
74
+ </api-response>
75
+ </div>
76
+ </div>
77
+ `,
78
+ )}
79
+ </div>
80
+ `,
81
+ )}
82
+ </div>
83
+ `,
84
+ )}
85
+ `;
86
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Renders multi-language code snippets and custom x-codeSamples / x-code-samples tab panels.
3
+ */
4
+ import { html } from "lit";
5
+
6
+ import { copyToClipboard } from "~/utils/common-utils";
7
+ import { scheduleHighlight } from "~/utils/highlighter";
8
+
9
+ export default function codeSamplesTemplate(xCodeSamples) {
10
+ return html` <div class="code-samples-container">
11
+ <h4 class="rapidoc-heading label-heading table-title top-gap" part="heading heading-4">Code samples</h4>
12
+ <div
13
+ part="tab-panel"
14
+ class="tab-panel col"
15
+ @click="${(e) => {
16
+ if (!e.target.classList.contains("tab-btn")) {
17
+ return;
18
+ }
19
+ const clickedTab = e.target.dataset.tab;
20
+
21
+ const tabButtons = [...e.currentTarget.querySelectorAll(".tab-btn")];
22
+ const tabContents = [...e.currentTarget.querySelectorAll(".tab-content")];
23
+ tabButtons.forEach((tabBtnEl) =>
24
+ tabBtnEl.classList[tabBtnEl.dataset.tab === clickedTab ? "add" : "remove"]("active"),
25
+ );
26
+ tabContents.forEach((tabBodyEl) => {
27
+ tabBodyEl.style.display = tabBodyEl.dataset.tab === clickedTab ? "block" : "none";
28
+ });
29
+ scheduleHighlight(e.currentTarget.getRootNode()?.host?.shadowRoot || e.currentTarget);
30
+ }}"
31
+ >
32
+ <div part="tab-btn-row" class="tab-buttons row" style="width:100%; overflow:auto;">
33
+ ${xCodeSamples.map((v, i) => html`<button part="tab-btn" class="tab-btn ${i === 0 ? "active" : ""}" data-tab="${v.lang}${i}">${v.label || v.lang}</button>`)}
34
+ </div>
35
+ ${xCodeSamples.map(
36
+ (v, i) =>
37
+ html`<div class="tab-content m-markdown" style="display:${i === 0 ? "block" : "none"}" data-tab="${v.lang}${i}">
38
+ <button
39
+ class="btn btn-sm btn-outline toolbar-btn"
40
+ part="btn btn-outline btn-copy"
41
+ @click="${(e) => {
42
+ copyToClipboard(v.source, e);
43
+ }}"
44
+ >
45
+ Copy
46
+ </button>
47
+ <pre><code class="language-${v.lang?.toLowerCase()}">${v.source}</code></pre>
48
+ </div>`,
49
+ )}
50
+ </div>
51
+ </div>`;
52
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Renders reusable OpenAPI component definitions (schemas, models) in the document.
3
+ */
4
+ import { html } from "lit";
5
+ import { unsafeHTML } from "lit/directives/unsafe-html.js";
6
+ import { sanitizeHTML } from "../utils/sanitize.js";
7
+ import { marked } from "marked";
8
+ import { schemaToAST } from "~/utils/schema-utils";
9
+ import "~/components/json-tree";
10
+ import "~/components/schema-tree";
11
+ import "~/components/schema-table";
12
+
13
+ function schemaBodyTemplate(sComponent) {
14
+ return html` <div class="expanded-endpoint-body observe-me ${sComponent.name}" id="cmp--${sComponent.id}">
15
+ <h3 class="rapidoc-heading component-title" part="heading heading-3">
16
+ ${sComponent.name} <span style="color:var(--muted-foreground); font-size:var(--font-size-small); font-weight:400;"> Schema </span>
17
+ </h3>
18
+ ${
19
+ this.schemaStyle === "table"
20
+ ? html` <schema-table
21
+ .data="${schemaToAST(sComponent.component)}"
22
+ schema-expand-level="${this.schemaExpandLevel}"
23
+ schema-description-expanded="${this.schemaDescriptionExpanded}"
24
+ allow-schema-description-expand-toggle="${this.allowSchemaDescriptionExpandToggle}"
25
+ schema-hide-read-only="false"
26
+ schema-hide-write-only="${this.schemaHideWriteOnly}"
27
+ exportparts="schema-description:schema-description, schema-multiline-toggle:schema-multiline-toggle"
28
+ >
29
+ </schema-table>`
30
+ : html`<schema-tree
31
+ .data="${schemaToAST(sComponent.component)}"
32
+ schema-expand-level="${this.schemaExpandLevel}"
33
+ schema-description-expanded="${this.schemaDescriptionExpanded}"
34
+ allow-schema-description-expand-toggle="${this.allowSchemaDescriptionExpandToggle}"
35
+ schema-hide-read-only="false"
36
+ schema-hide-write-only="${this.schemaHideWriteOnly}"
37
+ exportparts="schema-description:schema-description, schema-multiline-toggle:schema-multiline-toggle"
38
+ >
39
+ </schema-tree>`
40
+ }
41
+ </div>`;
42
+ }
43
+
44
+ function componentBodyTemplate(sComponent, componentType) {
45
+ if (sComponent.id.indexOf("schemas-") !== -1) {
46
+ return schemaBodyTemplate.call(this, sComponent);
47
+ }
48
+ return html`
49
+ <div class="expanded-endpoint-body observe-me ${sComponent.name}" id="cmp--${sComponent.id}">
50
+ ${html`
51
+ <h3 class="rapidoc-heading component-title" part="heading heading-3">
52
+ ${sComponent.name}
53
+ <span style="color:var(--muted-foreground); font-size:var(--font-size-small); font-weight:400"> ${componentType} </span>
54
+ </h3>
55
+ ${
56
+ sComponent.component
57
+ ? html`<div class="mono-font regular-font-size" style="padding: 8px 0; color:var(--card-foreground)">
58
+ <json-tree class="border tree" render-style="${this.renderStyle}" .data="${sComponent.component}"> </json-tree>
59
+ </div>`
60
+ : ""
61
+ }
62
+ `}
63
+ </div>
64
+ `;
65
+ }
66
+
67
+ export default function componentsTemplate() {
68
+ if (!this.resolvedSpec) {
69
+ return "";
70
+ }
71
+ return html`
72
+ ${this.resolvedSpec.components.map(
73
+ (component) => html`
74
+ <div id="cmp--${component.name.toLowerCase()}" class="regular-font section-gap--read-mode observe-me read-mode-tag">
75
+ <h2 class="rapidoc-heading title tag" part="heading heading-2">${component.name}</h2>
76
+ ${unsafeHTML(`<div class='m-markdown regular-font'>${sanitizeHTML(marked(component.description || ""))}</div>`)}
77
+ </div>
78
+ <div class="regular-font section-gap--read-mode">
79
+ ${component.subComponents
80
+ .filter((c) => c.expanded !== false)
81
+ .map((sComponent) => componentBodyTemplate.call(this, sComponent, component.name))}
82
+ </div>
83
+ `,
84
+ )}
85
+ `;
86
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Dedicated Download & Export Action Bar for OpenAPI specs and curated MCP servers.
3
+ * Rendered independently of show-info to guarantee accessibility across all render modes.
4
+ */
5
+ import { html } from "lit";
6
+ import { downloadResource, viewResource } from "~/utils/common-utils";
7
+ import { parseMcpTargets, MCP_TARGET_LABELS } from "~/utils/mcp-generator";
8
+
9
+ export default function downloadsTemplate() {
10
+ const mcpTargets = parseMcpTargets(this.mcp);
11
+ const showSpecDownload = this.specUrl && this.allowSpecFileDownload === "true";
12
+ const showMcpExport = mcpTargets.length > 0;
13
+
14
+ if (!showSpecDownload && !showMcpExport) {
15
+ return "";
16
+ }
17
+
18
+ let sectionClass = "section-gap";
19
+ if (this.renderStyle === "focused") {
20
+ sectionClass = "section-gap--focused-mode";
21
+ } else if (this.renderStyle === "read") {
22
+ sectionClass = "section-gap--read-mode";
23
+ }
24
+
25
+ return html`
26
+ <section id="downloads" part="section-downloads" class="downloads-bar observe-me ${sectionClass}">
27
+ ${
28
+ showSpecDownload
29
+ ? html`
30
+ <button
31
+ class="btn btn-outline download-btn"
32
+ part="btn btn-outline"
33
+ @click="${() => {
34
+ downloadResource(this.specUrl, "openapi-spec");
35
+ }}"
36
+ >
37
+ Download OpenAPI spec
38
+ </button>
39
+ ${
40
+ this.specUrl?.trim().toLowerCase().endsWith("json")
41
+ ? html`<button
42
+ class="btn btn-outline download-btn view-spec"
43
+ part="btn btn-outline"
44
+ @click="${() => {
45
+ viewResource(this.specUrl);
46
+ }}"
47
+ >
48
+ View OpenAPI spec (New Tab)
49
+ </button>`
50
+ : ""
51
+ }
52
+ `
53
+ : ""
54
+ }
55
+ ${
56
+ showMcpExport
57
+ ? mcpTargets.length === 1
58
+ ? html`<button
59
+ class="btn btn-outline download-btn"
60
+ part="btn btn-outline btn-mcp-export"
61
+ @click="${() => {
62
+ this.downloadMcpServer(mcpTargets[0]);
63
+ }}"
64
+ >
65
+ Export MCP Server (${MCP_TARGET_LABELS[mcpTargets[0]] || mcpTargets[0]})
66
+ </button>`
67
+ : html`<select
68
+ class="btn btn-outline download-select"
69
+ part="btn btn-outline btn-mcp-export"
70
+ @change="${(e) => {
71
+ const target = e.target.value;
72
+ if (target) {
73
+ this.downloadMcpServer(target);
74
+ e.target.value = "";
75
+ }
76
+ }}"
77
+ >
78
+ <option value="" disabled selected>Export MCP Server</option>
79
+ ${mcpTargets.map((t) => html`<option value="${t}">Export ${MCP_TARGET_LABELS[t] || t}</option>`)}
80
+ </select>`
81
+ : ""
82
+ }
83
+ </section>
84
+ `;
85
+ }