Files
Frontend/src/app/components/code-highlighter.tsx
2024-04-23 17:52:37 +01:00

93 lines
2.4 KiB
TypeScript

import { ReactElement } from "react";
import SyntaxHighlighter from "react-syntax-highlighter";
import createElement from "react-syntax-highlighter/dist/esm/create-element";
import { atomOneDark } from "react-syntax-highlighter/dist/esm/styles/hljs";
import { cn } from "@/app/common/utils";
import { capitalizeFirstLetter } from "@/app/common/string-utils";
type CodeHighlighterProps = {
/**
* The code to highlight.
*/
code: string;
/**
* The language of the code.
*/
language?: string;
/**
* Should the element be rounded?
*/
rounded?: boolean;
};
/**
* Render the rows with the ability to render links.
*
* @param rows the rows to render
* @param stylesheet the stylesheet to use
* @param useInlineStyles should inline styles be used
* @returns the rendered rows
*/
function rowRenderer({
rows,
stylesheet,
useInlineStyles,
}: {
rows: any;
stylesheet: { [key: string]: React.CSSProperties };
useInlineStyles: boolean;
}) {
return rows.map((node: any, i: number) => {
node.children = node.children.map((children: any) => {
const text = children?.children?.[0]?.value;
if (typeof text === "string" && text.startsWith('"http')) {
return {
...children,
tagName: "a",
properties: {
...children.properties,
href: text.slice(1, -1), // in JSON strings are enclosed with ", they need to be removed
target: "_blank",
// Tailwind CSS classes
class: "underline !text-primary hover:!text-muted-foreground transition-all",
},
};
}
return children;
});
return createElement({
node,
stylesheet,
useInlineStyles,
key: `code-segement${i}`,
});
});
}
export function CodeHighlighter({ code, language = "json", rounded = true }: CodeHighlighterProps): ReactElement {
return (
<div className="text-xs md:text-md relative">
{/* Language */}
<div className="absolute top-0 right-0 p-1 bg-muted rounded-bl-md rounded-tr-md">
<span className="text-xs text-muted-foreground">{capitalizeFirstLetter(language)}</span>
</div>
{/* Code */}
<SyntaxHighlighter
className={cn("max-h-[600px] !bg-secondary break-all rounded-md", rounded && "rounded-md")}
language={language}
style={atomOneDark}
wrapLongLines
renderer={rowRenderer}
>
{code}
</SyntaxHighlighter>
</div>
);
}