feat: whyrating - initial project from turbostarter boilerplate
This commit is contained in:
45
apps/web/src/modules/common/markdown/code.tsx
Normal file
45
apps/web/src/modules/common/markdown/code.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import { useTheme } from "next-themes";
|
||||
import ShikiHighlighter from "react-shiki";
|
||||
|
||||
import { cn } from "@turbostarter/ui";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import type { Element } from "react-shiki";
|
||||
|
||||
interface CodeHighlightProps {
|
||||
className?: string | undefined;
|
||||
children?: ReactNode | undefined;
|
||||
node?: Element | undefined;
|
||||
inline?: boolean;
|
||||
}
|
||||
|
||||
export const CodeHighlight = ({
|
||||
inline = false,
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: CodeHighlightProps) => {
|
||||
const { resolvedTheme } = useTheme();
|
||||
const match = className?.match(/language-(\w+)/);
|
||||
const language = match ? match[1] : undefined;
|
||||
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
||||
const code = String(children).trim();
|
||||
|
||||
return !inline ? (
|
||||
<ShikiHighlighter
|
||||
language={language}
|
||||
theme={`github-${resolvedTheme === "dark" ? "dark" : "light"}`}
|
||||
{...props}
|
||||
className={cn(
|
||||
"overflow-hidden rounded-none border-y @md:rounded-lg @md:border",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{code}
|
||||
</ShikiHighlighter>
|
||||
) : (
|
||||
<code className={className} {...props}>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
};
|
||||
52
apps/web/src/modules/common/markdown/memoized-markdown.tsx
Normal file
52
apps/web/src/modules/common/markdown/memoized-markdown.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import "katex/dist/katex.min.css";
|
||||
import { marked } from "marked";
|
||||
import { memo, useMemo } from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import { rehypeInlineCodeProperty } from "react-shiki";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import rehypeRaw from "rehype-raw";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
|
||||
import { CodeHighlight } from "./code";
|
||||
import { preprocessMarkdown } from "./utils";
|
||||
|
||||
function parseMarkdownIntoBlocks(markdown: string): string[] {
|
||||
const tokens = marked.lexer(markdown);
|
||||
return tokens.map((token) => token.raw);
|
||||
}
|
||||
|
||||
const MemoizedMarkdownBlock = memo(
|
||||
({ content }: { content: string }) => {
|
||||
const processedContent = preprocessMarkdown(content);
|
||||
return (
|
||||
<ReactMarkdown
|
||||
rehypePlugins={[rehypeRaw, rehypeKatex, rehypeInlineCodeProperty]}
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
components={{
|
||||
code: CodeHighlight,
|
||||
}}
|
||||
>
|
||||
{processedContent}
|
||||
</ReactMarkdown>
|
||||
);
|
||||
},
|
||||
(prevProps, nextProps) => {
|
||||
if (prevProps.content !== nextProps.content) return false;
|
||||
return true;
|
||||
},
|
||||
);
|
||||
|
||||
MemoizedMarkdownBlock.displayName = "MemoizedMarkdownBlock";
|
||||
|
||||
export const MemoizedMarkdown = memo<{ content: string; id: string }>(
|
||||
({ content, id }) => {
|
||||
const blocks = useMemo(() => parseMarkdownIntoBlocks(content), [content]);
|
||||
|
||||
return blocks.map((block, index) => (
|
||||
<MemoizedMarkdownBlock content={block} key={`${id}-block_${index}`} />
|
||||
));
|
||||
},
|
||||
);
|
||||
|
||||
MemoizedMarkdown.displayName = "MemoizedMarkdown";
|
||||
15
apps/web/src/modules/common/markdown/utils/index.ts
Normal file
15
apps/web/src/modules/common/markdown/utils/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export const preprocessLaTeX = (content: string) => {
|
||||
const blockProcessedContent = content.replace(
|
||||
/\\\[([\s\S]*?)\\\]/g,
|
||||
(_, equation) => `$$${equation}$$`,
|
||||
);
|
||||
const inlineProcessedContent = blockProcessedContent.replace(
|
||||
/\\\(([\s\S]*?)\\\)/g,
|
||||
(_, equation) => `$${equation}$`,
|
||||
);
|
||||
return inlineProcessedContent;
|
||||
};
|
||||
|
||||
export const preprocessMarkdown = (markdown: string) => {
|
||||
return preprocessLaTeX(markdown);
|
||||
};
|
||||
Reference in New Issue
Block a user