diff --git a/portal-template/src/components/api-page.tsx b/portal-template/src/components/api-page.tsx index fb783cd6..04be0021 100644 --- a/portal-template/src/components/api-page.tsx +++ b/portal-template/src/components/api-page.tsx @@ -1,5 +1,6 @@ import { createOpenAPIPage } from 'fumadocs-openapi/ui'; import { createCodeUsageGeneratorRegistry } from 'fumadocs-openapi/requests/generators'; +import { CodeBlock } from './code-block'; import { renderExampleLayout } from './example-layout'; import { renderUsageTabs } from './usage-tabs'; @@ -7,5 +8,6 @@ export const OpenAPIPage = createOpenAPIPage({ // Without a registry of its own, Fumadocs registers and bundles every request generator it has. codeUsages: createCodeUsageGeneratorRegistry(), generateTypeScriptDefinitions: false, + components: { CodeBlock }, content: { renderAPIExampleLayout: renderExampleLayout, renderAPIExampleUsageTabs: renderUsageTabs } }); diff --git a/portal-template/src/components/code-block.tsx b/portal-template/src/components/code-block.tsx new file mode 100644 index 00000000..2dd09e6d --- /dev/null +++ b/portal-template/src/components/code-block.tsx @@ -0,0 +1,9 @@ +import { DynamicCodeBlock } from 'fumadocs-ui/components/dynamic-codeblock.core'; +import { useRenderContext } from 'fumadocs-openapi/ui'; + +// Fumadocs passes `JSON.stringify(example.value)`, which is undefined for an example with no `value`. +export function CodeBlock({ lang, code }: Readonly<{ lang: string; code: string | undefined }>) { + const { shiki, shikiOptions } = useRenderContext(); + if (code === undefined) return null; + return shiki.getOrInit()} options={shikiOptions} />; +} diff --git a/portal-template/src/components/usage-tabs.tsx b/portal-template/src/components/usage-tabs.tsx index 13a53951..751dd3a0 100644 --- a/portal-template/src/components/usage-tabs.tsx +++ b/portal-template/src/components/usage-tabs.tsx @@ -1,11 +1,11 @@ import { useEffect, useState, type ReactNode } from 'react'; import { CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger } from 'fumadocs-ui/components/codeblock'; -import { DynamicCodeBlock } from 'fumadocs-ui/components/dynamic-codeblock.core'; import { useOperationContext, useRenderContext, useServerContext } from 'fumadocs-openapi/ui'; import { pathnameFromRequest } from 'fumadocs-openapi/requests/generators'; import { curl } from 'fumadocs-openapi/requests/generators/curl'; import { joinURL, resolveServerUrl } from '@fumadocs/api-docs/utils/url'; import { CodeSample } from '@/lib/code-samples'; +import { CodeBlock } from './code-block'; import { useExampleSelection } from './example-layout'; interface UsageTab { @@ -68,11 +68,6 @@ function CurlCode() { return ; } -function CodeBlock({ lang, code }: Readonly<{ lang: string; code: string }>) { - const { shiki, shikiOptions } = useRenderContext(); - return shiki.getOrInit()} options={shikiOptions} />; -} - // Fumadocs reports the playground's edits to the selected example through its listeners. function useSelectedRequest() { const { examples, example, addListener, removeListener } = useOperationContext();