diff --git a/.changeset/fast-pigs-itch.md b/.changeset/fast-pigs-itch.md new file mode 100644 index 000000000..cb8281fc7 --- /dev/null +++ b/.changeset/fast-pigs-itch.md @@ -0,0 +1,5 @@ +--- +'gitbook': patch +--- + +Improve codeblock highlight style diff --git a/packages/gitbook/src/components/DocumentView/CodeBlock/ClientCodeBlock.tsx b/packages/gitbook/src/components/DocumentView/CodeBlock/ClientCodeBlock.tsx index 77d9b33c5..7b08fb5c1 100644 --- a/packages/gitbook/src/components/DocumentView/CodeBlock/ClientCodeBlock.tsx +++ b/packages/gitbook/src/components/DocumentView/CodeBlock/ClientCodeBlock.tsx @@ -5,6 +5,7 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { useInViewportListener } from '@/components/hooks/useInViewportListener'; import { useScrollListener } from '@/components/hooks/useScrollListener'; +import { useTheme } from 'next-themes'; import { useDebounceCallback } from 'usehooks-ts'; import type { BlockProps } from '../Block'; import { CodeBlockRenderer } from './CodeBlockRenderer'; @@ -26,6 +27,7 @@ export function ClientCodeBlock(props: ClientBlockProps) { const [isInViewport, setIsInViewport] = useState(false); const plainLines = useMemo(() => plainHighlight(block, []), [block]); const [lines, setLines] = useState(null); + const { resolvedTheme } = useTheme(); // Preload the highlighter when the block is mounted. useEffect(() => { @@ -78,7 +80,7 @@ export function ClientCodeBlock(props: ClientBlockProps) { if (typeof window !== 'undefined') { import('./highlight').then(({ highlight }) => { - highlight(block, inlines).then((lines) => { + highlight(block, inlines, resolvedTheme).then((lines) => { if (cancelled) { return; } @@ -95,7 +97,7 @@ export function ClientCodeBlock(props: ClientBlockProps) { // Otherwise if the block is not in viewport, we reset to the plain lines setLines(null); - }, [isInViewport, block, inlines]); + }, [isInViewport, block, inlines, resolvedTheme]); return ( diff --git a/packages/gitbook/src/components/DocumentView/CodeBlock/CodeBlock.tsx b/packages/gitbook/src/components/DocumentView/CodeBlock/CodeBlock.tsx index 2516c8d4f..46f2bd9b0 100644 --- a/packages/gitbook/src/components/DocumentView/CodeBlock/CodeBlock.tsx +++ b/packages/gitbook/src/components/DocumentView/CodeBlock/CodeBlock.tsx @@ -3,6 +3,8 @@ import type { DocumentBlockCode } from '@gitbook/api'; import { getNodeFragmentByType } from '@/lib/document'; import { isV2 } from '@/lib/v2'; +import { MiddlewareHeaders } from '@v2/lib/middleware'; +import { headers } from 'next/headers'; import type { BlockProps } from '../Block'; import { Blocks } from '../Blocks'; import { ClientCodeBlock } from './ClientCodeBlock'; @@ -38,9 +40,14 @@ export async function CodeBlock(props: BlockProps) { if (isV2() && !isEstimatedOffscreen) { // In v2, we render the code block server-side - const lines = await highlight(block, richInlines); + const theme = getTheme(); + const lines = await highlight(block, richInlines, theme); return ; } return ; } + +function getTheme() { + return headers().get(MiddlewareHeaders.Theme) || 'light'; +} diff --git a/packages/gitbook/src/components/DocumentView/CodeBlock/CodeBlockRenderer.tsx b/packages/gitbook/src/components/DocumentView/CodeBlock/CodeBlockRenderer.tsx index e2279f744..fdb73fb7f 100644 --- a/packages/gitbook/src/components/DocumentView/CodeBlock/CodeBlockRenderer.tsx +++ b/packages/gitbook/src/components/DocumentView/CodeBlock/CodeBlockRenderer.tsx @@ -9,7 +9,6 @@ import type { BlockProps } from '../Block'; import { CopyCodeButton } from './CopyCodeButton'; import type { HighlightLine, HighlightToken } from './highlight'; -import './theme.css'; import './CodeBlockRenderer.css'; type CodeBlockRendererProps = Pick, 'block' | 'style'> & { diff --git a/packages/gitbook/src/components/DocumentView/CodeBlock/highlight.ts b/packages/gitbook/src/components/DocumentView/CodeBlock/highlight.ts index eb02b5b92..19532ecab 100644 --- a/packages/gitbook/src/components/DocumentView/CodeBlock/highlight.ts +++ b/packages/gitbook/src/components/DocumentView/CodeBlock/highlight.ts @@ -3,15 +3,11 @@ import type { DocumentBlockCodeLine, DocumentInlineAnnotation, } from '@gitbook/api'; -import { - type ThemedToken, - createCssVariablesTheme, - createSingletonShorthands, - createdBundledHighlighter, -} from 'shiki/core'; +import { type ThemedToken, createSingletonShorthands, createdBundledHighlighter } from 'shiki/core'; import { createJavaScriptRegexEngine } from 'shiki/engine/javascript'; import { type BundledLanguage, bundledLanguages } from 'shiki/langs'; - +import githubDarkDefault from 'shiki/themes/github-dark-default.mjs'; +import minLight from 'shiki/themes/min-light.mjs'; import { plainHighlight } from './plain-highlight'; export type HighlightLine = { @@ -33,12 +29,15 @@ export type RenderedInline = { body: React.ReactNode; }; -const theme = createCssVariablesTheme(); +const themes = { + light: minLight, + dark: githubDarkDefault, +}; const { getSingletonHighlighter } = createSingletonShorthands( createdBundledHighlighter({ langs: bundledLanguages, - themes: {}, + themes: themes, engine: () => createJavaScriptRegexEngine({ forgiving: true, target: 'ES2018' }), }) ); @@ -51,7 +50,7 @@ export async function preloadHighlight(block: DocumentBlockCode) { if (langName) { await getSingletonHighlighter({ langs: [langName], - themes: [theme], + themes: Object.values(themes), }); } } @@ -61,9 +60,11 @@ export async function preloadHighlight(block: DocumentBlockCode) { */ export async function highlight( block: DocumentBlockCode, - inlines: RenderedInline[] + inlines: RenderedInline[], + theme?: string ): Promise { const langName = getBlockLang(block); + if (!langName) { // Language not found, fallback to plain highlighting return plainHighlight(block, inlines); @@ -73,12 +74,19 @@ export async function highlight( const highlighter = await getSingletonHighlighter({ langs: [langName], - themes: [theme], + themes: Object.values(themes), }); + const selectedTheme = (() => { + if (theme === 'dark') { + return themes.dark; + } + return themes.light; + })(); + const lines = highlighter.codeToTokensBase(code, { lang: langName, - theme, + theme: selectedTheme, tokenizeMaxLineLength: 400, }); diff --git a/packages/gitbook/src/components/DocumentView/CodeBlock/theme.css b/packages/gitbook/src/components/DocumentView/CodeBlock/theme.css deleted file mode 100644 index 22ce3b60e..000000000 --- a/packages/gitbook/src/components/DocumentView/CodeBlock/theme.css +++ /dev/null @@ -1,31 +0,0 @@ -:root { - --shiki-color-text: theme("colors.tint.11"); - --shiki-token-constant: #0a6355; - --shiki-token-string: #8b6d32; - --shiki-token-comment: theme("colors.teal.700/.64"); - --shiki-token-keyword: theme("colors.pomegranate.600"); - --shiki-token-parameter: #0a3069; - --shiki-token-function: #8250df; - --shiki-token-string-expression: #6a4906; - --shiki-token-punctuation: theme("colors.pomegranate.700/.92"); - --shiki-token-link: theme("colors.tint.12"); - --shiki-token-inserted: #22863a; - --shiki-token-deleted: #b31d28; - --shiki-token-changed: #8250df; -} - -html.dark { - --shiki-color-text: theme("colors.tint.11"); - --shiki-token-constant: #d19a66; - --shiki-token-string: theme("colors.pomegranate.300"); - --shiki-token-comment: theme("colors.teal.300/.64"); - --shiki-token-keyword: theme("colors.pomegranate.400"); - --shiki-token-parameter: theme("colors.yellow.500"); - --shiki-token-function: #56b6c2; - --shiki-token-string-expression: theme("colors.tint.11"); - --shiki-token-punctuation: #acc6ee; - --shiki-token-link: theme("colors.pomegranate.400"); - --shiki-token-inserted: #85e89d; - --shiki-token-deleted: #fdaeb7; - --shiki-token-changed: #56b6c2; -} diff --git a/packages/gitbook/src/components/DocumentView/OpenAPI/style.css b/packages/gitbook/src/components/DocumentView/OpenAPI/style.css index babf85071..471666ee1 100644 --- a/packages/gitbook/src/components/DocumentView/OpenAPI/style.css +++ b/packages/gitbook/src/components/DocumentView/OpenAPI/style.css @@ -384,7 +384,7 @@ /* Code Sample */ .openapi-codesample { - @apply border rounded bg-tint border-tint-subtle; + @apply border rounded bg-tint-subtle theme-gradient:bg-transparent border-tint-subtle; } .openapi-codesample-header { @@ -458,11 +458,11 @@ /* Response Example */ .openapi-response-example { - @apply border rounded bg-tint border-tint-subtle; + @apply border rounded bg-tint-subtle theme-gradient:bg-transparent border-tint-subtle; } .openapi-response-example-empty { - @apply relative text-tint bg-tint min-h-20 flex flex-col justify-center items-center; + @apply relative text-tint bg-tint-subtle theme-gradient:bg-transparent min-h-20 flex flex-col justify-center items-center; } /* Common Elements */ @@ -692,3 +692,9 @@ opacity: 0; } } + +.openapi-codesample pre, +.openapi-response-example pre, +.openapi-response-example-empty pre { + @apply !bg-tint-subtle text-[0.8125rem] leading-5; +}