mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-18 00:25:07 +00:00
Improve codeblock highlight style
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'gitbook': patch
|
||||
---
|
||||
|
||||
Improve codeblock highlight style
|
||||
@@ -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 | HighlightLine[]>(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 (
|
||||
<CodeBlockRenderer ref={blockRef} block={block} style={style} lines={lines ?? plainLines} />
|
||||
|
||||
@@ -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<DocumentBlockCode>) {
|
||||
|
||||
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 <CodeBlockRenderer block={block} style={style} lines={lines} />;
|
||||
}
|
||||
|
||||
return <ClientCodeBlock block={block} style={style} inlines={richInlines} />;
|
||||
}
|
||||
|
||||
function getTheme() {
|
||||
return headers().get(MiddlewareHeaders.Theme) || 'light';
|
||||
}
|
||||
|
||||
@@ -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<BlockProps<DocumentBlockCode>, 'block' | 'style'> & {
|
||||
|
||||
@@ -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<any, any>({
|
||||
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<HighlightLine[]> {
|
||||
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,
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user