From 3b6b31d62cf2d7beb6b5fcc65fcf8453dc873707 Mon Sep 17 00:00:00 2001 From: chan <1499188352@qq.com> Date: Mon, 31 Aug 2026 15:42:43 +0800 Subject: [PATCH 1/3] fix(styles): center search dialog horizontally with top offset --- app/globals.css | 1 + 1 file changed, 1 insertion(+) diff --git a/app/globals.css b/app/globals.css index 75e7cc99d5..829be797b9 100644 --- a/app/globals.css +++ b/app/globals.css @@ -2841,6 +2841,7 @@ select { .search-dialog { width: min(720px, calc(100vw - 28px)); max-height: min(680px, calc(100vh - 40px)); + margin: 10vh auto auto; padding: 0; overflow: hidden; border: 1px solid var(--border-strong); From 5a66ec241bf9847670dfc00c0e8d5ebf5d065a46 Mon Sep 17 00:00:00 2001 From: chan <1499188352@qq.com> Date: Mon, 31 Aug 2026 16:03:19 +0800 Subject: [PATCH 2/3] fix(styles): align pagination arrows with navigation labels --- app/globals.css | 27 ++++++++++++++++----------- src/components/docs/pagination.tsx | 13 +++++++++---- src/components/ui/icons.tsx | 14 ++++++++++++++ 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/app/globals.css b/app/globals.css index 829be797b9..fde0197f78 100644 --- a/app/globals.css +++ b/app/globals.css @@ -2739,6 +2739,20 @@ select { text-transform: uppercase; } +.pagination a .pagination-label { + display: flex; + width: 100%; + align-items: center; + gap: 5px; +} + +.pagination a .pagination-label svg { + width: 15px; + height: 15px; + flex: 0 0 auto; + color: var(--accent); +} + .pagination a strong { min-width: 0; overflow: hidden; @@ -2752,13 +2766,8 @@ select { text-align: right; } -.pagination-next svg { - position: absolute; - right: 11px; - bottom: 10px; - width: 15px; - height: 15px; - color: var(--accent); +.pagination-next .pagination-label { + justify-content: flex-end; } .toc-nav > p { @@ -4641,10 +4650,6 @@ html.dark .guide-markdown a { grid-template-columns: 1fr; } - .pagination-next { - text-align: left; - } - .feedback-card, .site-footer { align-items: flex-start; diff --git a/src/components/docs/pagination.tsx b/src/components/docs/pagination.tsx index e8f9cad975..2b09a0bdd7 100644 --- a/src/components/docs/pagination.tsx +++ b/src/components/docs/pagination.tsx @@ -1,5 +1,5 @@ import Link from 'next/link'; -import { ChevronRightIcon } from '@/src/components/ui/icons'; +import { ChevronLeftIcon, ChevronRightIcon } from '@/src/components/ui/icons'; import type { Locale } from '@/src/lib/i18n'; import { t, toLocalizedPath } from '@/src/lib/i18n'; import type { RouteRecord } from '@/src/types/docs'; @@ -20,7 +20,10 @@ export function Pagination({ diff --git a/src/components/ui/icons.tsx b/src/components/ui/icons.tsx index 7b3e205fd0..12affa4ef1 100644 --- a/src/components/ui/icons.tsx +++ b/src/components/ui/icons.tsx @@ -99,6 +99,20 @@ export function ChevronRightIcon(props: IconProps) { ); } +export function ChevronLeftIcon(props: IconProps) { + return ( + + + + ); +} + export function CopyIcon(props: IconProps) { return ( From 3e3482bfe49f85c127764a3e83c19076874886c6 Mon Sep 17 00:00:00 2001 From: chan <1499188352@qq.com> Date: Mon, 31 Aug 2026 17:25:17 +0800 Subject: [PATCH 3/3] fix(styles/code): unify syntax highlighting and code block controls --- app/globals.css | 37 ++++++++++++++------ source.config.ts | 11 +++++- src/components/docs/code-block.tsx | 43 ++++++++++++++++++------ src/components/docs/guides-page.tsx | 37 +++++++++++--------- src/components/docs/markdown-content.tsx | 40 +++++++++++++++------- src/components/mdx-components.tsx | 2 ++ src/lib/code-highlighting.ts | 21 ++++++++++++ 7 files changed, 142 insertions(+), 49 deletions(-) create mode 100644 src/lib/code-highlighting.ts diff --git a/app/globals.css b/app/globals.css index fde0197f78..0921352ede 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1368,7 +1368,7 @@ select { .prose-docs pre { overflow: auto; margin: 20px 0; - padding: 19px 20px; + padding: 20px 5px; border: 1px solid color-mix(in srgb, var(--border) 75%, black); border-radius: 12px; background: var(--code-bg); @@ -1386,7 +1386,7 @@ select { .prose-docs .code-block-shell pre { margin: 0; - padding-top: 48px; + padding: 20px 5px; } .prose-docs .code-block-shell.is-inside-tabs { @@ -1403,17 +1403,16 @@ select { z-index: 1; top: 10px; right: 10px; - display: inline-flex; + display: inline-grid; + width: 30px; + height: 30px; align-items: center; - gap: 6px; - min-height: 30px; - padding: 0 9px; + justify-content: center; + padding: 0; border: 1px solid rgb(255 255 255 / 0.11); border-radius: 7px; background: rgb(255 255 255 / 0.06); color: color-mix(in srgb, var(--code-text) 86%, white); - font-size: 11px; - font-weight: 700; transition: background 150ms ease, border-color 150ms ease, @@ -1427,8 +1426,13 @@ select { } .code-copy-button svg { - width: 15px; - height: 15px; + width: 14px; + height: 14px; +} + +.code-copy-button:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; } .code-tabs { @@ -3946,6 +3950,19 @@ html.dark .guide-markdown a { line-height: 1.65; } +.guide-markdown .code-block-shell { + position: relative; + margin: 20px 0; +} + +.guide-markdown .code-block-shell pre { + max-height: none; + overflow-x: auto; + overflow-y: visible; + margin: 0; + padding: 20px 5px; +} + .guide-markdown blockquote { margin: 0; padding: 14px 16px; diff --git a/source.config.ts b/source.config.ts index 31523071fd..739368ebf7 100644 --- a/source.config.ts +++ b/source.config.ts @@ -22,4 +22,13 @@ export const docs = defineDocs({ }, }); -export default defineConfig(); +export default defineConfig({ + mdxOptions: { + rehypeCodeOptions: { + themes: { + light: 'github-dark', + dark: 'github-dark', + }, + }, + }, +}); diff --git a/src/components/docs/code-block.tsx b/src/components/docs/code-block.tsx index 5b36fc7434..366b4444e2 100644 --- a/src/components/docs/code-block.tsx +++ b/src/components/docs/code-block.tsx @@ -1,12 +1,13 @@ 'use client'; -import { useState } from 'react'; +import { useRef, useState, type ComponentPropsWithoutRef, type ReactNode } from 'react'; import { CheckIcon, CopyIcon } from '@/src/components/ui/icons'; import { writeClipboardText } from '@/src/lib/clipboard'; import type { Locale } from '@/src/lib/i18n'; export type CodeTab = { code: string; + highlightedHtml?: string; language: string; title: string; }; @@ -14,21 +15,28 @@ export type CodeTab = { export function CodeBlock({ code, insideTabs = false, + highlightedHtml, + children, language, locale = 'en', + ...preProps }: { - code: string; + code?: string; insideTabs?: boolean; - language: string; + highlightedHtml?: string; + language?: string; locale?: Locale; -}) { + children?: ReactNode; +} & Omit, 'children'>) { + const codeBlockRef = useRef(null); const [copied, setCopied] = useState(false); const copyLabel = locale === 'zh' ? '复制代码' : 'Copy code'; const copiedLabel = locale === 'zh' ? '已复制' : 'Copied'; async function copyCode() { try { - await writeClipboardText(code); + const renderedCode = codeBlockRef.current?.querySelector('pre')?.textContent; + await writeClipboardText(renderedCode ?? code ?? ''); setCopied(true); window.setTimeout(() => setCopied(false), 1400); } catch { @@ -37,7 +45,7 @@ export function CodeBlock({ } return ( -
+
-
-        {code}
-      
+ {highlightedHtml ? ( +
+ ) : children !== undefined ? ( +
+
{children}
+
+ ) : ( +
+          {code ?? ''}
+        
+ )}
); } @@ -75,7 +90,13 @@ export function CodeTabs({ locale = 'en', tabs }: { locale?: Locale; tabs: CodeT ))}
- +
); } diff --git a/src/components/docs/guides-page.tsx b/src/components/docs/guides-page.tsx index fbe158056c..3ea7847f17 100644 --- a/src/components/docs/guides-page.tsx +++ b/src/components/docs/guides-page.tsx @@ -3,11 +3,13 @@ import { resolve } from 'node:path'; import Link from 'next/link'; import { notFound, redirect } from 'next/navigation'; import { Fragment, type ReactNode } from 'react'; +import { CodeBlock } from '@/src/components/docs/code-block'; import { ArticleHeader } from '@/src/components/docs/article-header'; import type { ContextOption } from '@/src/components/docs/context-picker'; import { DocsShell } from '@/src/components/docs/docs-shell'; import { ChevronRightIcon } from '@/src/components/ui/icons'; import guidesContentData from '@/src/generated/guides-content.json'; +import { highlightCode } from '@/src/lib/code-highlighting'; import { extractMarkdownHeadings } from '@/src/lib/heading-ids'; import type { Locale } from '@/src/lib/i18n'; import { toLocalizedPath } from '@/src/lib/i18n'; @@ -884,7 +886,7 @@ function GuidesBody({ ); } -function GuideMarkdown({ +async function GuideMarkdown({ body, locale, sourceMap, @@ -897,16 +899,18 @@ function GuideMarkdown({ }) { return (
- {parseGuideMarkdown(body).map((block, index) => ( - - ))} + {await Promise.all( + parseGuideMarkdown(body).map((block, index) => ( + + )), + )}
); } @@ -920,7 +924,7 @@ type GuideMarkdownBlock = | { type: 'paragraph'; text: string } | { type: 'table'; rows: string[][] }; -function GuideMarkdownBlock({ +async function GuideMarkdownBlock({ block, index, locale, @@ -964,9 +968,12 @@ function GuideMarkdownBlock({ if (block.type === 'code') { return ( -
-        {block.code}
-      
+ ); } diff --git a/src/components/docs/markdown-content.tsx b/src/components/docs/markdown-content.tsx index 8dbf4a4560..a2f8ceaee9 100644 --- a/src/components/docs/markdown-content.tsx +++ b/src/components/docs/markdown-content.tsx @@ -2,6 +2,7 @@ import Link from 'next/link'; import { Fragment, type ReactNode } from 'react'; import { CodeBlock, CodeTabs, type CodeTab } from '@/src/components/docs/code-block'; import { createHeadingIdGenerator } from '@/src/lib/heading-ids'; +import { highlightCode } from '@/src/lib/code-highlighting'; import type { Locale } from '@/src/lib/i18n'; import { t, toLocalizedPath } from '@/src/lib/i18n'; import { matchCommercialSymbol } from '@/src/lib/client-sdk-commercial'; @@ -21,7 +22,7 @@ type InlineRenderOptions = { locale: Locale; }; -export function MarkdownContent({ +export async function MarkdownContent({ body, locale, commercialNames, @@ -34,19 +35,21 @@ export function MarkdownContent({ return ( <> - {parseMarkdown(body).map((block, index) => ( - - ))} + {await Promise.all( + parseMarkdown(body).map((block, index) => ( + + )), + )} ); } -function MarkdownBlockView({ +async function MarkdownBlockView({ block, index, inlineOptions, @@ -81,11 +84,24 @@ function MarkdownBlockView({ } if (block.type === 'code') { - return ; + return ( + + ); } if (block.type === 'codeTabs') { - return ; + const tabs = await Promise.all( + block.tabs.map(async (tab) => ({ + ...tab, + highlightedHtml: await highlightCode(tab.code, tab.language), + })), + ); + return ; } if (block.type === 'list') { diff --git a/src/components/mdx-components.tsx b/src/components/mdx-components.tsx index cf4edb23c9..642a815841 100644 --- a/src/components/mdx-components.tsx +++ b/src/components/mdx-components.tsx @@ -2,6 +2,7 @@ import Link from 'next/link'; import type { ComponentPropsWithoutRef } from 'react'; import type { MDXComponents } from 'mdx/types'; import defaultMdxComponents from 'fumadocs-ui/mdx'; +import { CodeBlock } from '@/src/components/docs/code-block'; import { ChatHero, FeaturedSamplesSection, @@ -29,6 +30,7 @@ function SmartLink({ export function getMDXComponents(locale: Locale = 'en', components?: MDXComponents): MDXComponents { return { ...defaultMdxComponents, + pre: (props) => , a: (props) => , ChatHero: (props) => , FeaturedSamplesSection: () => , diff --git a/src/lib/code-highlighting.ts b/src/lib/code-highlighting.ts new file mode 100644 index 0000000000..b0293dfc0c --- /dev/null +++ b/src/lib/code-highlighting.ts @@ -0,0 +1,21 @@ +import { codeToHtml } from 'shiki'; + +const codeTheme = { + light: 'github-dark', + dark: 'github-dark', +} as const; + +/** Render code on the server so custom Markdown uses the same Shiki theme as MDX. */ +export async function highlightCode(code: string, language: string) { + const options = { + defaultColor: false, + lang: language || 'text', + themes: codeTheme, + } as const; + + try { + return await codeToHtml(code, options); + } catch { + return await codeToHtml(code, { ...options, lang: 'text' }); + } +}