mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-26 06:31:49 +00:00
Core & UI: Support line numbers in codeblock
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'fumadocs-twoslash': patch
|
||||
'fumadocs-core': patch
|
||||
'fumadocs-ui': patch
|
||||
---
|
||||
|
||||
Support line numbers in codeblock
|
||||
@@ -218,7 +218,7 @@ console.log('Hello World');
|
||||
|
||||
We support some of the [Shiki Transformers](https://shiki.style/packages/transformers), allowing you to highlight/style specific lines.
|
||||
|
||||
````md tab="Input"
|
||||
````md tab="Input" lineNumbers
|
||||
```tsx
|
||||
// highlight a line
|
||||
<div>Hello World</div> // [\!code highlight]
|
||||
@@ -233,7 +233,7 @@ console.log('hello'); // [\!code ++]
|
||||
```
|
||||
````
|
||||
|
||||
```tsx tab="Output"
|
||||
```tsx tab="Output" lineNumbers
|
||||
// highlight a line
|
||||
<div>Hello World</div> // [!code highlight]
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ console.log('Hello World');
|
||||
|
||||
Learn more about [Twoslash notations](https://twoslash.netlify.app/refs/notations).
|
||||
|
||||
```ts twoslash title="Test"
|
||||
```ts twoslash title="Test" lineNumbers
|
||||
type Player = {
|
||||
/**
|
||||
* The player name
|
||||
|
||||
@@ -17,10 +17,17 @@ import type { CodeBlockIcon, IconOptions } from './transformer-icon';
|
||||
import { transformerIcon } from './transformer-icon';
|
||||
import { defaultThemes, getHighlighter } from '@/highlight/shiki';
|
||||
|
||||
interface MetaValue {
|
||||
name: string;
|
||||
regex: RegExp;
|
||||
}
|
||||
type Meta = Record<string, unknown>;
|
||||
|
||||
type MetaValue =
|
||||
| {
|
||||
name: string;
|
||||
regex: RegExp;
|
||||
}
|
||||
| {
|
||||
regex: RegExp;
|
||||
onSet: (map: Meta, match: string[]) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom meta string values
|
||||
@@ -38,6 +45,14 @@ const metaValues: MetaValue[] = [
|
||||
name: 'tab',
|
||||
regex: /tab="(?<value>[^"]+)"/,
|
||||
},
|
||||
{
|
||||
regex: /lineNumbers=(\d+)|lineNumbers/,
|
||||
onSet(map, args) {
|
||||
map['data-line-numbers'] = true;
|
||||
if (args[0] !== undefined)
|
||||
map['data-line-numbers-start'] = Number(args[0]);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const rehypeCodeDefaultOptions: RehypeCodeOptions = {
|
||||
@@ -58,12 +73,16 @@ export const rehypeCodeDefaultOptions: RehypeCodeOptions = {
|
||||
}),
|
||||
],
|
||||
parseMetaString(meta) {
|
||||
const map: Record<string, string> = {};
|
||||
const map: Meta = {};
|
||||
|
||||
for (const value of metaValues) {
|
||||
meta = meta.replace(value.regex, (_, ...args) => {
|
||||
const first = args.at(0);
|
||||
map[value.name] = typeof first === 'string' ? first : '';
|
||||
if ('onSet' in value) {
|
||||
value.onSet(map, args);
|
||||
} else {
|
||||
const first = args.at(0);
|
||||
map[value.name] = typeof first === 'string' ? first : '';
|
||||
}
|
||||
|
||||
return '';
|
||||
});
|
||||
|
||||
@@ -10,10 +10,6 @@
|
||||
--color-twoslash-tag-annotate: #1ba67320;
|
||||
}
|
||||
|
||||
.twoslash .line {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.twoslash-meta-line,
|
||||
.twoslash-completion-list {
|
||||
position: relative;
|
||||
|
||||
@@ -3,9 +3,26 @@
|
||||
color: var(--shiki-light);
|
||||
}
|
||||
|
||||
code .line {
|
||||
min-height: 1em;
|
||||
@apply relative inline-block;
|
||||
}
|
||||
|
||||
&[data-line-numbers] code .line {
|
||||
width: calc(100% - 6 * var(--spacing));
|
||||
counter-increment: line;
|
||||
@apply ml-6;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
content: counter(line);
|
||||
@apply top-0 -left-4 text-fd-muted-foreground/60;
|
||||
}
|
||||
}
|
||||
|
||||
code .diff::before {
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
left: var(--spacing);
|
||||
}
|
||||
|
||||
code .diff.remove {
|
||||
@@ -28,15 +45,11 @@
|
||||
}
|
||||
|
||||
code .diff {
|
||||
width: calc(100% + 8 * var(--spacing));
|
||||
|
||||
@apply relative inline-block -mx-4 px-4;
|
||||
@apply relative;
|
||||
}
|
||||
|
||||
.highlighted {
|
||||
width: calc(100% + 8 * var(--spacing));
|
||||
|
||||
@apply inline-block -mx-4 px-4 bg-fd-primary/10;
|
||||
@apply bg-fd-primary/10;
|
||||
}
|
||||
|
||||
.highlighted-word {
|
||||
|
||||
@@ -40,6 +40,16 @@ export type CodeBlockProps = HTMLAttributes<HTMLElement> & {
|
||||
keepBackground?: boolean;
|
||||
|
||||
viewportProps?: ScrollAreaViewportProps;
|
||||
|
||||
/**
|
||||
* show line numbers
|
||||
*/
|
||||
'data-line-numbers'?: boolean;
|
||||
|
||||
/**
|
||||
* @defaultValue 1
|
||||
*/
|
||||
'data-line-numbers-start'?: number;
|
||||
};
|
||||
|
||||
export const Pre = forwardRef<HTMLPreElement, HTMLAttributes<HTMLPreElement>>(
|
||||
@@ -47,7 +57,7 @@ export const Pre = forwardRef<HTMLPreElement, HTMLAttributes<HTMLPreElement>>(
|
||||
return (
|
||||
<pre
|
||||
ref={ref}
|
||||
className={cn('text-[13px] p-4 *:block *:w-full', className)}
|
||||
className={cn('size-full *:block *:w-full', className)}
|
||||
{...props}
|
||||
>
|
||||
{props.children}
|
||||
@@ -66,6 +76,7 @@ export const CodeBlock = forwardRef<HTMLElement, CodeBlockProps>(
|
||||
keepBackground = false,
|
||||
icon,
|
||||
viewportProps,
|
||||
children,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
@@ -127,9 +138,19 @@ export const CodeBlock = forwardRef<HTMLElement, CodeBlockProps>(
|
||||
<ScrollArea ref={areaRef} dir="ltr">
|
||||
<ScrollViewport
|
||||
{...viewportProps}
|
||||
className={cn('max-h-[600px]', viewportProps?.className)}
|
||||
className={cn(
|
||||
'text-[13px] py-3.5 [&_.line]:px-4 max-h-[600px]',
|
||||
props['data-line-numbers'] && '[&_.line]:pl-3',
|
||||
viewportProps?.className,
|
||||
)}
|
||||
style={{
|
||||
counterSet: props['data-line-numbers']
|
||||
? `line ${Number(props['data-line-numbers-start'] ?? 1) - 1}`
|
||||
: undefined,
|
||||
...viewportProps?.style,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
{children}
|
||||
</ScrollViewport>
|
||||
<ScrollBar orientation="horizontal" />
|
||||
</ScrollArea>
|
||||
|
||||
Reference in New Issue
Block a user