@farming-labs/docs 0.2.120 → 0.2.121
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.
- package/README.md +8 -0
- package/dist/access.d.mts +13 -0
- package/dist/access.mjs +56 -0
- package/dist/agent-conformance.d.mts +60 -0
- package/dist/agent-conformance.mjs +538 -0
- package/dist/agent-contract.d.mts +47 -0
- package/dist/agent-contract.mjs +645 -0
- package/dist/agent-detection.d.mts +6 -0
- package/dist/agent-detection.mjs +83 -0
- package/dist/agent-evals.d.mts +224 -0
- package/dist/{agent-evals-BLNLvXbG.mjs → agent-evals.mjs} +7 -565
- package/dist/agent-feedback-evaluations-node.mjs +38 -0
- package/dist/agent-feedback-evaluations.d.mts +91 -0
- package/dist/{agent-feedback-evaluations-CcMGlgsO.mjs → agent-feedback-evaluations.mjs} +2 -2
- package/dist/agent-feedback-loop.d.mts +45 -0
- package/dist/{agent-feedback-loop-CcZL_sb1.mjs → agent-feedback-loop.mjs} +1 -1
- package/dist/agent-maintenance.d.mts +71 -0
- package/dist/{agent-maintenance-CuZ7iAKq.mjs → agent-maintenance.mjs} +1 -1
- package/dist/agent-provenance.d.mts +23 -0
- package/dist/agent-provenance.mjs +77 -0
- package/dist/agent-scope.mjs +321 -0
- package/dist/agent-skills-archive.mjs +86 -0
- package/dist/agent-skills-bundle.d.mts +1 -1
- package/dist/{agent-skills-progressive-disclosure-PjPtTYL-.mjs → agent-skills-progressive-disclosure.mjs} +2 -2
- package/dist/{agent-skills-server-D5IuvwM7.d.mts → agent-skills-server.d.mts} +3 -3
- package/dist/{agent-skills-server-BC7md56k.mjs → agent-skills-server.mjs} +2 -2
- package/dist/agent-skills-vite.d.mts +3 -3
- package/dist/agent-skills-vite.mjs +1 -2
- package/dist/agent-surface-drift.mjs +236 -0
- package/dist/{golden-evaluations-CLUViycJ.mjs → agent-usefulness.mjs} +5 -290
- package/dist/{content-change-hydration-wqXIZHp6.d.mts → agent.d.mts} +10 -411
- package/dist/agent.mjs +2729 -0
- package/dist/{analytics-w3FDOksr.d.mts → analytics.d.mts} +2 -2
- package/dist/{analytics-p8Vhhu2P.mjs → analytics.mjs} +2 -2
- package/dist/{sitemap-server-BUdPBcGs.d.mts → api-reference.d.mts} +2 -6
- package/dist/api-reference.mjs +1481 -0
- package/dist/{audience-exposure-DBrFWULA.d.mts → audience-exposure.d.mts} +1 -1
- package/dist/audience-exposure.mjs +8 -0
- package/dist/audience.d.mts +44 -0
- package/dist/audience.mjs +1654 -0
- package/dist/authoring-mcp.d.mts +44 -0
- package/dist/{authoring-mcp-CiCUQe3o.mjs → authoring-mcp.mjs} +3 -3
- package/dist/browser.d.mts +6 -4
- package/dist/browser.mjs +6 -7
- package/dist/build.d.mts +8 -0
- package/dist/build.mjs +8 -0
- package/dist/changelog.d.mts +20 -0
- package/dist/changelog.mjs +39 -0
- package/dist/{agent-export-CA6IUQ7T.mjs → cli/agent-export.mjs} +19 -45
- package/dist/{agent-CYk2DxY7.mjs → cli/agent.mjs} +6 -35
- package/dist/{agents-BUiVmj8i.mjs → cli/agents.mjs} +5 -18
- package/dist/cli/api-doctor.mjs +206 -0
- package/dist/{authoring-C2ymvYWl.mjs → cli/authoring.mjs} +2 -8
- package/dist/{cloud-6eKrlmKd.mjs → cli/cloud.mjs} +3 -3
- package/dist/{codeblocks-CHpgAwY-.mjs → cli/codeblocks.mjs} +2 -2
- package/dist/cli/config.d.mts +11 -0
- package/dist/{config-BZnKAj_Y.mjs → cli/config.mjs} +1 -1
- package/dist/cli/dev-banner.mjs +58 -0
- package/dist/{dev-Bnn-NUNa.mjs → cli/dev.mjs} +3 -57
- package/dist/{doctor-Bfkrkzad.mjs → cli/doctor.mjs} +41 -226
- package/dist/{downgrade-CHMta85e.mjs → cli/downgrade.mjs} +2 -2
- package/dist/cli/errors.d.mts +4 -0
- package/dist/{errors-BtnVLhNF.mjs → cli/errors.mjs} +1 -1
- package/dist/{feedback-evals-DMilg9pY.mjs → cli/feedback-evals.mjs} +3 -36
- package/dist/{feedback-DL4T5oIO.mjs → cli/feedback.mjs} +1 -1
- package/dist/cli/git-dates.mjs +35 -0
- package/dist/cli/golden-evaluations.mjs +52 -0
- package/dist/cli/index.d.mts +2 -3
- package/dist/cli/index.mjs +38 -38
- package/dist/{init-K0LmXDZ5.mjs → cli/init.mjs} +3 -3
- package/dist/{mcp-iLCmnPJI.mjs → cli/mcp.mjs} +7 -19
- package/dist/{openapi-diff-HcpUNjA-.mjs → cli/openapi-diff.mjs} +2 -7
- package/dist/{openapi-sdk-BQKC6H-_.mjs → cli/openapi-sdk.mjs} +2 -7
- package/dist/{openapi-source-DK6ts_Bk.mjs → cli/openapi-source.mjs} +2 -2
- package/dist/{package-version-HJhYeq4M.mjs → cli/package-version.mjs} +2 -2
- package/dist/{propose-JmUv_BTt.mjs → cli/propose.mjs} +1 -1
- package/dist/{review-C6EmbV0l.mjs → cli/review.mjs} +16 -15
- package/dist/{robots-DteZB8vl.mjs → cli/robots.mjs} +3 -7
- package/dist/{search-Bhuv_YsL.mjs → cli/search.mjs} +7 -20
- package/dist/{sitemap-DW5LDKDa.mjs → cli/sitemap.mjs} +7 -42
- package/dist/{skills-BAPQLSCm.mjs → cli/skills.mjs} +5 -19
- package/dist/{templates-BuQ8YsGv.mjs → cli/templates.mjs} +1 -1
- package/dist/{upgrade-DlQMOFV1.mjs → cli/upgrade.mjs} +2 -2
- package/dist/{utils-C7KDrPpA.mjs → cli/utils.mjs} +1 -1
- package/dist/client/react.d.mts +2 -18
- package/dist/client/react.mjs +1 -157
- package/dist/{cloud-analytics-D14HplI7.mjs → cloud-analytics.mjs} +1 -1
- package/dist/{cloud-ask-ai-BrsTXQf6.d.mts → cloud-ask-ai.d.mts} +2 -2
- package/dist/{cloud-ask-ai-BqKhjpS5.mjs → cloud-ask-ai.mjs} +3 -3
- package/dist/code-blocks.d.mts +51 -0
- package/dist/{code-blocks-GYdvS3BH.mjs → code-blocks.mjs} +2 -2
- package/dist/code-group-mdx.d.mts +11 -0
- package/dist/code-group-mdx.mjs +57 -0
- package/dist/content-change-hydration.d.mts +87 -0
- package/dist/{openapi-mcp-BIOTLiA7.mjs → content-change-hydration.mjs} +5 -210
- package/dist/content-changes.d.mts +47 -0
- package/dist/content-changes.mjs +216 -0
- package/dist/create-theme.d.mts +48 -0
- package/dist/create-theme.mjs +57 -0
- package/dist/date.d.mts +5 -0
- package/dist/date.mjs +17 -0
- package/dist/define-docs.d.mts +9 -0
- package/dist/define-docs.mjs +46 -0
- package/dist/docs-cloud-client.d.mts +18 -0
- package/dist/docs-cloud-client.mjs +161 -0
- package/dist/docs-cloud-server.d.mts +2 -2
- package/dist/docs-cloud-server.mjs +2 -3
- package/dist/heading-toc.d.mts +10 -0
- package/dist/heading-toc.mjs +23 -0
- package/dist/http-body.d.mts +14 -0
- package/dist/{http-body-DkAPk6D5.mjs → http-body.mjs} +1 -1
- package/dist/http-cache.d.mts +25 -0
- package/dist/http-cache.mjs +140 -0
- package/dist/http-link.mjs +81 -0
- package/dist/i18n.d.mts +18 -0
- package/dist/{i18n-B1gJ9EW0.mjs → i18n.mjs} +1 -1
- package/dist/index.d.mts +42 -674
- package/dist/index.mjs +40 -14
- package/dist/markdown-rendering.mjs +1 -1
- package/dist/markdown-section-cache.mjs +49 -0
- package/dist/markdown-sections.d.mts +47 -0
- package/dist/{markdown-sections-9Oxr33b-.mjs → markdown-sections.mjs} +29 -703
- package/dist/mcp-auth.d.mts +14 -0
- package/dist/mcp-auth.mjs +99 -0
- package/dist/mcp.d.mts +5 -3
- package/dist/mcp.mjs +21 -9
- package/dist/mdx.d.mts +6 -0
- package/dist/mdx.mjs +7 -0
- package/dist/metadata.d.mts +60 -0
- package/dist/{metadata-BOHn0Y5K.mjs → metadata.mjs} +2 -2
- package/dist/native-api-reference.mjs +968 -0
- package/dist/okf.d.mts +42 -0
- package/dist/okf.mjs +145 -0
- package/dist/openapi-contract.mjs +154 -0
- package/dist/{openapi-mcp-node-C4jUpZG6.mjs → openapi-mcp-node.mjs} +1 -1
- package/dist/openapi-mcp.d.mts +26 -0
- package/dist/openapi-mcp.mjs +132 -0
- package/dist/openapi-operations.d.mts +71 -0
- package/dist/{openapi-operations-DFpaqOqA.mjs → openapi-operations.mjs} +1 -1
- package/dist/openapi-overlays.mjs +221 -0
- package/dist/openapi-references.mjs +233 -0
- package/dist/pagination.d.mts +7 -0
- package/dist/pagination.mjs +100 -0
- package/dist/prompt-references.d.mts +12 -0
- package/dist/prompt-references.mjs +132 -0
- package/dist/prompt-utils.d.mts +36 -0
- package/dist/{prompt-utils-B5g-bv7o.mjs → prompt-utils.mjs} +1 -57
- package/dist/reading-time.d.mts +23 -0
- package/dist/reading-time.mjs +58 -0
- package/dist/related.d.mts +7 -0
- package/dist/related.mjs +50 -0
- package/dist/retrieval-digest.d.mts +16 -0
- package/dist/{retrieval-digest-CT-eIEcp.mjs → retrieval-digest.mjs} +1 -1
- package/dist/review.d.mts +69 -0
- package/dist/review.mjs +562 -0
- package/dist/robots.d.mts +54 -0
- package/dist/{robots-BYJAVeI6.mjs → robots.mjs} +5 -3
- package/dist/runtime.d.mts +3 -3
- package/dist/runtime.mjs +3 -9
- package/dist/search.d.mts +210 -0
- package/dist/search.mjs +3401 -0
- package/dist/server.d.mts +22 -198
- package/dist/server.mjs +21 -19
- package/dist/sidebar.d.mts +15 -0
- package/dist/sidebar.mjs +73 -0
- package/dist/sitemap-server.d.mts +7 -0
- package/dist/{sitemap-server-Lg3YhUx4.mjs → sitemap-server.mjs} +2 -2
- package/dist/{sitemap-Cl8vrixd.d.mts → sitemap.d.mts} +3 -3
- package/dist/sitemap.mjs +254 -0
- package/dist/{standards-discovery-BHPc7YIX.d.mts → standards-discovery.d.mts} +2 -2
- package/dist/{standards-discovery-sSEojU80.mjs → standards-discovery.mjs} +3 -139
- package/dist/telemetry-runtime.d.mts +42 -0
- package/dist/telemetry-runtime.mjs +238 -0
- package/dist/telemetry.d.mts +15 -0
- package/dist/telemetry.mjs +117 -0
- package/dist/{types-BJFeMXWZ.d.mts → types.d.mts} +3 -2
- package/dist/urls.d.mts +7 -0
- package/dist/urls.mjs +21 -0
- package/dist/utils.d.mts +8 -0
- package/dist/utils.mjs +22 -0
- package/package.json +21 -3
- package/dist/agent-DkAidqtV.mjs +0 -9026
- package/dist/api-reference-BYee4zMo.mjs +0 -3046
- package/dist/prompt-references-DMgbu_dz.mjs +0 -1082
- package/dist/retrieval-digest-AOx2-LbC.d.mts +0 -367
- package/dist/sidebar-CFHBj29f.mjs +0 -421
- package/dist/sidebar-CMPbVfGP.d.mts +0 -63
- /package/dist/{openapi-CfCzX_a1.mjs → cli/openapi.mjs} +0 -0
|
@@ -0,0 +1,968 @@
|
|
|
1
|
+
//#region src/native-api-reference.ts
|
|
2
|
+
const METHOD_TONES = {
|
|
3
|
+
GET: "success",
|
|
4
|
+
POST: "info",
|
|
5
|
+
PUT: "warning",
|
|
6
|
+
PATCH: "warning",
|
|
7
|
+
DELETE: "danger",
|
|
8
|
+
OPTIONS: "neutral",
|
|
9
|
+
HEAD: "neutral",
|
|
10
|
+
TRACE: "neutral"
|
|
11
|
+
};
|
|
12
|
+
function renderNativeApiReferenceHtml(options) {
|
|
13
|
+
const { model } = options;
|
|
14
|
+
const tokens = resolveTokens(options.theme);
|
|
15
|
+
const description = options.description?.trim();
|
|
16
|
+
const versions = options.versions ?? [];
|
|
17
|
+
const groupedOperations = groupOperations(model.operations);
|
|
18
|
+
const operationMarkup = model.operations.length > 0 ? model.operations.map((operation) => renderOperation(operation, options)).join("\n") : renderEmptyContract();
|
|
19
|
+
return `<!doctype html>
|
|
20
|
+
<html lang="en">
|
|
21
|
+
<head>
|
|
22
|
+
<meta charset="utf-8" />
|
|
23
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
24
|
+
<meta name="generator" content="@farming-labs/docs" />
|
|
25
|
+
<meta name="color-scheme" content="light dark" />
|
|
26
|
+
<title>${escapeHtml(options.pageTitle)}</title>
|
|
27
|
+
<style>${buildStyles(tokens)}</style>
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
<a class="fl-skip-link" href="#fl-api-content">Skip to API operations</a>
|
|
31
|
+
<div class="fl-api-shell">
|
|
32
|
+
<header class="fl-api-topbar">
|
|
33
|
+
<a class="fl-api-brand" href="${escapeAttribute(options.basePath)}" aria-label="API reference home">
|
|
34
|
+
<span class="fl-api-brand-mark" aria-hidden="true">FL</span>
|
|
35
|
+
<span>API Reference</span>
|
|
36
|
+
</a>
|
|
37
|
+
<div class="fl-api-topbar-actions">
|
|
38
|
+
${renderVersionSelect(versions, options.activeVersion)}
|
|
39
|
+
<a class="fl-api-action" href="${escapeAttribute(options.openApiUrl)}" download>OpenAPI</a>
|
|
40
|
+
<button class="fl-api-icon-button" type="button" data-theme-toggle aria-label="Switch color theme" title="Switch color theme">
|
|
41
|
+
${themeIcon()}
|
|
42
|
+
</button>
|
|
43
|
+
</div>
|
|
44
|
+
</header>
|
|
45
|
+
<div class="fl-api-layout">
|
|
46
|
+
<aside class="fl-api-sidebar" aria-label="API operations">
|
|
47
|
+
<div class="fl-api-sidebar-heading">
|
|
48
|
+
<span class="fl-api-eyebrow">${escapeHtml(model.specificationVersion)}</span>
|
|
49
|
+
<span>${model.operationCount} ${model.operationCount === 1 ? "operation" : "operations"}</span>
|
|
50
|
+
</div>
|
|
51
|
+
<label class="fl-api-search-label" for="fl-api-search">Search operations</label>
|
|
52
|
+
<div class="fl-api-search-wrap">
|
|
53
|
+
${searchIcon()}
|
|
54
|
+
<input id="fl-api-search" class="fl-api-search" type="search" placeholder="Method, path, tag…" autocomplete="off" />
|
|
55
|
+
<kbd>/</kbd>
|
|
56
|
+
</div>
|
|
57
|
+
<p class="fl-api-search-status" data-search-status aria-live="polite"></p>
|
|
58
|
+
<nav class="fl-api-operation-nav">
|
|
59
|
+
${renderNavigation(groupedOperations)}
|
|
60
|
+
</nav>
|
|
61
|
+
</aside>
|
|
62
|
+
<main id="fl-api-content" class="fl-api-content">
|
|
63
|
+
<section class="fl-api-hero" aria-labelledby="fl-api-title">
|
|
64
|
+
<div class="fl-api-hero-copy">
|
|
65
|
+
<p class="fl-api-eyebrow">${escapeHtml(model.version)} · ${escapeHtml(model.specificationVersion)}</p>
|
|
66
|
+
<h1 id="fl-api-title">${escapeHtml(options.title ?? model.title)}</h1>
|
|
67
|
+
${description ? `<div class="fl-api-description">${renderText(description)}</div>` : ""}
|
|
68
|
+
</div>
|
|
69
|
+
<dl class="fl-api-summary">
|
|
70
|
+
<div><dt>Operations</dt><dd>${model.operationCount}</dd></div>
|
|
71
|
+
<div><dt>Tags</dt><dd>${model.tags.length}</dd></div>
|
|
72
|
+
<div><dt>Servers</dt><dd>${model.servers.length}</dd></div>
|
|
73
|
+
</dl>
|
|
74
|
+
${renderServers(model.servers)}
|
|
75
|
+
</section>
|
|
76
|
+
<section class="fl-api-operations" aria-label="Operations">
|
|
77
|
+
${operationMarkup}
|
|
78
|
+
<div class="fl-api-no-results" data-no-results hidden>
|
|
79
|
+
<strong>No operations match this search.</strong>
|
|
80
|
+
<span>Try a method, route segment, operation ID, or tag.</span>
|
|
81
|
+
</div>
|
|
82
|
+
</section>
|
|
83
|
+
</main>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
<div class="fl-api-live-region" aria-live="polite" aria-atomic="true" data-copy-status></div>
|
|
87
|
+
<script>${buildClientScript()}<\/script>
|
|
88
|
+
</body>
|
|
89
|
+
</html>`;
|
|
90
|
+
}
|
|
91
|
+
function renderVersionSelect(versions, activeVersion) {
|
|
92
|
+
if (versions.length === 0) return "";
|
|
93
|
+
return `<label class="fl-api-version-control">
|
|
94
|
+
<span>Version</span>
|
|
95
|
+
<select data-version-select aria-label="API version">
|
|
96
|
+
${versions.map((version) => `<option value="${escapeAttribute(version.href)}"${version.active || version.id === activeVersion ? " selected" : ""}>${escapeHtml(version.label)}</option>`).join("")}
|
|
97
|
+
</select>
|
|
98
|
+
</label>`;
|
|
99
|
+
}
|
|
100
|
+
function renderNavigation(groups) {
|
|
101
|
+
if (groups.length === 0) return "<p class=\"fl-api-sidebar-empty\">No operations in this contract.</p>";
|
|
102
|
+
return groups.map((group) => `<section class="fl-api-nav-group" data-nav-group>
|
|
103
|
+
<h2>${escapeHtml(group.tag)}</h2>
|
|
104
|
+
<ul>
|
|
105
|
+
${group.operations.map((operation) => `<li data-nav-operation data-search="${escapeAttribute(operationSearchText(operation))}">
|
|
106
|
+
<a href="#operation-${escapeAttribute(operation.slug)}">
|
|
107
|
+
<span class="fl-api-method fl-api-method--${methodTone(operation.method)}">${escapeHtml(operation.method)}</span>
|
|
108
|
+
<span>${escapeHtml(operation.title)}</span>
|
|
109
|
+
</a>
|
|
110
|
+
</li>`).join("")}
|
|
111
|
+
</ul>
|
|
112
|
+
</section>`).join("\n");
|
|
113
|
+
}
|
|
114
|
+
function renderOperation(operation, options) {
|
|
115
|
+
const markdownUrl = `${options.basePath}/operations/${encodeURIComponent(operation.slug)}.md`;
|
|
116
|
+
const curl = buildCurlExample(operation, options.model.servers);
|
|
117
|
+
const consoleMarkup = renderApiConsole(operation, options.model.servers);
|
|
118
|
+
const searchText = operationSearchText(operation);
|
|
119
|
+
const description = operation.description?.trim();
|
|
120
|
+
return `<article id="operation-${escapeAttribute(operation.slug)}" class="fl-api-operation" data-operation data-search="${escapeAttribute(searchText)}">
|
|
121
|
+
<header class="fl-api-operation-header">
|
|
122
|
+
<div class="fl-api-operation-meta">
|
|
123
|
+
<span class="fl-api-method fl-api-method--${methodTone(operation.method)}">${escapeHtml(operation.method)}</span>
|
|
124
|
+
<code>${escapeHtml(operation.path)}</code>
|
|
125
|
+
${operation.deprecated ? "<span class=\"fl-api-chip fl-api-chip--danger\">Deprecated</span>" : ""}
|
|
126
|
+
</div>
|
|
127
|
+
<h2>${escapeHtml(operation.title)}</h2>
|
|
128
|
+
${operation.summary && operation.summary !== operation.title ? `<p class="fl-api-summary-copy">${escapeHtml(operation.summary)}</p>` : ""}
|
|
129
|
+
${description ? `<div class="fl-api-description">${renderText(description)}</div>` : ""}
|
|
130
|
+
<div class="fl-api-operation-links">
|
|
131
|
+
<a href="${escapeAttribute(markdownUrl)}">Markdown</a>
|
|
132
|
+
<a href="#operation-${escapeAttribute(operation.slug)}" aria-label="Link to ${escapeAttribute(operation.title)}">Permalink</a>
|
|
133
|
+
<code>${escapeHtml(operation.operationId)}</code>
|
|
134
|
+
</div>
|
|
135
|
+
</header>
|
|
136
|
+
<div class="fl-api-operation-grid">
|
|
137
|
+
<div class="fl-api-operation-details">
|
|
138
|
+
${renderParameters(operation.parameters)}
|
|
139
|
+
${renderRequestBody(operation.requestBody?.content ?? [], operation.requestBody?.required === true, operation.requestBody?.description)}
|
|
140
|
+
${renderResponses(operation.responses)}
|
|
141
|
+
${renderSecurity(operation)}
|
|
142
|
+
</div>
|
|
143
|
+
<aside class="fl-api-code-panel" aria-label="Request example and interactive console" data-console-root>
|
|
144
|
+
<div class="fl-api-code-header">
|
|
145
|
+
<div class="fl-api-code-tabs" role="tablist" aria-label="Request tools">
|
|
146
|
+
<button id="example-tab-${escapeAttribute(operation.slug)}" type="button" role="tab" aria-selected="true" aria-controls="example-panel-${escapeAttribute(operation.slug)}" tabindex="0" data-console-tab="example">cURL</button>
|
|
147
|
+
<button id="console-tab-${escapeAttribute(operation.slug)}" type="button" role="tab" aria-selected="false" aria-controls="console-panel-${escapeAttribute(operation.slug)}" tabindex="-1" data-console-tab="console">Try it</button>
|
|
148
|
+
</div>
|
|
149
|
+
<button type="button" class="fl-api-copy" data-copy="${escapeAttribute(curl)}" data-example-copy aria-label="Copy cURL request">Copy</button>
|
|
150
|
+
</div>
|
|
151
|
+
<div id="example-panel-${escapeAttribute(operation.slug)}" role="tabpanel" aria-labelledby="example-tab-${escapeAttribute(operation.slug)}" data-console-panel="example">
|
|
152
|
+
<pre><code>${escapeHtml(curl)}</code></pre>
|
|
153
|
+
</div>
|
|
154
|
+
<div id="console-panel-${escapeAttribute(operation.slug)}" role="tabpanel" aria-labelledby="console-tab-${escapeAttribute(operation.slug)}" data-console-panel="console" hidden>
|
|
155
|
+
${consoleMarkup}
|
|
156
|
+
</div>
|
|
157
|
+
</aside>
|
|
158
|
+
</div>
|
|
159
|
+
</article>`;
|
|
160
|
+
}
|
|
161
|
+
function renderApiConsole(operation, modelServers) {
|
|
162
|
+
const server = operation.servers[0] ?? modelServers[0] ?? "";
|
|
163
|
+
const requestBody = operation.requestBody;
|
|
164
|
+
const requestMedia = requestBody?.content[0];
|
|
165
|
+
const bodyExample = requestMedia ? mediaTypeExample(requestMedia) : void 0;
|
|
166
|
+
const mutating = ![
|
|
167
|
+
"GET",
|
|
168
|
+
"HEAD",
|
|
169
|
+
"OPTIONS"
|
|
170
|
+
].includes(operation.method);
|
|
171
|
+
return `<form class="fl-api-console" data-api-console data-method="${escapeAttribute(operation.method)}" data-path="${escapeAttribute(operation.path)}" novalidate>
|
|
172
|
+
<div class="fl-api-console-intro">
|
|
173
|
+
<div>
|
|
174
|
+
<span class="fl-api-console-kicker">Browser request</span>
|
|
175
|
+
<strong>${escapeHtml(operation.method)} ${escapeHtml(operation.path)}</strong>
|
|
176
|
+
</div>
|
|
177
|
+
<span class="fl-api-console-state" data-console-state data-state="idle">Ready</span>
|
|
178
|
+
</div>
|
|
179
|
+
<label class="fl-api-field">
|
|
180
|
+
<span>Server URL or path <em>required</em></span>
|
|
181
|
+
<input name="server" type="text" value="${escapeAttribute(server)}" placeholder="https://api.example.com" required spellcheck="false" autocomplete="url" />
|
|
182
|
+
</label>
|
|
183
|
+
${renderConsoleParameters(operation.parameters)}
|
|
184
|
+
${renderConsoleAuthentication(operation)}
|
|
185
|
+
${requestBody ? renderConsoleRequestBody(requestBody.content, requestBody.required, bodyExample) : ""}
|
|
186
|
+
<label class="fl-api-field fl-api-field--inline">
|
|
187
|
+
<span>Browser credentials</span>
|
|
188
|
+
<select name="credentials" aria-label="Browser credentials mode">
|
|
189
|
+
<option value="same-origin">Same origin</option>
|
|
190
|
+
<option value="include">Include cookies</option>
|
|
191
|
+
<option value="omit">Omit cookies</option>
|
|
192
|
+
</select>
|
|
193
|
+
</label>
|
|
194
|
+
${mutating ? `<label class="fl-api-console-confirm"><input type="checkbox" data-console-confirm /><span>I understand this request may change data.</span></label>` : ""}
|
|
195
|
+
<div class="fl-api-console-actions">
|
|
196
|
+
<button class="fl-api-console-send" type="submit" data-console-send${mutating ? " disabled" : ""}>Send request</button>
|
|
197
|
+
<button class="fl-api-console-cancel" type="button" data-console-cancel hidden>Cancel</button>
|
|
198
|
+
<span data-console-message role="status" aria-live="polite"></span>
|
|
199
|
+
</div>
|
|
200
|
+
<div class="fl-api-console-error" data-console-error role="alert" hidden></div>
|
|
201
|
+
<section class="fl-api-console-response" data-console-response aria-label="API response" tabindex="-1" hidden>
|
|
202
|
+
<div class="fl-api-console-response-header">
|
|
203
|
+
<div>
|
|
204
|
+
<span class="fl-api-console-status" data-response-status></span>
|
|
205
|
+
<span data-response-duration></span>
|
|
206
|
+
</div>
|
|
207
|
+
<button type="button" class="fl-api-copy" data-response-copy aria-label="Copy response body">Copy</button>
|
|
208
|
+
</div>
|
|
209
|
+
<div class="fl-api-console-response-meta">
|
|
210
|
+
<code data-response-url></code>
|
|
211
|
+
<span data-response-type></span>
|
|
212
|
+
</div>
|
|
213
|
+
<details class="fl-api-console-response-headers">
|
|
214
|
+
<summary>Response headers${chevronIcon()}</summary>
|
|
215
|
+
<pre><code data-response-headers></code></pre>
|
|
216
|
+
</details>
|
|
217
|
+
<pre class="fl-api-console-response-body"><code data-response-body></code></pre>
|
|
218
|
+
</section>
|
|
219
|
+
<p class="fl-api-console-note">Requests run in this browser. Cross-origin APIs must allow this docs origin through CORS. Credentials stay in memory and are never saved by Farming Labs.</p>
|
|
220
|
+
</form>`;
|
|
221
|
+
}
|
|
222
|
+
function renderConsoleParameters(parameters) {
|
|
223
|
+
const editable = parameters.filter((parameter) => parameter.in !== "cookie");
|
|
224
|
+
const cookies = parameters.filter((parameter) => parameter.in === "cookie");
|
|
225
|
+
if (editable.length === 0 && cookies.length === 0) return "";
|
|
226
|
+
return `<fieldset class="fl-api-console-group">
|
|
227
|
+
<legend>Parameters</legend>
|
|
228
|
+
${editable.map(renderConsoleParameter).join("")}
|
|
229
|
+
${cookies.length > 0 ? `<p class="fl-api-console-hint">Cookie parameters (${cookies.map((parameter) => `<code>${escapeHtml(parameter.name)}</code>`).join(", ")}) are browser-managed. Choose <strong>Include cookies</strong> below when the API uses them.</p>` : ""}
|
|
230
|
+
</fieldset>`;
|
|
231
|
+
}
|
|
232
|
+
function renderConsoleParameter(parameter) {
|
|
233
|
+
const type = schemaType(asRecord(parameter.schema));
|
|
234
|
+
const value = parameterExample(parameter);
|
|
235
|
+
const common = `name="parameter" data-param-name="${escapeAttribute(parameter.name)}" data-param-location="${escapeAttribute(parameter.in)}" data-param-array="${type === "array" ? "true" : "false"}"${parameter.required ? " required" : ""}`;
|
|
236
|
+
const control = type === "boolean" ? `<select ${common}><option value="">Select…</option><option value="true"${value === true ? " selected" : ""}>true</option><option value="false"${value === false ? " selected" : ""}>false</option></select>` : `<input ${common} type="${type === "number" || type === "integer" ? "number" : "text"}"${value !== void 0 ? ` value="${escapeAttribute(formatInputValue(value))}"` : ""} placeholder="${escapeAttribute(parameterPlaceholder(parameter, type))}" spellcheck="false" autocomplete="off" />`;
|
|
237
|
+
return `<label class="fl-api-field">
|
|
238
|
+
<span><code>${escapeHtml(parameter.name)}</code> <small>${escapeHtml(parameter.in)}</small>${parameter.required ? " <em>required</em>" : ""}</span>
|
|
239
|
+
${control}
|
|
240
|
+
${parameter.description ? `<small class="fl-api-field-help">${escapeHtml(parameter.description)}</small>` : ""}
|
|
241
|
+
</label>`;
|
|
242
|
+
}
|
|
243
|
+
function renderConsoleAuthentication(operation) {
|
|
244
|
+
const schemes = Object.entries(operation.securitySchemes);
|
|
245
|
+
if (operation.security.length === 0 || schemes.length === 0) return "";
|
|
246
|
+
return `<fieldset class="fl-api-console-group">
|
|
247
|
+
<legend>Authentication</legend>
|
|
248
|
+
${schemes.map(([name, scheme]) => renderConsoleSecurityScheme(name, scheme)).join("")}
|
|
249
|
+
<p class="fl-api-console-hint">Authentication values are used only for this request and are not persisted.</p>
|
|
250
|
+
</fieldset>`;
|
|
251
|
+
}
|
|
252
|
+
function renderConsoleSecurityScheme(name, value) {
|
|
253
|
+
const scheme = asRecord(value) ?? {};
|
|
254
|
+
const type = typeof scheme.type === "string" ? scheme.type.toLocaleLowerCase() : "";
|
|
255
|
+
const httpScheme = typeof scheme.scheme === "string" ? scheme.scheme.toLocaleLowerCase() : "";
|
|
256
|
+
const location = typeof scheme.in === "string" ? scheme.in : "header";
|
|
257
|
+
const parameterName = typeof scheme.name === "string" ? scheme.name : name;
|
|
258
|
+
if (type === "http" && httpScheme === "basic") return `<div class="fl-api-auth-pair" data-auth-basic>
|
|
259
|
+
<label class="fl-api-field"><span>${escapeHtml(name)} username</span><input type="text" data-auth-username autocomplete="username" /></label>
|
|
260
|
+
<label class="fl-api-field"><span>${escapeHtml(name)} password</span><input type="password" data-auth-password autocomplete="current-password" /></label>
|
|
261
|
+
</div>`;
|
|
262
|
+
if (type === "apikey") {
|
|
263
|
+
if (location === "cookie") return `<p class="fl-api-console-hint"><strong>${escapeHtml(name)}</strong> uses the browser-managed <code>${escapeHtml(parameterName)}</code> cookie. Choose <strong>Include cookies</strong> below.</p>`;
|
|
264
|
+
return `<label class="fl-api-field">
|
|
265
|
+
<span>${escapeHtml(name)} <small>${escapeHtml(location)} · ${escapeHtml(parameterName)}</small></span>
|
|
266
|
+
<input type="password" data-auth-api-key data-auth-location="${escapeAttribute(location)}" data-auth-name="${escapeAttribute(parameterName)}" autocomplete="off" />
|
|
267
|
+
</label>`;
|
|
268
|
+
}
|
|
269
|
+
const tokenPrefix = type === "http" && httpScheme && httpScheme !== "bearer" ? httpScheme : "Bearer";
|
|
270
|
+
return `<label class="fl-api-field">
|
|
271
|
+
<span>${escapeHtml(name)} token</span>
|
|
272
|
+
<input type="password" data-auth-token data-auth-prefix="${escapeAttribute(tokenPrefix)}" autocomplete="off" placeholder="Paste access token" />
|
|
273
|
+
</label>`;
|
|
274
|
+
}
|
|
275
|
+
function renderConsoleRequestBody(content, required, bodyExample) {
|
|
276
|
+
const mediaTypes = content.length > 0 ? content : [{
|
|
277
|
+
mediaType: "application/json",
|
|
278
|
+
examples: []
|
|
279
|
+
}];
|
|
280
|
+
return `<fieldset class="fl-api-console-group">
|
|
281
|
+
<legend>Request body${required ? " · required" : ""}</legend>
|
|
282
|
+
<label class="fl-api-field fl-api-field--inline">
|
|
283
|
+
<span>Content type</span>
|
|
284
|
+
<select name="content-type">
|
|
285
|
+
${mediaTypes.map((media) => `<option value="${escapeAttribute(media.mediaType)}">${escapeHtml(media.mediaType)}</option>`).join("")}
|
|
286
|
+
</select>
|
|
287
|
+
</label>
|
|
288
|
+
<label class="fl-api-field">
|
|
289
|
+
<span>Body${required ? " <em>required</em>" : ""}</span>
|
|
290
|
+
<textarea name="request-body" rows="8"${required ? " required" : ""} spellcheck="false" placeholder="Request body">${bodyExample === void 0 ? "" : escapeHtml(formatRequestBody(bodyExample, mediaTypes[0]?.mediaType))}</textarea>
|
|
291
|
+
</label>
|
|
292
|
+
</fieldset>`;
|
|
293
|
+
}
|
|
294
|
+
function renderParameters(parameters) {
|
|
295
|
+
if (parameters.length === 0) return "";
|
|
296
|
+
return `<section class="fl-api-section">
|
|
297
|
+
<h3>Parameters <span>${parameters.length}</span></h3>
|
|
298
|
+
<div class="fl-api-table-wrap">
|
|
299
|
+
<table>
|
|
300
|
+
<thead><tr><th scope="col">Name</th><th scope="col">Location</th><th scope="col">Type</th><th scope="col">Description</th></tr></thead>
|
|
301
|
+
<tbody>
|
|
302
|
+
${parameters.map(renderParameter).join("")}
|
|
303
|
+
</tbody>
|
|
304
|
+
</table>
|
|
305
|
+
</div>
|
|
306
|
+
</section>`;
|
|
307
|
+
}
|
|
308
|
+
function renderParameter(parameter) {
|
|
309
|
+
return `<tr>
|
|
310
|
+
<td><code>${escapeHtml(parameter.name)}</code>${parameter.required ? "<span class=\"fl-api-required\">required</span>" : ""}</td>
|
|
311
|
+
<td><span class="fl-api-chip">${escapeHtml(parameter.in)}</span></td>
|
|
312
|
+
<td><code>${escapeHtml(schemaLabel(parameter.schema))}</code></td>
|
|
313
|
+
<td>${parameter.description ? renderText(parameter.description) : "<span class=\"fl-api-muted\">—</span>"}</td>
|
|
314
|
+
</tr>`;
|
|
315
|
+
}
|
|
316
|
+
function renderRequestBody(content, required, description) {
|
|
317
|
+
if (content.length === 0 && !description) return "";
|
|
318
|
+
return `<section class="fl-api-section">
|
|
319
|
+
<h3>Request body ${required ? "<span class=\"fl-api-required\">required</span>" : ""}</h3>
|
|
320
|
+
${description ? `<div class="fl-api-description">${renderText(description)}</div>` : ""}
|
|
321
|
+
${content.map((media) => renderMediaType(media, "Request schema")).join("")}
|
|
322
|
+
</section>`;
|
|
323
|
+
}
|
|
324
|
+
function renderResponses(responses) {
|
|
325
|
+
if (responses.length === 0) return `<section class="fl-api-section"><h3>Responses</h3><p class="fl-api-muted">No responses are documented.</p></section>`;
|
|
326
|
+
return `<section class="fl-api-section">
|
|
327
|
+
<h3>Responses <span>${responses.length}</span></h3>
|
|
328
|
+
<div class="fl-api-response-list">
|
|
329
|
+
${responses.map(renderResponse).join("")}
|
|
330
|
+
</div>
|
|
331
|
+
</section>`;
|
|
332
|
+
}
|
|
333
|
+
function renderResponse(response) {
|
|
334
|
+
return `<details class="fl-api-response"${response.status.startsWith("2") ? " open" : ""}>
|
|
335
|
+
<summary>
|
|
336
|
+
<code>${escapeHtml(response.status)}</code>
|
|
337
|
+
<span>${escapeHtml(response.description ?? "Response")}</span>
|
|
338
|
+
${chevronIcon()}
|
|
339
|
+
</summary>
|
|
340
|
+
<div class="fl-api-response-content">
|
|
341
|
+
${response.content.length > 0 ? response.content.map((media) => renderMediaType(media, "Response schema")).join("") : "<p class=\"fl-api-muted\">No response body.</p>"}
|
|
342
|
+
</div>
|
|
343
|
+
</details>`;
|
|
344
|
+
}
|
|
345
|
+
function renderMediaType(media, label) {
|
|
346
|
+
const schema = media.schema === void 0 ? void 0 : safeJson(media.schema);
|
|
347
|
+
const example = media.examples.find((entry) => entry.value !== void 0);
|
|
348
|
+
return `<div class="fl-api-media">
|
|
349
|
+
<div class="fl-api-media-heading"><span class="fl-api-chip">${escapeHtml(media.mediaType)}</span></div>
|
|
350
|
+
${schema ? renderCodeDisclosure(label, schema) : ""}
|
|
351
|
+
${example?.value !== void 0 ? renderCodeDisclosure(example.summary ?? "Example", safeJson(example.value)) : ""}
|
|
352
|
+
</div>`;
|
|
353
|
+
}
|
|
354
|
+
function renderCodeDisclosure(label, value) {
|
|
355
|
+
return `<details class="fl-api-schema">
|
|
356
|
+
<summary>${escapeHtml(label)}${chevronIcon()}</summary>
|
|
357
|
+
<div class="fl-api-schema-code">
|
|
358
|
+
<button type="button" class="fl-api-copy" data-copy="${escapeAttribute(value)}" aria-label="Copy ${escapeAttribute(label)}">Copy</button>
|
|
359
|
+
<pre><code>${escapeHtml(value)}</code></pre>
|
|
360
|
+
</div>
|
|
361
|
+
</details>`;
|
|
362
|
+
}
|
|
363
|
+
function renderSecurity(operation) {
|
|
364
|
+
if (operation.security.length === 0) return "";
|
|
365
|
+
return `<section class="fl-api-section">
|
|
366
|
+
<h3>Authentication</h3>
|
|
367
|
+
<div class="fl-api-chip-row">${Array.from(new Set(operation.security.flatMap((requirement) => Object.keys(requirement)))).map((name) => `<span class="fl-api-chip">${lockIcon()}${escapeHtml(name)}</span>`).join("")}</div>
|
|
368
|
+
</section>`;
|
|
369
|
+
}
|
|
370
|
+
function renderServers(servers) {
|
|
371
|
+
if (servers.length === 0) return "";
|
|
372
|
+
return `<div class="fl-api-servers">
|
|
373
|
+
<span>Server</span>
|
|
374
|
+
<code>${escapeHtml(servers[0] ?? "")}</code>
|
|
375
|
+
<button type="button" class="fl-api-copy" data-copy="${escapeAttribute(servers[0] ?? "")}" aria-label="Copy server URL">Copy</button>
|
|
376
|
+
</div>`;
|
|
377
|
+
}
|
|
378
|
+
function renderEmptyContract() {
|
|
379
|
+
return `<div class="fl-api-empty">
|
|
380
|
+
<strong>No operations found.</strong>
|
|
381
|
+
<span>Add paths and operations to the OpenAPI contract, then refresh this reference.</span>
|
|
382
|
+
</div>`;
|
|
383
|
+
}
|
|
384
|
+
function groupOperations(operations) {
|
|
385
|
+
const groups = /* @__PURE__ */ new Map();
|
|
386
|
+
for (const operation of operations) {
|
|
387
|
+
const tag = operation.tags[0] ?? "General";
|
|
388
|
+
const group = groups.get(tag) ?? [];
|
|
389
|
+
group.push(operation);
|
|
390
|
+
groups.set(tag, group);
|
|
391
|
+
}
|
|
392
|
+
return Array.from(groups.entries()).sort(([left], [right]) => left.localeCompare(right)).map(([tag, grouped]) => ({
|
|
393
|
+
tag,
|
|
394
|
+
operations: grouped.sort((left, right) => left.title.localeCompare(right.title))
|
|
395
|
+
}));
|
|
396
|
+
}
|
|
397
|
+
function buildCurlExample(operation, modelServers) {
|
|
398
|
+
const target = shellQuote(`${(operation.servers[0] ?? modelServers[0] ?? "https://api.example.com").replace(/\/$/, "")}${operation.path}`);
|
|
399
|
+
const parts = [`curl --request ${operation.method}`, ` --url ${target}`];
|
|
400
|
+
const contentType = operation.requestBody?.content[0]?.mediaType;
|
|
401
|
+
if (contentType) parts.push(` --header ${shellQuote(`Content-Type: ${contentType}`)}`);
|
|
402
|
+
const example = operation.requestBody?.content.flatMap((entry) => entry.examples).find((entry) => entry.value !== void 0)?.value;
|
|
403
|
+
if (operation.requestBody) parts.push(` --data ${shellQuote(example === void 0 ? "{}" : safeJson(example))}`);
|
|
404
|
+
return parts.join(" \\\n");
|
|
405
|
+
}
|
|
406
|
+
function mediaTypeExample(media) {
|
|
407
|
+
const example = media.examples.find((entry) => entry.value !== void 0)?.value;
|
|
408
|
+
return example === void 0 ? schemaExample(media.schema) : example;
|
|
409
|
+
}
|
|
410
|
+
function parameterExample(parameter) {
|
|
411
|
+
const example = parameter.examples.find((entry) => entry.value !== void 0)?.value;
|
|
412
|
+
if (example !== void 0) return example;
|
|
413
|
+
const schema = asRecord(parameter.schema);
|
|
414
|
+
if (!schema) return void 0;
|
|
415
|
+
if (Object.hasOwn(schema, "example")) return schema.example;
|
|
416
|
+
if (Object.hasOwn(schema, "default")) return schema.default;
|
|
417
|
+
if (Object.hasOwn(schema, "const")) return schema.const;
|
|
418
|
+
return Array.isArray(schema.enum) ? schema.enum[0] : void 0;
|
|
419
|
+
}
|
|
420
|
+
function schemaExample(schema, depth = 0) {
|
|
421
|
+
if (depth > 5) return void 0;
|
|
422
|
+
const record = asRecord(schema);
|
|
423
|
+
if (!record) return void 0;
|
|
424
|
+
if (Object.hasOwn(record, "example")) return record.example;
|
|
425
|
+
if (Object.hasOwn(record, "default")) return record.default;
|
|
426
|
+
if (Object.hasOwn(record, "const")) return record.const;
|
|
427
|
+
if (Array.isArray(record.enum) && record.enum.length > 0) return record.enum[0];
|
|
428
|
+
const composed = [
|
|
429
|
+
record.oneOf,
|
|
430
|
+
record.anyOf,
|
|
431
|
+
record.allOf
|
|
432
|
+
].find((value) => Array.isArray(value) && value.length > 0);
|
|
433
|
+
if (Array.isArray(composed)) return schemaExample(composed[0], depth + 1);
|
|
434
|
+
const type = schemaType(record);
|
|
435
|
+
if (type === "array") {
|
|
436
|
+
const item = schemaExample(record.items, depth + 1);
|
|
437
|
+
return item === void 0 ? [] : [item];
|
|
438
|
+
}
|
|
439
|
+
if (type === "object" || asRecord(record.properties)) {
|
|
440
|
+
const properties = asRecord(record.properties) ?? {};
|
|
441
|
+
return Object.fromEntries(Object.entries(properties).map(([name, value]) => [name, schemaExample(value, depth + 1) ?? placeholderSchemaValue(value)]));
|
|
442
|
+
}
|
|
443
|
+
return placeholderSchemaValue(record);
|
|
444
|
+
}
|
|
445
|
+
function placeholderSchemaValue(schema) {
|
|
446
|
+
const type = schemaType(asRecord(schema));
|
|
447
|
+
if (type === "string") return "string";
|
|
448
|
+
if (type === "number" || type === "integer") return 0;
|
|
449
|
+
if (type === "boolean") return false;
|
|
450
|
+
if (type === "array") return [];
|
|
451
|
+
if (type === "object") return {};
|
|
452
|
+
}
|
|
453
|
+
function schemaType(schema) {
|
|
454
|
+
if (!schema) return "string";
|
|
455
|
+
if (typeof schema.type === "string") return schema.type;
|
|
456
|
+
if (Array.isArray(schema.type)) return schema.type.find((entry) => typeof entry === "string" && entry !== "null") ?? "string";
|
|
457
|
+
if (schema.items !== void 0) return "array";
|
|
458
|
+
if (schema.properties !== void 0 || schema.additionalProperties !== void 0) return "object";
|
|
459
|
+
return "string";
|
|
460
|
+
}
|
|
461
|
+
function parameterPlaceholder(parameter, type) {
|
|
462
|
+
if (type === "array") return "Comma-separated values";
|
|
463
|
+
if (parameter.in === "path") return parameter.name;
|
|
464
|
+
return `Optional ${type}`;
|
|
465
|
+
}
|
|
466
|
+
function formatInputValue(value) {
|
|
467
|
+
if (Array.isArray(value)) return value.map((entry) => String(entry)).join(", ");
|
|
468
|
+
if (value && typeof value === "object") return JSON.stringify(value);
|
|
469
|
+
return String(value);
|
|
470
|
+
}
|
|
471
|
+
function formatRequestBody(value, mediaType) {
|
|
472
|
+
if (typeof value === "string" && !isJsonMediaType(mediaType)) return value;
|
|
473
|
+
return safeJson(value);
|
|
474
|
+
}
|
|
475
|
+
function isJsonMediaType(mediaType) {
|
|
476
|
+
const normalized = mediaType?.toLocaleLowerCase() ?? "";
|
|
477
|
+
return normalized.includes("json") || normalized.includes("+json");
|
|
478
|
+
}
|
|
479
|
+
function asRecord(value) {
|
|
480
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
481
|
+
}
|
|
482
|
+
function operationSearchText(operation) {
|
|
483
|
+
return [
|
|
484
|
+
operation.method,
|
|
485
|
+
operation.path,
|
|
486
|
+
operation.title,
|
|
487
|
+
operation.summary,
|
|
488
|
+
operation.description,
|
|
489
|
+
operation.operationId,
|
|
490
|
+
...operation.tags
|
|
491
|
+
].filter(Boolean).join(" ").toLocaleLowerCase();
|
|
492
|
+
}
|
|
493
|
+
function schemaLabel(schema) {
|
|
494
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema)) return "any";
|
|
495
|
+
const record = schema;
|
|
496
|
+
if (typeof record.type === "string") {
|
|
497
|
+
if (record.type === "array") return `${schemaLabel(record.items)}[]`;
|
|
498
|
+
return record.type;
|
|
499
|
+
}
|
|
500
|
+
if (Array.isArray(record.type)) return record.type.join(" | ");
|
|
501
|
+
if (Array.isArray(record.enum)) return "enum";
|
|
502
|
+
return "object";
|
|
503
|
+
}
|
|
504
|
+
function safeJson(value) {
|
|
505
|
+
try {
|
|
506
|
+
return JSON.stringify(value, null, 2) ?? "null";
|
|
507
|
+
} catch {
|
|
508
|
+
return "[unserializable value]";
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
function shellQuote(value) {
|
|
512
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
513
|
+
}
|
|
514
|
+
function renderText(value) {
|
|
515
|
+
return value.split(/\n{2,}/).map((paragraph) => `<p>${escapeHtml(paragraph).replaceAll("\n", "<br />")}</p>`).join("");
|
|
516
|
+
}
|
|
517
|
+
function methodTone(method) {
|
|
518
|
+
return METHOD_TONES[method] ?? "neutral";
|
|
519
|
+
}
|
|
520
|
+
function escapeHtml(value) {
|
|
521
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'");
|
|
522
|
+
}
|
|
523
|
+
function escapeAttribute(value) {
|
|
524
|
+
return escapeHtml(value).replaceAll("`", "`");
|
|
525
|
+
}
|
|
526
|
+
function resolveTokens(theme) {
|
|
527
|
+
const colors = theme?.ui?.colors;
|
|
528
|
+
const typography = theme?.ui?.typography?.font?.style;
|
|
529
|
+
const background = colors?.background ?? "#ffffff";
|
|
530
|
+
const foreground = colors?.foreground ?? (looksDark(background, theme?.name) ? "#f5f5f4" : "#18181b");
|
|
531
|
+
const primary = colors?.primary ?? "#4f46e5";
|
|
532
|
+
return {
|
|
533
|
+
primary,
|
|
534
|
+
primaryForeground: colors?.primaryForeground ?? (looksDark(primary) ? "#ffffff" : "#111113"),
|
|
535
|
+
background,
|
|
536
|
+
card: colors?.card ?? background,
|
|
537
|
+
foreground,
|
|
538
|
+
muted: colors?.muted ?? (looksDark(background, theme?.name) ? "#a1a1aa" : "#62626d"),
|
|
539
|
+
border: colors?.border ?? (looksDark(background, theme?.name) ? "#2d2d33" : "#dedee3"),
|
|
540
|
+
radius: theme?.ui?.radius ?? "8px",
|
|
541
|
+
sans: typography?.sans ?? "\"Geist\", \"Inter\", ui-sans-serif, system-ui, sans-serif",
|
|
542
|
+
mono: typography?.mono ?? "\"Geist Mono\", \"SFMono-Regular\", Menlo, monospace",
|
|
543
|
+
customPalette: Boolean(colors)
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
function looksDark(color, name) {
|
|
547
|
+
if (name?.toLocaleLowerCase().includes("dark")) return true;
|
|
548
|
+
const match = /^#([\da-f]{6})$/i.exec(color.trim());
|
|
549
|
+
if (!match?.[1]) return false;
|
|
550
|
+
const red = Number.parseInt(match[1].slice(0, 2), 16);
|
|
551
|
+
const green = Number.parseInt(match[1].slice(2, 4), 16);
|
|
552
|
+
const blue = Number.parseInt(match[1].slice(4, 6), 16);
|
|
553
|
+
return (red * 299 + green * 587 + blue * 114) / 1e3 < 128;
|
|
554
|
+
}
|
|
555
|
+
function buildStyles(tokens) {
|
|
556
|
+
const automaticDark = tokens.customPalette ? "" : `@media (prefers-color-scheme: dark) {
|
|
557
|
+
:root:not([data-theme]) { --fl-bg: #0b0b0d; --fl-card: #111114; --fl-fg: #f4f4f5; --fl-muted: #a1a1aa; --fl-border: #2b2b31; --fl-soft: #18181d; }
|
|
558
|
+
}`;
|
|
559
|
+
return `
|
|
560
|
+
:root { color-scheme: light; --fl-primary: ${tokens.primary}; --fl-primary-fg: ${tokens.primaryForeground}; --fl-bg: ${tokens.background}; --fl-card: ${tokens.card}; --fl-fg: ${tokens.foreground}; --fl-muted: ${tokens.muted}; --fl-border: ${tokens.border}; --fl-soft: color-mix(in srgb, var(--fl-card) 92%, var(--fl-fg) 8%); --fl-radius: ${tokens.radius}; --fl-sans: ${tokens.sans}; --fl-mono: ${tokens.mono}; }
|
|
561
|
+
:root[data-theme="light"] { color-scheme: light; }
|
|
562
|
+
:root[data-theme="dark"] { color-scheme: dark; --fl-bg: #0b0b0d; --fl-card: #111114; --fl-fg: #f4f4f5; --fl-muted: #a1a1aa; --fl-border: #2b2b31; --fl-soft: #18181d; }
|
|
563
|
+
${automaticDark}
|
|
564
|
+
* { box-sizing: border-box; }
|
|
565
|
+
html { scroll-behavior: smooth; scroll-padding-top: 88px; }
|
|
566
|
+
body { margin: 0; background: var(--fl-bg); color: var(--fl-fg); font: 15px/1.6 var(--fl-sans); }
|
|
567
|
+
button, input, select { font: inherit; }
|
|
568
|
+
button, a, input, select { -webkit-tap-highlight-color: transparent; }
|
|
569
|
+
a { color: inherit; }
|
|
570
|
+
code, pre, kbd { font-family: var(--fl-mono); }
|
|
571
|
+
.fl-skip-link { position: fixed; z-index: 100; top: 8px; left: 8px; transform: translateY(-150%); background: var(--fl-fg); color: var(--fl-bg); padding: 8px 12px; }
|
|
572
|
+
.fl-skip-link:focus { transform: translateY(0); }
|
|
573
|
+
.fl-api-shell { min-height: 100vh; }
|
|
574
|
+
.fl-api-topbar { position: sticky; z-index: 20; top: 0; min-height: 58px; display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 8px 20px; border-bottom: 1px solid var(--fl-border); background: color-mix(in srgb, var(--fl-bg) 94%, transparent); backdrop-filter: blur(12px); }
|
|
575
|
+
.fl-api-brand { display: inline-flex; align-items: center; gap: 10px; text-decoration: none; font-weight: 650; }
|
|
576
|
+
.fl-api-brand-mark { display: grid; place-items: center; width: 28px; height: 28px; border: 1px solid var(--fl-fg); font: 700 10px/1 var(--fl-mono); letter-spacing: -.04em; }
|
|
577
|
+
.fl-api-topbar-actions, .fl-api-version-control { display: flex; align-items: center; gap: 8px; }
|
|
578
|
+
.fl-api-version-control > span { color: var(--fl-muted); font: 11px/1 var(--fl-mono); text-transform: uppercase; letter-spacing: .08em; }
|
|
579
|
+
.fl-api-version-control select, .fl-api-action, .fl-api-icon-button { min-height: 34px; border: 1px solid var(--fl-border); border-radius: calc(var(--fl-radius) * .65); background: var(--fl-card); color: var(--fl-fg); }
|
|
580
|
+
.fl-api-version-control select { padding: 0 28px 0 10px; }
|
|
581
|
+
.fl-api-action { display: inline-flex; align-items: center; padding: 0 11px; text-decoration: none; font: 12px/1 var(--fl-mono); }
|
|
582
|
+
.fl-api-icon-button { width: 34px; display: grid; place-items: center; cursor: pointer; }
|
|
583
|
+
.fl-api-icon-button svg, .fl-api-search-wrap svg, .fl-api-chip svg { width: 15px; height: 15px; }
|
|
584
|
+
.fl-api-layout { display: grid; grid-template-columns: minmax(240px, 292px) minmax(0, 1fr); }
|
|
585
|
+
.fl-api-sidebar { position: sticky; top: 59px; height: calc(100vh - 59px); overflow: auto; padding: 20px 16px 32px; border-right: 1px solid var(--fl-border); }
|
|
586
|
+
.fl-api-sidebar-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 18px; color: var(--fl-muted); font-size: 12px; }
|
|
587
|
+
.fl-api-eyebrow { color: var(--fl-muted); font: 11px/1.3 var(--fl-mono); letter-spacing: .08em; text-transform: uppercase; }
|
|
588
|
+
.fl-api-search-label { display: block; margin-bottom: 7px; color: var(--fl-muted); font-size: 12px; }
|
|
589
|
+
.fl-api-search-wrap { display: grid; grid-template-columns: 18px 1fr auto; align-items: center; gap: 7px; height: 38px; padding: 0 10px; border: 1px solid var(--fl-border); border-radius: var(--fl-radius); background: var(--fl-card); }
|
|
590
|
+
.fl-api-search { width: 100%; border: 0; outline: 0; background: transparent; color: var(--fl-fg); }
|
|
591
|
+
.fl-api-search::placeholder { color: var(--fl-muted); }
|
|
592
|
+
.fl-api-search-wrap:focus-within { border-color: var(--fl-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--fl-primary) 18%, transparent); }
|
|
593
|
+
.fl-api-search-wrap kbd { padding: 2px 5px; border: 1px solid var(--fl-border); color: var(--fl-muted); font-size: 10px; }
|
|
594
|
+
.fl-api-search-status { min-height: 18px; margin: 7px 0 0; color: var(--fl-muted); font-size: 11px; }
|
|
595
|
+
.fl-api-nav-group { margin-top: 22px; }
|
|
596
|
+
.fl-api-nav-group[hidden] { display: none; }
|
|
597
|
+
.fl-api-nav-group h2 { margin: 0 8px 8px; color: var(--fl-muted); font: 600 11px/1.3 var(--fl-mono); letter-spacing: .08em; text-transform: uppercase; }
|
|
598
|
+
.fl-api-nav-group ul { display: grid; gap: 2px; margin: 0; padding: 0; list-style: none; }
|
|
599
|
+
.fl-api-nav-group a { display: grid; grid-template-columns: 46px minmax(0, 1fr); align-items: center; gap: 8px; min-height: 36px; padding: 6px 8px; border-left: 2px solid transparent; text-decoration: none; font-size: 13px; }
|
|
600
|
+
.fl-api-nav-group a:hover, .fl-api-nav-group a:focus-visible { border-left-color: var(--fl-primary); background: var(--fl-soft); outline: none; }
|
|
601
|
+
.fl-api-nav-group a span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
602
|
+
.fl-api-method { display: inline-flex; align-items: center; justify-content: center; min-width: 46px; height: 22px; padding: 0 6px; border: 1px solid currentColor; border-radius: calc(var(--fl-radius) * .5); font: 700 10px/1 var(--fl-mono); letter-spacing: .04em; }
|
|
603
|
+
.fl-api-method--success { color: #16803d; }.fl-api-method--info { color: #2563eb; }.fl-api-method--warning { color: #b45309; }.fl-api-method--danger { color: #dc2626; }.fl-api-method--neutral { color: var(--fl-muted); }
|
|
604
|
+
.fl-api-content { min-width: 0; }
|
|
605
|
+
.fl-api-hero { padding: clamp(32px, 6vw, 76px) clamp(22px, 6vw, 82px) 44px; border-bottom: 1px solid var(--fl-border); }
|
|
606
|
+
.fl-api-hero-copy { max-width: 780px; }
|
|
607
|
+
.fl-api-hero h1 { max-width: 18ch; margin: 10px 0 12px; font-size: clamp(34px, 5vw, 64px); line-height: 1.02; letter-spacing: -.045em; }
|
|
608
|
+
.fl-api-description { max-width: 76ch; color: var(--fl-muted); }
|
|
609
|
+
.fl-api-description p { margin: 0 0 8px; }
|
|
610
|
+
.fl-api-summary { display: flex; flex-wrap: wrap; gap: 1px; margin: 30px 0 0; }
|
|
611
|
+
.fl-api-summary div { min-width: 120px; padding: 12px 16px; border: 1px solid var(--fl-border); background: var(--fl-card); }
|
|
612
|
+
.fl-api-summary dt { color: var(--fl-muted); font: 10px/1.3 var(--fl-mono); text-transform: uppercase; letter-spacing: .08em; }
|
|
613
|
+
.fl-api-summary dd { margin: 4px 0 0; font: 650 20px/1.2 var(--fl-mono); }
|
|
614
|
+
.fl-api-servers { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; max-width: 780px; margin-top: 18px; border: 1px solid var(--fl-border); background: var(--fl-soft); }
|
|
615
|
+
.fl-api-servers > span { padding: 9px 12px; border-right: 1px solid var(--fl-border); color: var(--fl-muted); font: 10px/1 var(--fl-mono); text-transform: uppercase; }
|
|
616
|
+
.fl-api-servers code { overflow: auto; padding: 9px 12px; font-size: 12px; white-space: nowrap; }
|
|
617
|
+
.fl-api-copy { min-width: 54px; align-self: stretch; padding: 7px 10px; border: 0; border-left: 1px solid var(--fl-border); background: transparent; color: var(--fl-muted); cursor: pointer; font: 11px/1 var(--fl-mono); }
|
|
618
|
+
.fl-api-copy:hover, .fl-api-copy:focus-visible { background: var(--fl-card); color: var(--fl-fg); outline: 2px solid var(--fl-primary); outline-offset: -2px; }
|
|
619
|
+
.fl-api-operations { padding: 0 clamp(22px, 5vw, 72px) 80px; }
|
|
620
|
+
.fl-api-operation { padding: clamp(36px, 6vw, 72px) 0; border-bottom: 1px solid var(--fl-border); scroll-margin-top: 76px; }
|
|
621
|
+
.fl-api-operation[hidden] { display: none; }
|
|
622
|
+
.fl-api-operation-header { max-width: 900px; }
|
|
623
|
+
.fl-api-operation-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 9px; }
|
|
624
|
+
.fl-api-operation-meta > code { overflow-wrap: anywhere; color: var(--fl-fg); font-size: 13px; }
|
|
625
|
+
.fl-api-operation h2 { margin: 15px 0 7px; font-size: clamp(25px, 3vw, 38px); line-height: 1.15; letter-spacing: -.025em; }
|
|
626
|
+
.fl-api-summary-copy { margin: 0 0 8px; font-weight: 520; }
|
|
627
|
+
.fl-api-operation-links { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; margin-top: 18px; font: 11px/1.4 var(--fl-mono); }
|
|
628
|
+
.fl-api-operation-links a { text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 3px; }
|
|
629
|
+
.fl-api-operation-links code { color: var(--fl-muted); }
|
|
630
|
+
.fl-api-operation-grid { display: grid; grid-template-columns: minmax(0, 1.12fr) minmax(300px, .88fr); gap: clamp(24px, 5vw, 60px); margin-top: 30px; align-items: start; }
|
|
631
|
+
.fl-api-operation-details { min-width: 0; }
|
|
632
|
+
.fl-api-section { padding: 24px 0; border-top: 1px solid var(--fl-border); }
|
|
633
|
+
.fl-api-section h3 { display: flex; align-items: center; gap: 8px; margin: 0 0 14px; font-size: 15px; }
|
|
634
|
+
.fl-api-section h3 > span { color: var(--fl-muted); font: 11px/1 var(--fl-mono); }
|
|
635
|
+
.fl-api-table-wrap { overflow-x: auto; border: 1px solid var(--fl-border); }
|
|
636
|
+
table { width: 100%; min-width: 620px; border-collapse: collapse; font-size: 13px; }
|
|
637
|
+
th, td { padding: 11px 12px; border-bottom: 1px solid var(--fl-border); text-align: left; vertical-align: top; }
|
|
638
|
+
th { background: var(--fl-soft); color: var(--fl-muted); font: 10px/1.3 var(--fl-mono); text-transform: uppercase; letter-spacing: .06em; }
|
|
639
|
+
tbody tr:last-child td { border-bottom: 0; }
|
|
640
|
+
td code { font-size: 12px; }
|
|
641
|
+
.fl-api-required { display: inline-block; margin-left: 7px; color: #dc2626; font: 9px/1 var(--fl-mono); text-transform: uppercase; }
|
|
642
|
+
.fl-api-chip-row { display: flex; flex-wrap: wrap; gap: 7px; }
|
|
643
|
+
.fl-api-chip { display: inline-flex; align-items: center; gap: 5px; padding: 4px 7px; border: 1px solid var(--fl-border); border-radius: calc(var(--fl-radius) * .5); background: var(--fl-soft); color: var(--fl-muted); font: 10px/1 var(--fl-mono); }
|
|
644
|
+
.fl-api-chip--danger { color: #dc2626; }
|
|
645
|
+
.fl-api-code-panel { position: sticky; top: 84px; overflow: hidden; border: 1px solid var(--fl-border); border-radius: var(--fl-radius); background: #0c0c0f; color: #f4f4f5; }
|
|
646
|
+
.fl-api-code-header { min-height: 38px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #29292f; color: #a1a1aa; font: 10px/1 var(--fl-mono); text-transform: uppercase; letter-spacing: .08em; }
|
|
647
|
+
.fl-api-code-tabs { align-self: stretch; display: flex; align-items: stretch; }
|
|
648
|
+
.fl-api-code-tabs button { position: relative; min-width: 70px; padding: 0 12px; border: 0; border-right: 1px solid #29292f; background: transparent; color: #a1a1aa; cursor: pointer; font: inherit; letter-spacing: inherit; text-transform: inherit; }
|
|
649
|
+
.fl-api-code-tabs button[aria-selected="true"] { background: #151519; color: #f4f4f5; }
|
|
650
|
+
.fl-api-code-tabs button[aria-selected="true"]::after { content: ""; position: absolute; right: 10px; bottom: -1px; left: 10px; height: 2px; background: var(--fl-primary); }
|
|
651
|
+
.fl-api-code-tabs button:hover, .fl-api-code-tabs button:focus-visible { color: #f4f4f5; outline: 2px solid var(--fl-primary); outline-offset: -2px; }
|
|
652
|
+
.fl-api-code-panel .fl-api-copy { color: #a1a1aa; border-left-color: #29292f; }
|
|
653
|
+
.fl-api-code-panel pre, .fl-api-schema pre { margin: 0; overflow: auto; padding: 16px; font-size: 12px; line-height: 1.7; }
|
|
654
|
+
.fl-api-console { display: grid; gap: 16px; max-height: min(76vh, 780px); overflow-y: auto; padding: 16px; background: #0c0c0f; color: #f4f4f5; }
|
|
655
|
+
.fl-api-console-intro, .fl-api-console-response-header, .fl-api-console-actions { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
|
656
|
+
.fl-api-console-intro > div { min-width: 0; display: grid; gap: 5px; }
|
|
657
|
+
.fl-api-console-intro strong { overflow-wrap: anywhere; font: 600 12px/1.45 var(--fl-mono); }
|
|
658
|
+
.fl-api-console-kicker, .fl-api-console-group legend { color: #a1a1aa; font: 10px/1.3 var(--fl-mono); text-transform: uppercase; letter-spacing: .08em; }
|
|
659
|
+
.fl-api-console-state { flex: 0 0 auto; padding: 4px 7px; border: 1px solid #3f3f46; color: #a1a1aa; font: 10px/1 var(--fl-mono); }
|
|
660
|
+
.fl-api-console-state[data-state="sending"] { border-color: #6366f1; color: #a5b4fc; }
|
|
661
|
+
.fl-api-console-state[data-state="success"] { border-color: #22c55e; color: #86efac; }
|
|
662
|
+
.fl-api-console-state[data-state="error"] { border-color: #ef4444; color: #fca5a5; }
|
|
663
|
+
.fl-api-console-group { display: grid; gap: 12px; min-width: 0; margin: 0; padding: 14px; border: 1px solid #29292f; }
|
|
664
|
+
.fl-api-console-group legend { padding: 0 6px; }
|
|
665
|
+
.fl-api-field { display: grid; gap: 6px; min-width: 0; color: #d4d4d8; font: 11px/1.35 var(--fl-mono); }
|
|
666
|
+
.fl-api-field > span { display: flex; flex-wrap: wrap; align-items: baseline; gap: 5px; }
|
|
667
|
+
.fl-api-field > span small { color: #71717a; font-size: 9px; text-transform: uppercase; }
|
|
668
|
+
.fl-api-field em { color: #fca5a5; font-size: 9px; font-style: normal; text-transform: uppercase; }
|
|
669
|
+
.fl-api-field input, .fl-api-field select, .fl-api-field textarea { width: 100%; min-width: 0; border: 1px solid #3f3f46; border-radius: calc(var(--fl-radius) * .55); background: #151519; color: #f4f4f5; font: 12px/1.45 var(--fl-mono); }
|
|
670
|
+
.fl-api-field input, .fl-api-field select { height: 36px; padding: 0 10px; }
|
|
671
|
+
.fl-api-field textarea { resize: vertical; min-height: 112px; padding: 10px; tab-size: 2; }
|
|
672
|
+
.fl-api-field input::placeholder, .fl-api-field textarea::placeholder { color: #71717a; }
|
|
673
|
+
.fl-api-field input:focus, .fl-api-field select:focus, .fl-api-field textarea:focus { border-color: #818cf8; outline: 2px solid color-mix(in srgb, #6366f1 45%, transparent); outline-offset: 0; }
|
|
674
|
+
.fl-api-field--inline { grid-template-columns: minmax(120px, .7fr) minmax(0, 1fr); align-items: center; }
|
|
675
|
+
.fl-api-field-help, .fl-api-console-hint, .fl-api-console-note { color: #a1a1aa; font-size: 10px; line-height: 1.5; }
|
|
676
|
+
.fl-api-console-hint, .fl-api-console-note { margin: 0; }
|
|
677
|
+
.fl-api-console-hint code { color: #d4d4d8; }
|
|
678
|
+
.fl-api-auth-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
|
|
679
|
+
.fl-api-console-confirm { display: flex; align-items: flex-start; gap: 8px; color: #fcd34d; font: 10px/1.45 var(--fl-mono); }
|
|
680
|
+
.fl-api-console-confirm input { margin: 1px 0 0; accent-color: #6366f1; }
|
|
681
|
+
.fl-api-console-actions { justify-content: flex-start; min-height: 36px; }
|
|
682
|
+
.fl-api-console-actions button { min-height: 34px; padding: 0 12px; border: 1px solid #3f3f46; border-radius: calc(var(--fl-radius) * .55); cursor: pointer; font: 600 11px/1 var(--fl-mono); }
|
|
683
|
+
.fl-api-console-send { background: var(--fl-primary); color: var(--fl-primary-fg); border-color: var(--fl-primary) !important; }
|
|
684
|
+
.fl-api-console-send:hover:not(:disabled), .fl-api-console-send:focus-visible { filter: brightness(1.08); outline: 2px solid #a5b4fc; outline-offset: 2px; }
|
|
685
|
+
.fl-api-console-send:disabled { cursor: not-allowed; opacity: .45; }
|
|
686
|
+
.fl-api-console-cancel { background: transparent; color: #d4d4d8; }
|
|
687
|
+
.fl-api-console-actions > span { color: #a1a1aa; font: 10px/1.4 var(--fl-mono); }
|
|
688
|
+
.fl-api-console-error { padding: 10px 12px; border: 1px solid #7f1d1d; background: #2a1013; color: #fecaca; font: 11px/1.5 var(--fl-mono); }
|
|
689
|
+
.fl-api-console-response { min-width: 0; border: 1px solid #3f3f46; outline: none; }
|
|
690
|
+
.fl-api-console-response:focus-visible { border-color: #818cf8; box-shadow: 0 0 0 2px #6366f1; }
|
|
691
|
+
.fl-api-console-response-header { min-height: 38px; border-bottom: 1px solid #29292f; }
|
|
692
|
+
.fl-api-console-response-header > div { display: flex; align-items: center; gap: 8px; padding-left: 11px; color: #a1a1aa; font: 10px/1 var(--fl-mono); }
|
|
693
|
+
.fl-api-console-status { color: #86efac; font-weight: 700; }
|
|
694
|
+
.fl-api-console-status[data-ok="false"] { color: #fca5a5; }
|
|
695
|
+
.fl-api-console-response-meta { display: grid; gap: 4px; padding: 9px 11px; border-bottom: 1px solid #29292f; color: #a1a1aa; font: 9px/1.4 var(--fl-mono); }
|
|
696
|
+
.fl-api-console-response-meta code { overflow: hidden; color: #d4d4d8; text-overflow: ellipsis; white-space: nowrap; }
|
|
697
|
+
.fl-api-console-response-headers { border-bottom: 1px solid #29292f; }
|
|
698
|
+
.fl-api-console-response-headers summary { display: flex; align-items: center; justify-content: space-between; padding: 9px 11px; color: #a1a1aa; cursor: pointer; font: 10px/1.3 var(--fl-mono); list-style: none; }
|
|
699
|
+
.fl-api-console-response-headers summary::-webkit-details-marker { display: none; }
|
|
700
|
+
.fl-api-console-response-headers summary svg { width: 14px; transition: transform .15s ease; }
|
|
701
|
+
.fl-api-console-response-headers[open] summary svg { transform: rotate(180deg); }
|
|
702
|
+
.fl-api-console-response-headers pre { max-height: 180px; padding: 11px; border-top: 1px solid #29292f; color: #d4d4d8; }
|
|
703
|
+
.fl-api-console-response-body { max-height: 340px; min-height: 72px; color: #f4f4f5; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
704
|
+
.fl-api-console-note { padding-top: 2px; border-top: 1px solid #29292f; }
|
|
705
|
+
.fl-api-response-list { display: grid; border: 1px solid var(--fl-border); }
|
|
706
|
+
.fl-api-response + .fl-api-response { border-top: 1px solid var(--fl-border); }
|
|
707
|
+
.fl-api-response > summary, .fl-api-schema > summary { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 10px; padding: 11px 12px; cursor: pointer; list-style: none; }
|
|
708
|
+
.fl-api-response > summary::-webkit-details-marker, .fl-api-schema > summary::-webkit-details-marker { display: none; }
|
|
709
|
+
.fl-api-response > summary:hover, .fl-api-schema > summary:hover { background: var(--fl-soft); }
|
|
710
|
+
.fl-api-response > summary svg, .fl-api-schema > summary svg { width: 14px; transition: transform .15s ease; }
|
|
711
|
+
.fl-api-response[open] > summary svg, .fl-api-schema[open] > summary svg { transform: rotate(180deg); }
|
|
712
|
+
.fl-api-response-content { padding: 0 12px 12px; }
|
|
713
|
+
.fl-api-media + .fl-api-media { margin-top: 12px; }
|
|
714
|
+
.fl-api-media-heading { margin: 10px 0 8px; }
|
|
715
|
+
.fl-api-schema { overflow: hidden; margin-top: 8px; border: 1px solid var(--fl-border); border-radius: calc(var(--fl-radius) * .7); }
|
|
716
|
+
.fl-api-schema > summary { grid-template-columns: minmax(0, 1fr) auto; font: 11px/1.4 var(--fl-mono); }
|
|
717
|
+
.fl-api-schema-code { position: relative; border-top: 1px solid var(--fl-border); background: #0c0c0f; color: #f4f4f5; }
|
|
718
|
+
.fl-api-schema-code .fl-api-copy { position: absolute; z-index: 1; top: 0; right: 0; height: 34px; color: #a1a1aa; border: 1px solid #29292f; border-top: 0; border-right: 0; background: #0c0c0f; }
|
|
719
|
+
.fl-api-muted { color: var(--fl-muted); }
|
|
720
|
+
.fl-api-empty, .fl-api-no-results { display: grid; gap: 6px; padding: 32px; border: 1px dashed var(--fl-border); color: var(--fl-muted); }
|
|
721
|
+
.fl-api-empty strong, .fl-api-no-results strong { color: var(--fl-fg); }
|
|
722
|
+
.fl-api-no-results { margin-top: 48px; }
|
|
723
|
+
.fl-api-sidebar-empty { color: var(--fl-muted); font-size: 13px; }
|
|
724
|
+
.fl-api-live-region { position: fixed; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
|
|
725
|
+
:focus-visible { outline: 2px solid var(--fl-primary); outline-offset: 2px; }
|
|
726
|
+
@media (max-width: 980px) { .fl-api-layout { grid-template-columns: 230px minmax(0, 1fr); } .fl-api-operation-grid { grid-template-columns: 1fr; } .fl-api-code-panel { position: relative; top: 0; } }
|
|
727
|
+
@media (max-width: 720px) { .fl-api-topbar { padding-inline: 12px; } .fl-api-brand > span:last-child, .fl-api-version-control > span, .fl-api-action { display: none; } .fl-api-layout { display: block; } .fl-api-sidebar { position: relative; top: 0; width: 100%; height: auto; max-height: 52vh; border-right: 0; border-bottom: 1px solid var(--fl-border); } .fl-api-hero { padding: 32px 18px; } .fl-api-operations { padding-inline: 18px; } .fl-api-operation-grid { gap: 22px; } .fl-api-servers { grid-template-columns: auto minmax(0, 1fr); } .fl-api-servers .fl-api-copy { display: none; } .fl-api-field--inline, .fl-api-auth-pair { grid-template-columns: 1fr; } .fl-api-console { max-height: none; } }
|
|
728
|
+
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } *, *::before, *::after { transition-duration: .01ms !important; animation-duration: .01ms !important; } }
|
|
729
|
+
`;
|
|
730
|
+
}
|
|
731
|
+
function buildClientScript() {
|
|
732
|
+
return `(() => {
|
|
733
|
+
const root = document.documentElement;
|
|
734
|
+
const search = document.querySelector('[data-search-status]');
|
|
735
|
+
const input = document.getElementById('fl-api-search');
|
|
736
|
+
const operations = Array.from(document.querySelectorAll('[data-operation]'));
|
|
737
|
+
const navItems = Array.from(document.querySelectorAll('[data-nav-operation]'));
|
|
738
|
+
const groups = Array.from(document.querySelectorAll('[data-nav-group]'));
|
|
739
|
+
const noResults = document.querySelector('[data-no-results]');
|
|
740
|
+
const copyStatus = document.querySelector('[data-copy-status]');
|
|
741
|
+
const applySearch = () => {
|
|
742
|
+
const query = (input?.value || '').trim().toLocaleLowerCase();
|
|
743
|
+
let visible = 0;
|
|
744
|
+
for (const operation of operations) {
|
|
745
|
+
const match = !query || (operation.dataset.search || '').includes(query);
|
|
746
|
+
operation.hidden = !match;
|
|
747
|
+
if (match) visible += 1;
|
|
748
|
+
}
|
|
749
|
+
for (const item of navItems) item.hidden = Boolean(query) && !(item.dataset.search || '').includes(query);
|
|
750
|
+
for (const group of groups) group.hidden = !group.querySelector('[data-nav-operation]:not([hidden])');
|
|
751
|
+
if (noResults) noResults.hidden = visible !== 0;
|
|
752
|
+
if (search) search.textContent = query ? visible + ' of ' + operations.length + ' operations' : '';
|
|
753
|
+
};
|
|
754
|
+
input?.addEventListener('input', applySearch);
|
|
755
|
+
document.addEventListener('keydown', (event) => {
|
|
756
|
+
if (event.key === '/' && document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'SELECT') {
|
|
757
|
+
event.preventDefault(); input?.focus();
|
|
758
|
+
}
|
|
759
|
+
});
|
|
760
|
+
document.querySelector('[data-version-select]')?.addEventListener('change', (event) => {
|
|
761
|
+
const target = event.currentTarget;
|
|
762
|
+
if (target instanceof HTMLSelectElement && target.value) window.location.assign(target.value);
|
|
763
|
+
});
|
|
764
|
+
document.querySelector('[data-theme-toggle]')?.addEventListener('click', () => {
|
|
765
|
+
const current = root.dataset.theme || (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
|
766
|
+
const next = current === 'dark' ? 'light' : 'dark';
|
|
767
|
+
root.dataset.theme = next;
|
|
768
|
+
try { localStorage.setItem('farming-labs-api-theme', next); } catch {}
|
|
769
|
+
});
|
|
770
|
+
try { const saved = localStorage.getItem('farming-labs-api-theme'); if (saved === 'dark' || saved === 'light') root.dataset.theme = saved; } catch {}
|
|
771
|
+
const activateConsoleTab = (consoleRoot, tab) => {
|
|
772
|
+
const tabs = Array.from(consoleRoot.querySelectorAll('[data-console-tab]'));
|
|
773
|
+
const panels = Array.from(consoleRoot.querySelectorAll('[data-console-panel]'));
|
|
774
|
+
for (const candidate of tabs) {
|
|
775
|
+
const active = candidate.dataset.consoleTab === tab;
|
|
776
|
+
candidate.setAttribute('aria-selected', String(active));
|
|
777
|
+
candidate.tabIndex = active ? 0 : -1;
|
|
778
|
+
}
|
|
779
|
+
for (const panel of panels) panel.hidden = panel.dataset.consolePanel !== tab;
|
|
780
|
+
const exampleCopy = consoleRoot.querySelector('[data-example-copy]');
|
|
781
|
+
if (exampleCopy instanceof HTMLElement) exampleCopy.hidden = tab !== 'example';
|
|
782
|
+
};
|
|
783
|
+
for (const consoleRoot of document.querySelectorAll('[data-console-root]')) {
|
|
784
|
+
const tabs = Array.from(consoleRoot.querySelectorAll('[data-console-tab]'));
|
|
785
|
+
for (const tab of tabs) {
|
|
786
|
+
tab.addEventListener('click', () => activateConsoleTab(consoleRoot, tab.dataset.consoleTab || 'example'));
|
|
787
|
+
tab.addEventListener('keydown', (event) => {
|
|
788
|
+
if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return;
|
|
789
|
+
event.preventDefault();
|
|
790
|
+
const current = tabs.indexOf(tab);
|
|
791
|
+
const next = event.key === 'Home' ? 0 : event.key === 'End' ? tabs.length - 1 : (current + (event.key === 'ArrowRight' ? 1 : -1) + tabs.length) % tabs.length;
|
|
792
|
+
const nextTab = tabs[next];
|
|
793
|
+
if (nextTab instanceof HTMLElement) {
|
|
794
|
+
activateConsoleTab(consoleRoot, nextTab.dataset.consoleTab || 'example');
|
|
795
|
+
nextTab.focus();
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
const setConsoleState = (form, state, label) => {
|
|
801
|
+
const badge = form.querySelector('[data-console-state]');
|
|
802
|
+
if (badge instanceof HTMLElement) { badge.dataset.state = state; badge.textContent = label; }
|
|
803
|
+
};
|
|
804
|
+
const encodeBasicCredentials = (value) => {
|
|
805
|
+
const bytes = new TextEncoder().encode(value);
|
|
806
|
+
let binary = '';
|
|
807
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
808
|
+
return btoa(binary);
|
|
809
|
+
};
|
|
810
|
+
const appendQueryValue = (searchParams, name, value, array) => {
|
|
811
|
+
const values = array ? value.split(',').map((entry) => entry.trim()).filter(Boolean) : [value];
|
|
812
|
+
for (const entry of values) searchParams.append(name, entry);
|
|
813
|
+
};
|
|
814
|
+
const formatResponseBody = (value, contentType) => {
|
|
815
|
+
if (!value) return '(empty response)';
|
|
816
|
+
if (!contentType.includes('json') && !contentType.includes('+json')) return value;
|
|
817
|
+
try { return JSON.stringify(JSON.parse(value), null, 2); } catch { return value; }
|
|
818
|
+
};
|
|
819
|
+
for (const form of document.querySelectorAll('[data-api-console]')) {
|
|
820
|
+
if (!(form instanceof HTMLFormElement)) continue;
|
|
821
|
+
let controller;
|
|
822
|
+
const send = form.querySelector('[data-console-send]');
|
|
823
|
+
const cancel = form.querySelector('[data-console-cancel]');
|
|
824
|
+
const confirm = form.querySelector('[data-console-confirm]');
|
|
825
|
+
const message = form.querySelector('[data-console-message]');
|
|
826
|
+
const error = form.querySelector('[data-console-error]');
|
|
827
|
+
const responsePanel = form.querySelector('[data-console-response]');
|
|
828
|
+
if (confirm instanceof HTMLInputElement && send instanceof HTMLButtonElement) {
|
|
829
|
+
confirm.addEventListener('change', () => { send.disabled = !confirm.checked; });
|
|
830
|
+
}
|
|
831
|
+
cancel?.addEventListener('click', () => controller?.abort());
|
|
832
|
+
form.addEventListener('submit', async (event) => {
|
|
833
|
+
event.preventDefault();
|
|
834
|
+
if (!form.reportValidity() || !(send instanceof HTMLButtonElement)) return;
|
|
835
|
+
if (error instanceof HTMLElement) { error.hidden = true; error.textContent = ''; }
|
|
836
|
+
if (responsePanel instanceof HTMLElement) responsePanel.hidden = true;
|
|
837
|
+
const originalLabel = send.textContent || 'Send request';
|
|
838
|
+
send.disabled = true;
|
|
839
|
+
send.textContent = 'Sending…';
|
|
840
|
+
if (cancel instanceof HTMLElement) cancel.hidden = false;
|
|
841
|
+
if (message instanceof HTMLElement) message.textContent = 'Request in progress';
|
|
842
|
+
setConsoleState(form, 'sending', 'Sending');
|
|
843
|
+
controller = new AbortController();
|
|
844
|
+
const started = performance.now();
|
|
845
|
+
try {
|
|
846
|
+
const serverInput = form.elements.namedItem('server');
|
|
847
|
+
if (!(serverInput instanceof HTMLInputElement)) throw new Error('Server URL is required.');
|
|
848
|
+
const server = new URL(serverInput.value, window.location.origin);
|
|
849
|
+
if (server.protocol !== 'http:' && server.protocol !== 'https:') throw new Error('Server URL must use HTTP or HTTPS.');
|
|
850
|
+
let operationPath = form.dataset.path || '/';
|
|
851
|
+
const headers = new Headers();
|
|
852
|
+
const query = [];
|
|
853
|
+
for (const input of form.querySelectorAll('[data-param-name]')) {
|
|
854
|
+
if (!(input instanceof HTMLInputElement || input instanceof HTMLSelectElement)) continue;
|
|
855
|
+
const value = input.value.trim();
|
|
856
|
+
if (!value) continue;
|
|
857
|
+
const name = input.dataset.paramName || '';
|
|
858
|
+
const location = input.dataset.paramLocation;
|
|
859
|
+
if (location === 'path') operationPath = operationPath.split('{' + name + '}').join(encodeURIComponent(value));
|
|
860
|
+
else if (location === 'query') query.push([name, value, input.dataset.paramArray === 'true']);
|
|
861
|
+
else if (location === 'header') headers.set(name, value);
|
|
862
|
+
}
|
|
863
|
+
server.hash = '';
|
|
864
|
+
server.search = '';
|
|
865
|
+
server.pathname = server.pathname.replace(/\\/+$/, '') + '/' + operationPath.replace(/^\\/+/, '');
|
|
866
|
+
const url = server;
|
|
867
|
+
for (const [name, value, array] of query) appendQueryValue(url.searchParams, name, value, array);
|
|
868
|
+
for (const auth of form.querySelectorAll('[data-auth-token]')) {
|
|
869
|
+
if (auth instanceof HTMLInputElement && auth.value) headers.set('authorization', (auth.dataset.authPrefix || 'Bearer') + ' ' + auth.value);
|
|
870
|
+
}
|
|
871
|
+
for (const auth of form.querySelectorAll('[data-auth-api-key]')) {
|
|
872
|
+
if (!(auth instanceof HTMLInputElement) || !auth.value) continue;
|
|
873
|
+
const name = auth.dataset.authName || '';
|
|
874
|
+
if (auth.dataset.authLocation === 'query') url.searchParams.append(name, auth.value);
|
|
875
|
+
else headers.set(name, auth.value);
|
|
876
|
+
}
|
|
877
|
+
const basic = form.querySelector('[data-auth-basic]');
|
|
878
|
+
const username = basic?.querySelector('[data-auth-username]');
|
|
879
|
+
const password = basic?.querySelector('[data-auth-password]');
|
|
880
|
+
if (username instanceof HTMLInputElement && password instanceof HTMLInputElement && (username.value || password.value)) {
|
|
881
|
+
headers.set('authorization', 'Basic ' + encodeBasicCredentials(username.value + ':' + password.value));
|
|
882
|
+
}
|
|
883
|
+
const bodyInput = form.elements.namedItem('request-body');
|
|
884
|
+
const contentTypeInput = form.elements.namedItem('content-type');
|
|
885
|
+
let body;
|
|
886
|
+
if (bodyInput instanceof HTMLTextAreaElement && bodyInput.value.trim()) {
|
|
887
|
+
body = bodyInput.value;
|
|
888
|
+
const contentType = contentTypeInput instanceof HTMLSelectElement ? contentTypeInput.value : 'application/json';
|
|
889
|
+
if (contentType.toLocaleLowerCase().includes('json') || contentType.toLocaleLowerCase().includes('+json')) body = JSON.stringify(JSON.parse(body));
|
|
890
|
+
headers.set('content-type', contentType);
|
|
891
|
+
}
|
|
892
|
+
const credentialsInput = form.elements.namedItem('credentials');
|
|
893
|
+
const credentials = credentialsInput instanceof HTMLSelectElement ? credentialsInput.value : 'same-origin';
|
|
894
|
+
const response = await fetch(url, {
|
|
895
|
+
method: form.dataset.method || 'GET',
|
|
896
|
+
headers,
|
|
897
|
+
body,
|
|
898
|
+
credentials,
|
|
899
|
+
signal: controller.signal,
|
|
900
|
+
});
|
|
901
|
+
const rawBody = await response.text();
|
|
902
|
+
const contentType = response.headers.get('content-type') || 'unknown content type';
|
|
903
|
+
const formattedBody = formatResponseBody(rawBody, contentType.toLocaleLowerCase());
|
|
904
|
+
const duration = Math.round(performance.now() - started);
|
|
905
|
+
const status = form.querySelector('[data-response-status]');
|
|
906
|
+
const durationTarget = form.querySelector('[data-response-duration]');
|
|
907
|
+
const urlTarget = form.querySelector('[data-response-url]');
|
|
908
|
+
const typeTarget = form.querySelector('[data-response-type]');
|
|
909
|
+
const headersTarget = form.querySelector('[data-response-headers]');
|
|
910
|
+
const bodyTarget = form.querySelector('[data-response-body]');
|
|
911
|
+
const copy = form.querySelector('[data-response-copy]');
|
|
912
|
+
if (status instanceof HTMLElement) { status.textContent = response.status + ' ' + response.statusText; status.dataset.ok = String(response.ok); }
|
|
913
|
+
if (durationTarget instanceof HTMLElement) durationTarget.textContent = duration + ' ms';
|
|
914
|
+
if (urlTarget instanceof HTMLElement) { urlTarget.textContent = url.href; urlTarget.title = url.href; }
|
|
915
|
+
if (typeTarget instanceof HTMLElement) typeTarget.textContent = contentType;
|
|
916
|
+
if (headersTarget instanceof HTMLElement) headersTarget.textContent = Array.from(response.headers.entries()).map(([name, value]) => name + ': ' + value).join('\\n') || '(no exposed response headers)';
|
|
917
|
+
if (bodyTarget instanceof HTMLElement) bodyTarget.textContent = formattedBody;
|
|
918
|
+
if (copy instanceof HTMLButtonElement) copy.dataset.copy = formattedBody;
|
|
919
|
+
if (responsePanel instanceof HTMLElement) { responsePanel.hidden = false; responsePanel.focus({ preventScroll: true }); }
|
|
920
|
+
setConsoleState(form, response.ok ? 'success' : 'error', response.ok ? 'Complete' : 'HTTP error');
|
|
921
|
+
if (message instanceof HTMLElement) message.textContent = response.ok ? 'Request completed' : 'Request completed with an error status';
|
|
922
|
+
} catch (requestError) {
|
|
923
|
+
const aborted = requestError instanceof DOMException && requestError.name === 'AbortError';
|
|
924
|
+
const detail = aborted ? 'Request cancelled.' : requestError instanceof Error ? requestError.message : 'The request could not be sent.';
|
|
925
|
+
setConsoleState(form, aborted ? 'idle' : 'error', aborted ? 'Cancelled' : 'Failed');
|
|
926
|
+
if (message instanceof HTMLElement) message.textContent = aborted ? 'Request cancelled' : 'Request failed';
|
|
927
|
+
if (error instanceof HTMLElement) { error.hidden = false; error.textContent = detail; }
|
|
928
|
+
} finally {
|
|
929
|
+
controller = undefined;
|
|
930
|
+
send.textContent = originalLabel;
|
|
931
|
+
send.disabled = confirm instanceof HTMLInputElement ? !confirm.checked : false;
|
|
932
|
+
if (cancel instanceof HTMLElement) cancel.hidden = true;
|
|
933
|
+
}
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
document.addEventListener('click', async (event) => {
|
|
937
|
+
const button = event.target instanceof Element ? event.target.closest('[data-copy]') : null;
|
|
938
|
+
if (!(button instanceof HTMLButtonElement)) return;
|
|
939
|
+
const value = button.dataset.copy || '';
|
|
940
|
+
const original = button.textContent || 'Copy';
|
|
941
|
+
try {
|
|
942
|
+
await navigator.clipboard.writeText(value);
|
|
943
|
+
button.textContent = 'Copied';
|
|
944
|
+
if (copyStatus) copyStatus.textContent = 'Copied to clipboard';
|
|
945
|
+
setTimeout(() => { button.textContent = original; if (copyStatus) copyStatus.textContent = ''; }, 1600);
|
|
946
|
+
} catch {
|
|
947
|
+
button.textContent = 'Failed';
|
|
948
|
+
if (copyStatus) copyStatus.textContent = 'Could not copy to clipboard';
|
|
949
|
+
setTimeout(() => { button.textContent = original; if (copyStatus) copyStatus.textContent = ''; }, 1600);
|
|
950
|
+
}
|
|
951
|
+
});
|
|
952
|
+
})();`;
|
|
953
|
+
}
|
|
954
|
+
function searchIcon() {
|
|
955
|
+
return "<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" aria-hidden=\"true\"><circle cx=\"11\" cy=\"11\" r=\"7\"/><path d=\"m20 20-3.4-3.4\"/></svg>";
|
|
956
|
+
}
|
|
957
|
+
function themeIcon() {
|
|
958
|
+
return "<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" aria-hidden=\"true\"><path d=\"M12 3a9 9 0 1 0 9 9c0-.6-.1-1.2-.2-1.8A7 7 0 0 1 12 3Z\"/></svg>";
|
|
959
|
+
}
|
|
960
|
+
function lockIcon() {
|
|
961
|
+
return "<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" aria-hidden=\"true\"><rect x=\"5\" y=\"10\" width=\"14\" height=\"10\" rx=\"2\"/><path d=\"M8 10V7a4 4 0 0 1 8 0v3\"/></svg>";
|
|
962
|
+
}
|
|
963
|
+
function chevronIcon() {
|
|
964
|
+
return "<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" aria-hidden=\"true\"><path d=\"m6 9 6 6 6-6\"/></svg>";
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
//#endregion
|
|
968
|
+
export { renderNativeApiReferenceHtml };
|