docs-v2/layouts/partials/api/rapidoc.html

409 lines
13 KiB
HTML

{{/*
RapiDoc API Documentation Renderer
Primary API documentation renderer using RapiDoc with "Mix your own HTML" slots.
See: https://rapidocweb.com/examples.html
Required page params:
- staticFilePath: Path to the OpenAPI specification file
Optional page params:
- operationId: Specific operation to display (renders only that operation)
- tag: Tag to filter operations by
RapiDoc slots available for custom content:
- slot="header" - Custom header
- slot="footer" - Custom footer
- slot="overview" - Custom overview content
- slot="auth" - Custom authentication section
- slot="nav-logo" - Custom navigation logo
*/}}
{{ $specPath := .Params.staticFilePath }}
{{ $specPathJSON := replace $specPath ".yaml" ".json" | replace ".yml" ".json" }}
{{ $operationId := .Params.operationId | default "" }}
{{ $tag := .Params.tag | default "" }}
{{/* Machine-readable links for AI agent discovery */}}
{{ if $specPath }}
<link rel="alternate" type="application/x-yaml" href="{{ $specPath | absURL }}" title="OpenAPI Specification (YAML)" />
<link rel="alternate" type="application/json" href="{{ $specPathJSON | absURL }}" title="OpenAPI Specification (JSON)" />
{{ end }}
<div class="api-reference-wrapper">
{{/* RapiDoc component with slot-based customization */}}
<rapi-doc
id="api-doc"
spec-url="{{ $specPath }}"
theme="light"
bg-color="#ffffff"
text-color="#2b2b2b"
header-color="#020a47"
primary-color="#00A3FF"
nav-bg-color="#f7f8fa"
nav-text-color="#2b2b2b"
nav-hover-bg-color="#e8e8f0"
nav-hover-text-color="#020a47"
nav-accent-color="#020a47"
regular-font="Proxima Nova, -apple-system, BlinkMacSystemFont, sans-serif"
mono-font="IBM Plex Mono, Monaco, Consolas, monospace"
font-size="large"
render-style="view"
layout="column"
schema-style="table"
default-schema-tab="model"
response-area-height="400px"
show-header="false"
show-info="false"
show-side-nav="false"
show-components="false"
allow-authentication="true"
allow-try="false"
allow-spec-url-load="false"
allow-spec-file-load="false"
allow-server-selection="false"
allow-search="false"
fill-request-fields-with-example="true"
persist-auth="false"
{{ if $operationId }}goto-path="op/{{ $operationId }}"{{ end }}
{{ if $tag }}match-paths="tag/{{ $tag }}"{{ end }}
>
{{/* Custom overview slot - Hugo page content */}}
{{ with .Content }}
<div slot="overview">
{{ . }}
</div>
{{ end }}
{{/* Custom examples from frontmatter */}}
{{ with .Params.examples }}
<div slot="footer" class="api-custom-examples">
<h3>Examples</h3>
{{ range . }}
<div class="api-example">
<h4>{{ .title }}</h4>
{{ with .description }}<p>{{ . | markdownify }}</p>{{ end }}
<pre><code class="language-{{ .lang | default "bash" }}">{{ .code }}</code></pre>
</div>
{{ end }}
</div>
{{ end }}
</rapi-doc>
</div>
{{/* Load RapiDoc from CDN */}}
<script type="module" src="https://unpkg.com/rapidoc/dist/rapidoc-min.js"></script>
<script>
(function() {
'use strict';
console.log('[RapiDoc] Script loaded');
// Detect current theme by checking which theme stylesheet is enabled
function isDarkTheme() {
// Check for enabled dark-theme stylesheet
const darkStylesheet = document.querySelector('link[rel*="stylesheet"][title="dark-theme"]:not([disabled])');
if (darkStylesheet && !darkStylesheet.disabled) {
return true;
}
// Fallback: check for data-theme attribute (some pages may use this)
if (document.documentElement.dataset.theme === 'dark') {
return true;
}
// Fallback: check localStorage preference
try {
const stored = localStorage.getItem('defined_style_preference');
if (stored) {
const prefs = JSON.parse(stored);
return prefs.theme === 'dark';
}
} catch (e) {
// Ignore localStorage errors
}
return false;
}
// Update RapiDoc theme based on document theme
function updateRapiDocTheme() {
const rapiDoc = document.getElementById('api-doc');
if (!rapiDoc) return;
const isDark = isDarkTheme();
if (isDark) {
// Match Hugo dark theme: $grey10: #14141F
rapiDoc.setAttribute('theme', 'dark');
rapiDoc.setAttribute('bg-color', '#14141F');
rapiDoc.setAttribute('text-color', '#D4D7DD');
rapiDoc.setAttribute('header-color', '#D4D7DD');
rapiDoc.setAttribute('primary-color', '#a0a0ff');
rapiDoc.setAttribute('nav-bg-color', '#1a1a2a');
rapiDoc.setAttribute('nav-text-color', '#D4D7DD');
rapiDoc.setAttribute('nav-hover-bg-color', '#252535');
rapiDoc.setAttribute('nav-hover-text-color', '#ffffff');
rapiDoc.setAttribute('nav-accent-color', '#a0a0ff');
rapiDoc.setAttribute('code-theme', 'monokai');
} else {
// Match Hugo light theme: $g20-white: #FFFFFF
rapiDoc.setAttribute('theme', 'light');
rapiDoc.setAttribute('bg-color', '#ffffff');
rapiDoc.setAttribute('text-color', '#2b2b2b');
rapiDoc.setAttribute('header-color', '#020a47');
rapiDoc.setAttribute('primary-color', '#020a47');
rapiDoc.setAttribute('nav-bg-color', '#f7f8fa');
rapiDoc.setAttribute('nav-text-color', '#2b2b2b');
rapiDoc.setAttribute('nav-hover-bg-color', '#e8e8f0');
rapiDoc.setAttribute('nav-hover-text-color', '#020a47');
rapiDoc.setAttribute('nav-accent-color', '#020a47');
rapiDoc.setAttribute('code-theme', 'prism');
}
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', updateRapiDocTheme);
} else {
updateRapiDocTheme();
}
// Set custom CSS properties for input border styling
function setInputBorderStyles() {
const rapiDoc = document.getElementById('api-doc');
if (!rapiDoc) {
console.log('[RapiDoc] Element not found');
return;
}
// Set CSS custom property on the rapi-doc element
// This will penetrate the shadow DOM
rapiDoc.style.setProperty('--border-color', '#00A3FF');
console.log('[RapiDoc] Set --border-color to #00A3FF');
}
// Wait for RapiDoc custom element to be fully defined
if (customElements && customElements.whenDefined) {
customElements.whenDefined('rapi-doc').then(() => {
console.log('[RapiDoc] Custom element defined');
setInputBorderStyles();
// Set again after render
setTimeout(setInputBorderStyles, 500);
});
} else {
// Fallback for older browsers
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', setInputBorderStyles);
} else {
setInputBorderStyles();
}
setTimeout(setInputBorderStyles, 500);
}
// Watch for stylesheet changes (theme toggles enable/disable stylesheets)
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
// Check if a stylesheet's disabled attribute changed
if (mutation.type === 'attributes' &&
mutation.target.tagName === 'LINK' &&
mutation.target.title &&
mutation.target.title.includes('theme')) {
updateRapiDocTheme();
}
// Also watch for data-theme changes as a fallback
if (mutation.attributeName === 'data-theme') {
updateRapiDocTheme();
}
});
});
// Observe the head element for stylesheet changes
observer.observe(document.head, {
attributes: true,
attributeFilter: ['disabled'],
subtree: true
});
// Also observe the document element for data-theme changes
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme']
});
// Hide "Expand all | Collapse all" controls and Overview section inside shadow DOM
function hideExpandCollapseControls() {
const rapiDoc = document.getElementById('api-doc');
if (!rapiDoc) return;
// Wait for RapiDoc to render, try multiple times
let attempts = 0;
const maxAttempts = 10;
const tryHide = function() {
attempts++;
try {
const shadowRoot = rapiDoc.shadowRoot;
if (!shadowRoot) {
if (attempts < maxAttempts) {
setTimeout(tryHide, 500);
}
return;
}
// Find all elements in shadow DOM and hide those containing "Expand all"
const allElements = shadowRoot.querySelectorAll('*');
let hiddenCount = 0;
allElements.forEach(function(element) {
const text = element.textContent || '';
// Check if element contains "Expand all", "Collapse all"
if (text.includes('Expand all') || text.includes('Collapse all')) {
// Hide the element and its parent containers
element.style.display = 'none';
if (element.parentElement) {
element.parentElement.style.display = 'none';
}
hiddenCount++;
}
});
// Separately target all headings and hide those with "Overview"
const headings = shadowRoot.querySelectorAll('h1, h2, h3, h4');
headings.forEach(function(heading) {
const text = (heading.textContent || '').trim();
const innerText = (heading.innerText || '').trim();
// Log for debugging
if (text.includes('Overview') || innerText.includes('Overview')) {
console.log('[RapiDoc] Found heading:', heading.tagName, 'with text:', text, 'innerText:', innerText);
heading.style.display = 'none';
hiddenCount++;
}
});
// Also inject CSS as backup to hide Overview sections
const style = document.createElement('style');
style.textContent = `
/* Hide Overview section and headings */
.section-gap.section-tag,
[id*="overview"],
.regular-font.section-gap:empty,
h1:empty, h2:empty, h3:empty {
display: none !important;
}
`;
shadowRoot.appendChild(style);
console.log('[RapiDoc] Hidden ' + hiddenCount + ' elements containing expand/collapse text (attempt ' + attempts + ')');
if (hiddenCount === 0 && attempts < maxAttempts) {
// Try again if nothing was hidden
setTimeout(tryHide, 500);
}
} catch (e) {
console.log('[RapiDoc] Could not access shadow DOM:', e);
if (attempts < maxAttempts) {
setTimeout(tryHide, 500);
}
}
};
setTimeout(tryHide, 500);
}
// Call after RapiDoc initializes
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', hideExpandCollapseControls);
} else {
hideExpandCollapseControls();
}
})();
</script>
<style>
.api-reference-wrapper {
width: 100%;
}
rapi-doc {
width: 100%;
min-height: 600px;
display: block;
/* Override RapiDoc's internal font sizes to match Hugo docs */
--font-size-small: 15px;
--font-size-mono: 15px;
--font-size-regular: 17px;
/* Match Hugo theme backgrounds - light mode default */
--bg: #ffffff;
--bg2: #f7f8fa;
--bg3: #eef0f3;
/* Input field border styling - subtle with transparency */
--border-color: rgba(0, 163, 255, 0.25);
--light-border-color: rgba(0, 163, 255, 0.15);
/* HTTP method colors - lighter palette for light theme */
--blue: #00A3FF; /* $b-pool - GET */
--green: #34BB55; /* $gr-rainforest - POST */
--orange: #FFB94A; /* $y-pineapple - PUT (distinct from red) */
--red: #F95F53; /* $r-curacao - DELETE */
--purple: #9b2aff; /* $br-new-purple - PATCH */
}
/* Dark mode overrides - match Hugo $grey10: #14141F */
[data-theme="dark"] rapi-doc,
html:has(link[title="dark-theme"]:not([disabled])) rapi-doc {
--bg: #14141F;
/* Subtle border colors for dark mode with transparency */
--border-color: rgba(0, 163, 255, 0.35);
--light-border-color: rgba(0, 163, 255, 0.2);
--bg2: #1a1a2a;
--bg3: #252535;
--fg: #D4D7DD;
--fg2: #c8ccd2;
--fg3: #b0b4ba;
/* HTTP method colors - darker palette for dark theme */
--blue: #066FC5; /* $b-ocean - GET */
--green: #009F5F; /* $gr-viridian - POST */
--orange: #FFC800; /* $y-thunder - PUT (brighter for dark mode) */
--red: #DC4E58; /* $r-fire - DELETE */
--purple: #8E1FC3; /* $p-amethyst - PATCH */
}
/* Custom examples section styling */
.api-custom-examples {
padding: 1.5rem;
background: var(--bg2, #f7f8fa);
border-radius: 4px;
margin-top: 1rem;
}
.api-custom-examples h3 {
margin-top: 0;
margin-bottom: 1rem;
font-size: 1.1rem;
}
.api-example {
margin-bottom: 1.5rem;
}
.api-example:last-child {
margin-bottom: 0;
}
.api-example h4 {
margin: 0 0 0.5rem 0;
font-size: 1rem;
}
.api-example pre {
margin: 0;
padding: 1rem;
background: var(--bg3, #eef0f3);
border-radius: 4px;
overflow-x: auto;
}
</style>