feat: whyrating - initial project from turbostarter boilerplate

This commit is contained in:
Alejandro Gutiérrez
2026-02-04 01:54:52 +01:00
commit 5cdc07cd39
1618 changed files with 338230 additions and 0 deletions

View 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>
);
};

View 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";

View 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);
};