diff --git a/api-docs/influxdb3/core/v3/ref.yml b/api-docs/influxdb3/core/v3/ref.yml index 911e207ce..422abdffc 100644 --- a/api-docs/influxdb3/core/v3/ref.yml +++ b/api-docs/influxdb3/core/v3/ref.yml @@ -503,38 +503,6 @@ paths: description: Request entity too large. '422': description: Unprocessable entity. - x-codeSamples: - - label: cURL - Basic write - lang: Shell - source: | - curl --request POST "http://localhost:8181/api/v3/write_lp?db=sensors" \ - --header "Authorization: Bearer DATABASE_TOKEN" \ - --header "Content-Type: text/plain" \ - --data-raw "cpu,host=server01 usage=85.2 1638360000000000000" - - label: cURL - Write with millisecond precision - lang: Shell - source: | - curl --request POST "http://localhost:8181/api/v3/write_lp?db=sensors&precision=ms" \ - --header "Authorization: Bearer DATABASE_TOKEN" \ - --header "Content-Type: text/plain" \ - --data-raw "cpu,host=server01 usage=85.2 1638360000000" - - label: cURL - Asynchronous write with partial acceptance - lang: Shell - source: | - curl --request POST "http://localhost:8181/api/v3/write_lp?db=sensors&accept_partial=true&no_sync=true&precision=auto" \ - --header "Authorization: Bearer DATABASE_TOKEN" \ - --header "Content-Type: text/plain" \ - --data-raw "cpu,host=server01 usage=85.2 - memory,host=server01 used=4096" - - label: cURL - Multiple measurements with tags - lang: Shell - source: | - curl --request POST "http://localhost:8181/api/v3/write_lp?db=sensors&precision=ns" \ - --header "Authorization: Bearer DATABASE_TOKEN" \ - --header "Content-Type: text/plain" \ - --data-raw "cpu,host=server01,region=us-west usage=85.2,load=0.75 1638360000000000000 - memory,host=server01,region=us-west used=4096,free=12288 1638360000000000000 - disk,host=server01,region=us-west,device=/dev/sda1 used=50.5,free=49.5 1638360000000000000" tags: - Write data /api/v3/query_sql: @@ -1969,16 +1937,23 @@ components: text/plain: schema: type: string + description: | + Line protocol data. Each line represents a point with a measurement name, + optional tag set, field set, and optional timestamp. + + Format: `[,=...] =[,=...] []` examples: - line: - summary: Example line protocol - value: measurement,tag=value field=1 1234567890 - multiline: - summary: Example line protocol with UTF-8 characters + single-point: + summary: Write a single point + description: Write one point with tags and fields to a table. + value: cpu,host=server01 usage=85.2,load=0.75 1638360000000000000 + multiple-tables: + summary: Write to multiple tables + description: Write points to different tables (measurements) in a single request. value: | - measurement,tag=value field=1 1234567890 - measurement,tag=value field=2 1234567900 - measurement,tag=value field=3 1234568000 + cpu,host=server01,region=us-west usage=85.2,load=0.75 1638360000000000000 + memory,host=server01,region=us-west used=4096,free=12288 1638360000000000000 + disk,host=server01,region=us-west,device=/dev/sda1 used=50.5,free=49.5 1638360000000000000 queryRequestBody: required: true content: diff --git a/api-docs/influxdb3/enterprise/v3/ref.yml b/api-docs/influxdb3/enterprise/v3/ref.yml index bdfa3cfea..71d490893 100644 --- a/api-docs/influxdb3/enterprise/v3/ref.yml +++ b/api-docs/influxdb3/enterprise/v3/ref.yml @@ -458,38 +458,6 @@ paths: description: Request entity too large. '422': description: Unprocessable entity. - x-codeSamples: - - label: cURL - Basic write - lang: Shell - source: | - curl --request POST "http://localhost:8181/api/v3/write_lp?db=sensors" \ - --header "Authorization: Bearer DATABASE_TOKEN" \ - --header "Content-Type: text/plain" \ - --data-raw "cpu,host=server01 usage=85.2 1638360000000000000" - - label: cURL - Write with millisecond precision - lang: Shell - source: | - curl --request POST "http://localhost:8181/api/v3/write_lp?db=sensors&precision=ms" \ - --header "Authorization: Bearer DATABASE_TOKEN" \ - --header "Content-Type: text/plain" \ - --data-raw "cpu,host=server01 usage=85.2 1638360000000" - - label: cURL - Asynchronous write with partial acceptance - lang: Shell - source: | - curl --request POST "http://localhost:8181/api/v3/write_lp?db=sensors&accept_partial=true&no_sync=true&precision=auto" \ - --header "Authorization: Bearer DATABASE_TOKEN" \ - --header "Content-Type: text/plain" \ - --data-raw "cpu,host=server01 usage=85.2 - memory,host=server01 used=4096" - - label: cURL - Multiple measurements with tags - lang: Shell - source: | - curl --request POST "http://localhost:8181/api/v3/write_lp?db=sensors&precision=ns" \ - --header "Authorization: Bearer DATABASE_TOKEN" \ - --header "Content-Type: text/plain" \ - --data-raw "cpu,host=server01,region=us-west usage=85.2,load=0.75 1638360000000000000 - memory,host=server01,region=us-west used=4096,free=12288 1638360000000000000 - disk,host=server01,region=us-west,device=/dev/sda1 used=50.5,free=49.5 1638360000000000000" tags: - Write data /api/v3/query_sql: @@ -2019,16 +1987,23 @@ components: text/plain: schema: type: string + description: | + Line protocol data. Each line represents a point with a measurement name, + optional tag set, field set, and optional timestamp. + + Format: `[,=...] =[,=...] []` examples: - line: - summary: Example line protocol - value: measurement,tag=value field=1 1234567890 - multiline: - summary: Example line protocol with UTF-8 characters + single-point: + summary: Write a single point + description: Write one point with tags and fields to a table. + value: cpu,host=server01 usage=85.2,load=0.75 1638360000000000000 + multiple-tables: + summary: Write to multiple tables + description: Write points to different tables (measurements) in a single request. value: | - measurement,tag=value field=1 1234567890 - measurement,tag=value field=2 1234567900 - measurement,tag=value field=3 1234568000 + cpu,host=server01,region=us-west usage=85.2,load=0.75 1638360000000000000 + memory,host=server01,region=us-west used=4096,free=12288 1638360000000000000 + disk,host=server01,region=us-west,device=/dev/sda1 used=50.5,free=49.5 1638360000000000000 queryRequestBody: required: true content: diff --git a/assets/js/components/api-auth-input.ts b/assets/js/components/api-auth-input.ts index dc37965e4..7e99fe5e5 100644 --- a/assets/js/components/api-auth-input.ts +++ b/assets/js/components/api-auth-input.ts @@ -31,32 +31,43 @@ interface AuthCredentials { type CleanupFn = () => void; -// In-memory credential storage (not persisted) -let currentCredentials: AuthCredentials = {}; +// sessionStorage key for credentials +// Persists across page navigations, cleared when tab closes +const CREDENTIALS_KEY = 'influxdata-api-credentials'; /** - * Get current credentials (in-memory only) + * Get credentials from sessionStorage */ function getCredentials(): AuthCredentials { - return currentCredentials; + try { + const stored = sessionStorage.getItem(CREDENTIALS_KEY); + return stored ? JSON.parse(stored) : {}; + } catch { + return {}; + } } /** - * Set credentials (in-memory only, not persisted) + * Set credentials in sessionStorage */ function setCredentials(credentials: AuthCredentials): void { - currentCredentials = credentials; + try { + if (Object.keys(credentials).length === 0) { + sessionStorage.removeItem(CREDENTIALS_KEY); + } else { + sessionStorage.setItem(CREDENTIALS_KEY, JSON.stringify(credentials)); + } + } catch (e) { + console.warn('[API Auth] Failed to store credentials:', e); + } } /** * Check if any credentials are set */ function hasCredentials(): boolean { - return !!( - currentCredentials.bearer || - currentCredentials.basic?.password || - currentCredentials.querystring - ); + const creds = getCredentials(); + return !!(creds.bearer || creds.basic?.password || creds.querystring); } /** @@ -115,23 +126,15 @@ function applyCredentialsToRapiDoc( } } - // Apply bearer/token credentials + // Apply bearer/token credentials using setApiKey() only + // Using both HTML attributes AND setApiKey() causes "2 API keys applied" if (credentials.bearer) { try { - // Method 1: HTML attributes (most reliable) - rapiDoc.setAttribute('api-key-name', 'Authorization'); - rapiDoc.setAttribute('api-key-location', 'header'); - rapiDoc.setAttribute('api-key-value', `Bearer ${credentials.bearer}`); - console.log('[API Auth] Set auth via HTML attributes'); - - // Method 2: JavaScript API for scheme-specific auth if ('setApiKey' in rapiDoc) { (rapiDoc as any).setApiKey('BearerAuthentication', credentials.bearer); - (rapiDoc as any).setApiKey('TokenAuthentication', credentials.bearer); - console.log('[API Auth] Applied bearer/token via setApiKey()'); + console.log('[API Auth] Applied bearer via setApiKey()'); + applied = true; } - - applied = true; updateRapiDocAuthInput(rapiDoc, credentials.bearer, 'bearer'); } catch (e) { console.error('[API Auth] Failed to set API key:', e); @@ -336,19 +339,30 @@ function updateStatusIndicator(trigger: HTMLElement): void { export default function ApiAuthInput({ component, }: ComponentOptions): CleanupFn | void { - // Component is the trigger button - const trigger = component; - const popoverEl = trigger.nextElementSibling as HTMLElement | null; + // Component is the banner container, find the trigger button inside it + const triggerEl = + component.querySelector('.api-auth-trigger'); + const statusEl = component.querySelector('.api-auth-status'); - if (!popoverEl || !popoverEl.classList.contains('api-auth-popover')) { + if (!triggerEl) { + console.error('[API Auth] Trigger button not found in banner'); + return; + } + + // Find the popover element (it's a sibling before the banner) + const popoverEl = document.querySelector('.api-auth-popover'); + + if (!popoverEl) { console.error('[API Auth] Popover container not found'); return; } - // Now TypeScript knows popover is not null + // Reassign to new consts so TypeScript knows they're not null in closures + const trigger = triggerEl; const popover = popoverEl; - const schemesAttr = trigger.dataset.schemes || 'bearer'; + // Get schemes from the component (banner) dataset + const schemesAttr = component.dataset.schemes || 'bearer'; const schemes = schemesAttr.split(',').map((s) => s.trim().toLowerCase()); // Render popover content @@ -367,12 +381,35 @@ export default function ApiAuthInput({ const clearBtn = popover.querySelector('.auth-clear'); const closeBtn = popover.querySelector('.popover-close'); + // Backdrop element for modal overlay + const backdrop = document.querySelector('.api-auth-backdrop'); + + // Restore saved credentials from sessionStorage + const savedCredentials = getCredentials(); + if (savedCredentials.bearer && bearerInput) { + bearerInput.value = savedCredentials.bearer; + } + if (savedCredentials.basic) { + if (usernameInput) usernameInput.value = savedCredentials.basic.username; + if (passwordInput) passwordInput.value = savedCredentials.basic.password; + } + if (savedCredentials.querystring && querystringInput) { + querystringInput.value = savedCredentials.querystring; + } + + // Update status indicator based on saved credentials + if (hasCredentials() && statusEl) { + statusEl.textContent = 'Credentials set for this session.'; + trigger.textContent = 'Update credentials'; + } + /** * Toggle popover visibility */ function togglePopover(show?: boolean): void { const shouldShow = show ?? popover.hidden; popover.hidden = !shouldShow; + if (backdrop) backdrop.hidden = !shouldShow; trigger.setAttribute('aria-expanded', String(shouldShow)); if (shouldShow) { @@ -401,6 +438,9 @@ export default function ApiAuthInput({ // Close button closeBtn?.addEventListener('click', closePopover); + // Close on backdrop click + backdrop?.addEventListener('click', closePopover); + // Close on outside click function handleOutsideClick(e: MouseEvent): void { if ( @@ -462,17 +502,23 @@ export default function ApiAuthInput({ setCredentials(newCredentials); updateStatusIndicator(trigger); + // Update status text and button + if (hasCredentials()) { + if (statusEl) statusEl.textContent = 'Credentials set for this session.'; + trigger.textContent = 'Update credentials'; + } + // Apply to RapiDoc const rapiDoc = document.querySelector('rapi-doc') as HTMLElement | null; if (rapiDoc && 'setApiKey' in rapiDoc) { const applied = applyCredentialsToRapiDoc(rapiDoc, newCredentials); if (applied) { - showFeedback(popover, 'Credentials applied', 'success'); + showFeedback(popover, 'Credentials saved for this session', 'success'); } else { showFeedback(popover, 'No credentials to apply', 'error'); } } else { - showFeedback(popover, 'Saved (API viewer loading...)', 'success'); + showFeedback(popover, 'Credentials saved for this session', 'success'); } } @@ -489,19 +535,18 @@ export default function ApiAuthInput({ setCredentials({}); updateStatusIndicator(trigger); - // Clear from RapiDoc - const rapiDoc = document.querySelector('rapi-doc') as HTMLElement | null; - if (rapiDoc) { - rapiDoc.removeAttribute('api-key-name'); - rapiDoc.removeAttribute('api-key-location'); - rapiDoc.removeAttribute('api-key-value'); + // Reset status text and button + if (statusEl) + statusEl.textContent = 'This endpoint requires authentication.'; + trigger.textContent = 'Set credentials'; - if ('removeAllSecurityKeys' in rapiDoc) { - try { - (rapiDoc as any).removeAllSecurityKeys(); - } catch (e) { - console.debug('[API Auth] Failed to clear RapiDoc credentials:', e); - } + // Clear from RapiDoc using removeAllSecurityKeys() + const rapiDoc = document.querySelector('rapi-doc') as HTMLElement | null; + if (rapiDoc && 'removeAllSecurityKeys' in rapiDoc) { + try { + (rapiDoc as any).removeAllSecurityKeys(); + } catch (e) { + console.debug('[API Auth] Failed to clear RapiDoc credentials:', e); } } diff --git a/assets/js/components/api-toc.ts b/assets/js/components/api-toc.ts index 06b0d2f58..b76571949 100644 --- a/assets/js/components/api-toc.ts +++ b/assets/js/components/api-toc.ts @@ -37,16 +37,6 @@ interface OperationMeta { tags: string[]; } -/** - * Check if the active panel contains a RapiDoc component - */ -function isRapiDocActive(): boolean { - const activePanel = document.querySelector( - '.tab-content:not([style*="display: none"]), [data-tab-panel]:not([style*="display: none"])' - ); - return activePanel?.querySelector('rapi-doc') !== null; -} - /** * Get headings from the currently visible content */ @@ -90,11 +80,8 @@ function getVisibleHeadings(): TocEntry[] { */ function buildTocHtml(entries: TocEntry[]): string { if (entries.length === 0) { - // Check if RapiDoc is active - show helpful message - if (isRapiDocActive()) { - return '

Use RapiDoc\'s navigation below to explore this endpoint.

'; - } - return '

No sections on this page.

'; + // Return empty string - the TOC container can be hidden via CSS when empty + return ''; } let html = '
    '; @@ -405,8 +392,14 @@ export default function ApiToc({ component }: ComponentOptions): void { nav.innerHTML = buildTocHtml(entries); } - // Set up scroll highlighting - observer = setupScrollHighlighting(component, entries); + // Hide TOC if no entries, show if entries exist + if (entries.length === 0) { + component.classList.add('is-hidden'); + } else { + component.classList.remove('is-hidden'); + // Set up scroll highlighting only when we have entries + observer = setupScrollHighlighting(component, entries); + } } // Check initial visibility (hide for Operations tab, only for non-operations pages) diff --git a/assets/js/components/rapidoc-mini.ts b/assets/js/components/rapidoc-mini.ts index fba66fb47..e472920fb 100644 --- a/assets/js/components/rapidoc-mini.ts +++ b/assets/js/components/rapidoc-mini.ts @@ -247,10 +247,7 @@ function createRapiDocElement( // For credential input on operation pages, we need a custom // component (Task 5). // - // RECOMMENDATION: - // - Keep render-style="read" for compact operation display - // - Implement custom auth input component above RapiDoc (Task 5) - // - Use sessionStorage to pass credentials to "Try it" feature + // Layout and render style for compact operation display element.setAttribute('layout', 'column'); element.setAttribute('render-style', 'read'); element.setAttribute('show-header', 'false'); @@ -270,10 +267,8 @@ function createRapiDocElement( element.setAttribute('use-path-in-nav-bar', 'false'); element.setAttribute('show-info', 'false'); - // Authentication display - hide RapiDoc's built-in auth section - // We use a custom popover component for credential input instead - // Credentials are applied via HTML attributes (api-key-name, api-key-value) - // and the setApiKey() JavaScript API + // Authentication display - disabled because RapiDoc's auth UI doesn't work + // with match-paths filtering. We show a separate auth info banner instead. element.setAttribute('allow-authentication', 'false'); element.setAttribute('show-components', 'false'); @@ -319,84 +314,63 @@ function injectShadowStyles(element: HTMLElement): void { padding-top: 0 !important; } - /* Hide RapiDoc's built-in security section - we show our own */ - /* Target the authorization requirements shown near each operation */ - .api-key, - .api-key-info, - .security-info-button, - [class*="api-key"], - [class*="security-info"], - .m-markdown-small:has(.lock-icon), - div:has(> .lock-icon), - /* Target the section showing "AUTHORIZATIONS:" or similar */ - .req-resp-container > div:first-child:has(svg[style*="lock"]), - /* Target lock icons and their parent containers */ - svg.lock-icon, - .lock-icon, - /* Wide selectors for security-related elements */ - [part="section-operation-security"], - .expanded-endpoint-body > div:first-child:has([class*="lock"]) { - display: none !important; + /* Fix text cutoff - ensure content flows responsively */ + .req-res-title, + .resp-head, + .api-request, + .api-response, + .param-name, + .param-type, + .descr { + word-wrap: break-word; + overflow-wrap: break-word; + } + + /* Allow wrapping in tables and flex containers */ + table { + table-layout: auto !important; + width: 100% !important; + } + + td, th { + word-wrap: break-word; + overflow-wrap: break-word; + white-space: normal !important; + } + + /* Prevent horizontal overflow */ + .section-gap, + .expanded-req-resp-container { + max-width: 100%; + overflow-x: auto; + } + + /* Fix Copy button overlapping code content in Try It section */ + /* The Copy button is absolutely positioned, so add padding to prevent overlap */ + .curl-request pre, + .curl-request code, + .request-body-container pre, + .response-panel pre { + padding-right: 4.5rem !important; /* Space for Copy button */ + } + + /* Ensure Copy button stays visible and accessible */ + .copy-btn, + button[title="Copy"] { + z-index: 1; + background: rgba(0, 163, 255, 0.9) !important; + border-radius: 4px; + } + + /* Make code blocks wrap long URLs instead of requiring horizontal scroll */ + .curl-request code, + .request-body-container code { + white-space: pre-wrap !important; + word-break: break-all; } `; shadowRoot.appendChild(style); - // Hide security badge elements by examining content - const hideSecurityBadge = () => { - // Find elements containing security-related text and hide their container - const allElements = shadowRoot.querySelectorAll('span, div'); - allElements.forEach((el) => { - const text = el.textContent?.trim(); - // Find leaf elements that contain authorization-related text - if ( - el.children.length === 0 && - (text === 'HTTP Bearer' || - text === 'Bearer' || - text === 'AUTHORIZATIONS:' || - text === 'Authorization' || - text === 'api_token' || - text === 'BearerAuthentication') - ) { - // Walk up the DOM to find a suitable container to hide - // This hides both the text AND any sibling icons (like lock) - let target: HTMLElement = el as HTMLElement; - let parent: HTMLElement | null = el.parentElement; - let depth = 0; - while (parent && depth < 4) { - // Stop at reasonable container boundaries - if ( - parent.classList.contains('expanded-endpoint-body') || - parent.classList.contains('req-resp-container') || - parent.tagName === 'SECTION' - ) { - break; - } - target = parent; - parent = parent.parentElement; - depth++; - } - target.style.display = 'none'; - } - }); - }; - - // Run immediately and after delays for dynamic content - hideSecurityBadge(); - setTimeout(hideSecurityBadge, 300); - setTimeout(hideSecurityBadge, 800); - - // Watch for dynamically added security elements - const observer = new MutationObserver(() => { - hideSecurityBadge(); - }); - observer.observe(shadowRoot, { - childList: true, - subtree: true, - }); - - // Disconnect after 5 seconds to avoid performance issues - setTimeout(() => observer.disconnect(), 5000); - return true; }; diff --git a/assets/styles/layouts/_api-security-schemes.scss b/assets/styles/layouts/_api-security-schemes.scss index a80acb7fb..1c0446ca5 100644 --- a/assets/styles/layouts/_api-security-schemes.scss +++ b/assets/styles/layouts/_api-security-schemes.scss @@ -72,92 +72,61 @@ } } +// Dark theme overrides for security schemes +[data-theme="dark"], +html:has(link[title="dark-theme"]:not([disabled])) { + .api-security-schemes { + border-top-color: $grey25; + + .security-scheme { + background: $grey15; + border-color: $grey25; + } + + .scheme-details { + dt { + color: $g15-platinum; + } + } + + .scheme-description { + border-top-color: $grey25; + } + } +} + //////////////////////////////////////////////////////////////////////////////// -// API Auth Popover - Credential input for operation pages +// API Auth Modal - Credential input for operation pages // -// Popover-based UI triggered by "Set credentials" button. -// Positioned above RapiDoc, integrates with "Try it" via JavaScript API. +// Modal UI triggered by "Set credentials" button in auth info banner. +// Integrates with RapiDoc "Try it" via JavaScript API. //////////////////////////////////////////////////////////////////////////////// -.api-auth-row { - display: flex; - align-items: center; - gap: 0.75rem; - margin-bottom: 0.5rem; -} - -.api-auth-trigger-wrapper { - position: relative; - display: inline-block; -} - -.api-auth-schemes { - font-size: 0.85rem; - color: $g9-mountain; - - .api-auth-label { - font-weight: 400; - opacity: 0.8; - } -} - -.api-auth-trigger { - display: inline-flex; - align-items: center; - gap: 0.5rem; - padding: 0.4rem 0.75rem; - font-size: 0.85rem; - font-weight: 500; - color: $article-text; - background: $g20-white; - border: 1px solid $g5-pepper; - border-radius: 4px; - cursor: pointer; - transition: all 0.15s ease; - - &:hover { - background: rgba($b-pool, 0.08); - border-color: $b-pool; - } - - &:focus { - outline: 2px solid $b-pool; - outline-offset: 2px; - } - - &.has-credentials { - border-color: $gr-viridian; - background: rgba($gr-viridian, 0.08); - } - - .auth-icon { - color: $g9-mountain; - } - - &.has-credentials .auth-icon { - color: $gr-viridian; - } -} - -.auth-status-indicator { - width: 8px; - height: 8px; - background: $gr-viridian; - border-radius: 50%; - margin-left: 0.25rem; -} - -.api-auth-popover { - position: absolute; - top: calc(100% + 8px); - left: 0; +// Backdrop overlay +.api-auth-backdrop { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.4); z-index: 1000; + + &[hidden] { + display: none; + } +} + +// Credentials modal +.api-auth-popover { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 1001; min-width: 320px; max-width: 400px; background: $g20-white; border: 1px solid $g5-pepper; - border-radius: 6px; - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); + border-radius: 8px; + box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25); &[hidden] { display: none; @@ -219,10 +188,6 @@ color: $article-text; } - .auth-label-text { - margin-right: 0.25rem; - } - .auth-label-hint { font-weight: 400; color: $g9-mountain; @@ -284,25 +249,11 @@ } } -.auth-field-group { - margin-top: 1rem; - padding-top: 1rem; - border-top: 1px solid $g5-pepper; - - .auth-group-label { - margin: 0 0 0.75rem 0; - font-weight: 600; - font-size: 0.85rem; - color: $article-text; - } -} - .auth-actions { display: flex; gap: 0.5rem; margin-top: 1rem; - // Explicit button styling to avoid link-like appearance .auth-apply, .auth-clear { padding: 0.4rem 0.75rem; @@ -354,56 +305,9 @@ } } -// Dark theme overrides +// Dark theme for modal [data-theme="dark"], html:has(link[title="dark-theme"]:not([disabled])) { - .api-auth-schemes { - color: $g15-platinum; - } - - .api-security-schemes { - border-top-color: $grey25; - - .security-scheme { - background: $grey15; - border-color: $grey25; - } - - .scheme-details { - dt { - color: $g15-platinum; - } - } - - .scheme-description { - border-top-color: $grey25; - } - } - - .api-auth-trigger { - background: $grey15; - border-color: $grey25; - color: $g20-white; - - &:hover { - background: rgba($b-pool, 0.1); - border-color: $b-pool; - } - - &.has-credentials { - border-color: $gr-viridian; - background: rgba($gr-viridian, 0.1); - } - - .auth-icon { - color: $g15-platinum; - } - - &.has-credentials .auth-icon { - color: $gr-emerald; - } - } - .api-auth-popover { background: $grey15; border-color: $grey25; @@ -459,14 +363,6 @@ html:has(link[title="dark-theme"]:not([disabled])) { } } - .auth-field-group { - border-top-color: $grey25; - - .auth-group-label { - color: $g20-white; - } - } - .auth-feedback { &.auth-feedback--success { background: rgba($gr-viridian, 0.15); @@ -480,19 +376,7 @@ html:has(link[title="dark-theme"]:not([disabled])) { } .auth-actions { - .auth-apply { - background: $b-pool; - color: $g20-white; - border-color: $b-pool; - - &:hover { - background: lighten($b-pool, 5%); - border-color: lighten($b-pool, 5%); - } - } - .auth-clear { - background: transparent; color: $g15-platinum; border-color: $grey25; diff --git a/layouts/partials/api/rapidoc-mini.html b/layouts/partials/api/rapidoc-mini.html index 7ac783151..5e133ee0a 100644 --- a/layouts/partials/api/rapidoc-mini.html +++ b/layouts/partials/api/rapidoc-mini.html @@ -41,41 +41,21 @@ {{ end }} -{{/* Auth credentials popover trigger with scheme indicator */}} -{{/* Map scheme codes to display names */}} -{{ $schemeNames := dict "bearer" "HTTP Bearer" "token" "API Token" "basic" "HTTP Basic" "querystring" "Query String" }} -{{ $schemeList := split $authSchemes "," }} -{{ $displaySchemes := slice }} -{{ range $schemeList }} - {{ $name := index $schemeNames . }} - {{ if $name }} - {{ $displaySchemes = $displaySchemes | append $name }} - {{ end }} -{{ end }} +{{/* Auth credentials modal */}} + + -
    -
    - - {{/* Popover container - positioned by CSS */}} - -
    - - Auth: {{ delimit $displaySchemes " or " }} - +{{/* Authentication info banner with trigger for credentials modal */}} +
    + + + + This endpoint requires authentication. +
    {{/* Component container - TypeScript handles initialization */}} @@ -155,15 +135,57 @@ rapi-doc::part(section-tag) { display: none; } -/* Fix auth schemes at narrow widths - ensure content is scrollable */ -@media (max-width: 1280px) { - .api-reference-mini { - overflow-x: auto; - } +/* Responsive layout - allow content to flow naturally */ +.api-reference-mini { + overflow-x: auto; + max-width: 100%; +} - rapi-doc-mini, rapi-doc { - min-width: 800px; /* Prevent auth elements from collapsing/overlapping */ - } +/* Authentication info banner */ +.api-auth-info { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1rem; + margin-bottom: 1rem; + background: #F0F4FF; + border: 1px solid rgba(0, 163, 255, 0.3); + border-radius: 4px; + font-size: 0.9rem; + color: #545667; +} + +.api-auth-info .api-auth-icon { + flex-shrink: 0; + color: #00A3FF; +} + +.api-auth-info .api-auth-status { + flex: 1; +} + +.api-auth-info .api-auth-trigger { + background: none; + border: 1px solid #00A3FF; + color: #00A3FF; + padding: 0.25rem 0.75rem; + border-radius: 4px; + font-size: 0.85rem; + cursor: pointer; + transition: all 0.15s; +} + +.api-auth-info .api-auth-trigger:hover { + background: #00A3FF; + color: #fff; +} + +/* Dark mode */ +[data-theme="dark"] .api-auth-info, +html:has(link[title="dark-theme"]:not([disabled])) .api-auth-info { + background: #1a1a2e; + border-color: rgba(0, 163, 255, 0.2); + color: #D4D7DD; }