Blog markdown
This commit is contained in:
Generated
+2032
-3
File diff suppressed because it is too large
Load Diff
+12
-1
@@ -9,10 +9,21 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"framer-motion": "^12.23.24",
|
"framer-motion": "^12.23.24",
|
||||||
|
"highlight.js": "^11.11.1",
|
||||||
|
"katex": "^0.16.25",
|
||||||
"lucide-react": "^0.555.0",
|
"lucide-react": "^0.555.0",
|
||||||
"next": "16.0.6",
|
"next": "16.0.6",
|
||||||
"react": "19.2.0",
|
"react": "19.2.0",
|
||||||
"react-dom": "19.2.0"
|
"react-dom": "19.2.0",
|
||||||
|
"react-markdown": "^10.1.0",
|
||||||
|
"rehype-autolink-headings": "^7.1.0",
|
||||||
|
"rehype-highlight": "^7.0.2",
|
||||||
|
"rehype-katex": "^7.0.1",
|
||||||
|
"rehype-raw": "^7.0.0",
|
||||||
|
"rehype-slug": "^6.0.0",
|
||||||
|
"remark-breaks": "^4.0.0",
|
||||||
|
"remark-gfm": "^4.0.1",
|
||||||
|
"remark-math": "^6.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { Sparkles } from "lucide-react";
|
|||||||
import WebStuff from "@/components/Web";
|
import WebStuff from "@/components/Web";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { PostPreview } from "@/lib/posts";
|
import { PostPreview } from "@/lib/posts";
|
||||||
|
import Markdown from "@/components/Markdown";
|
||||||
|
|
||||||
export default function BlogPost() {
|
export default function BlogPost() {
|
||||||
const [post, setPost] = useState<{
|
const [post, setPost] = useState<{
|
||||||
@@ -112,11 +113,12 @@ export default function BlogPost() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Post Content */}
|
{/* Post Content */}
|
||||||
<motion.article
|
<motion.section
|
||||||
variants={itemVariants}
|
variants={itemVariants}
|
||||||
className="prose prose-invert max-w-none text-slate-300"
|
className="prose prose-invert max-w-none text-slate-300"
|
||||||
dangerouslySetInnerHTML={{ __html: post.content }}
|
>
|
||||||
/>
|
<Markdown content={post.content} />
|
||||||
|
</motion.section>
|
||||||
</motion.section>
|
</motion.section>
|
||||||
</WebStuff>
|
</WebStuff>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
import remarkGfm from "remark-gfm";
|
||||||
|
import remarkMath from "remark-math";
|
||||||
|
import rehypeKatex from "rehype-katex";
|
||||||
|
import rehypeHighlight from "rehype-highlight";
|
||||||
|
import rehypeSlug from "rehype-slug";
|
||||||
|
import rehypeAutolinkHeadings from "rehype-autolink-headings";
|
||||||
|
import remarkBreaks from "remark-breaks";
|
||||||
|
import rehypeRaw from "rehype-raw";
|
||||||
|
import "katex/dist/katex.min.css";
|
||||||
|
import "highlight.js/styles/github-dark.css";
|
||||||
|
import "highlight.js/styles/github.css";
|
||||||
|
import { CopyIcon } from "lucide-react";
|
||||||
|
|
||||||
|
interface MarkdownProps {
|
||||||
|
content: string;
|
||||||
|
components?: Record<string, React.ElementType>;
|
||||||
|
darkMode?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Markdown: React.FC<MarkdownProps> = ({
|
||||||
|
content,
|
||||||
|
components,
|
||||||
|
darkMode = true,
|
||||||
|
}) => {
|
||||||
|
// Copy to clipboard handler
|
||||||
|
const copyToClipboard = (text: string) => {
|
||||||
|
navigator.clipboard.writeText(text);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={darkMode ? "prose prose-invert" : "prose"}>
|
||||||
|
<ReactMarkdown
|
||||||
|
remarkPlugins={[remarkGfm, remarkMath]}
|
||||||
|
rehypePlugins={[
|
||||||
|
rehypeKatex,
|
||||||
|
rehypeHighlight,
|
||||||
|
rehypeSlug,
|
||||||
|
rehypeAutolinkHeadings,
|
||||||
|
rehypeRaw,
|
||||||
|
remarkBreaks,
|
||||||
|
]}
|
||||||
|
components={{
|
||||||
|
h1: ({ node, ...props }) => (
|
||||||
|
<h1 className="text-4xl font-bold my-6" {...props} />
|
||||||
|
),
|
||||||
|
h2: ({ node, ...props }) => (
|
||||||
|
<h2 className="text-3xl font-semibold my-5" {...props} />
|
||||||
|
),
|
||||||
|
h3: ({ node, ...props }) => (
|
||||||
|
<h3 className="text-2xl font-semibold my-4" {...props} />
|
||||||
|
),
|
||||||
|
a: ({ href, children, ...props }) => (
|
||||||
|
<a
|
||||||
|
href={href}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-purple-400 hover:underline"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</a>
|
||||||
|
),
|
||||||
|
code: ({ className, children, ...props }) => {
|
||||||
|
const language = className?.replace("language-", "") || "";
|
||||||
|
return (
|
||||||
|
<div className="relative group my-4">
|
||||||
|
<pre
|
||||||
|
className={`rounded-lg p-4 overflow-x-auto ${
|
||||||
|
darkMode ? "bg-gray-900" : "bg-gray-100"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<code className={className} {...props}>
|
||||||
|
{children}
|
||||||
|
</code>
|
||||||
|
</pre>
|
||||||
|
<button
|
||||||
|
onClick={() => copyToClipboard(String(children))}
|
||||||
|
className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity bg-gray-700 text-white px-2 py-1 text-sm rounded"
|
||||||
|
>
|
||||||
|
Copy
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
details: ({ node, ...props }) => (
|
||||||
|
<details
|
||||||
|
className="my-4 p-2 border rounded bg-gray-50 dark:bg-gray-800"
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
summary: ({ node, ...props }) => (
|
||||||
|
<summary className="font-semibold cursor-pointer" {...props} />
|
||||||
|
),
|
||||||
|
ul: ({ ...props }) => (
|
||||||
|
<ul {...props} className="list-disc list-inside my-1.5" />
|
||||||
|
),
|
||||||
|
ol: ({ ...props }) => (
|
||||||
|
<ol {...props} className="list-decimal list-inside my-1.5" />
|
||||||
|
),
|
||||||
|
hr: ({ ...props }) => <hr {...props} className="my-4" />,
|
||||||
|
...components,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Markdown;
|
||||||
Reference in New Issue
Block a user