Spaces:
Sleeping
Sleeping
File size: 1,928 Bytes
05c5ed5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | "use client";
import { toJsxRuntime } from "hast-util-to-jsx-runtime";
import { useTheme } from "next-themes";
import { Fragment, useLayoutEffect, useState } from "react";
import type { JSX, ReactNode } from "react";
import { codeToHast } from "shiki/bundle/web";
import { safe } from "ts-safe";
import { jsx, jsxs } from "react/jsx-runtime";
import { cn } from "lib/utils";
export function CodeBlock({
code,
lang,
fallback,
className,
showLineNumbers = true,
}: {
code?: string;
lang: string;
fallback?: ReactNode;
className?: string;
showLineNumbers?: boolean;
}) {
const { theme } = useTheme();
const [component, setComponent] = useState<JSX.Element | null>(null);
useLayoutEffect(() => {
safe()
.map(async () => {
const out = await codeToHast(code || "", {
lang: lang,
theme: theme == "dark" ? "github-dark" : "github-light",
});
return toJsxRuntime(out, {
Fragment,
jsx,
jsxs,
components: {
pre: (props) => (
<pre
{...props}
lang={lang}
style={undefined}
className={cn(props.className, className)}
>
<div className={cn(showLineNumbers && "pl-12 relative")}>
{showLineNumbers && (
<div className="absolute left-0 top-0 w-6 flex flex-col select-none text-right text-muted-foreground">
{code?.split("\n").map((_, index) => (
<span key={index}>{index + 1}</span>
))}
</div>
)}
{props.children}
</div>
</pre>
),
},
}) as JSX.Element;
})
.ifOk(setComponent);
}, [theme, lang, code]);
if (!code) return fallback;
return component ?? fallback;
}
|