From aa91f165256d6a7d8e2a11e7d572df9ccf1c24ea Mon Sep 17 00:00:00 2001
From: bored-arvi
Date: Fri, 20 Mar 2026 22:19:19 +0530
Subject: [PATCH 1/2] Add react-markdown dependency to package.json and
package-lock.json
---
package-lock.json | 49 ++++++++++++++++++++++++++++++++++++-----------
package.json | 3 ++-
2 files changed, 40 insertions(+), 12 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index ed03548..41e7f5a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,6 +16,7 @@
"next-mdx-remote": "^5.0.0",
"react": "^19",
"react-dom": "^19",
+ "react-markdown": "^9.0.1",
"serve": "^14.2.4",
"tailwind-merge": "^2.5.5",
"tailwindcss-animate": "^1.0.7"
@@ -1021,7 +1022,6 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz",
"integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==",
"license": "MIT",
- "peer": true,
"dependencies": {
"csstype": "^3.0.2"
}
@@ -1088,7 +1088,6 @@
"integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.36.0",
"@typescript-eslint/types": "8.36.0",
@@ -1642,7 +1641,6 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -3023,7 +3021,6 @@
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
@@ -3193,7 +3190,6 @@
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.9",
@@ -4199,6 +4195,16 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/html-url-attributes": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
+ "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/human-signals": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
@@ -6579,7 +6585,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -6768,7 +6773,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -6778,7 +6782,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
"license": "MIT",
- "peer": true,
"dependencies": {
"scheduler": "^0.26.0"
},
@@ -6793,6 +6796,33 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/react-markdown": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.1.0.tgz",
+ "integrity": "sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "hast-util-to-jsx-runtime": "^2.0.0",
+ "html-url-attributes": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-rehype": "^11.0.0",
+ "unified": "^11.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ },
+ "peerDependencies": {
+ "@types/react": ">=18",
+ "react": ">=18"
+ }
+ },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -7968,7 +7998,6 @@
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
"integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
@@ -8139,7 +8168,6 @@
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -8327,7 +8355,6 @@
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
diff --git a/package.json b/package.json
index 3044b1a..1dddcdb 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,8 @@
"react-dom": "^19",
"serve": "^14.2.4",
"tailwind-merge": "^2.5.5",
- "tailwindcss-animate": "^1.0.7"
+ "tailwindcss-animate": "^1.0.7",
+ "react-markdown": "^9.0.1"
},
"devDependencies": {
"@types/node": "^22",
From aededa6c4d394708711ab43dcc5c5e0fd643eb53 Mon Sep 17 00:00:00 2001
From: bored-arvi
Date: Fri, 20 Mar 2026 22:19:33 +0530
Subject: [PATCH 2/2] Refactor MarkdownRenderer to use ReactMarkdown for
enhanced markdown rendering
---
lib/markdown-renderer.tsx | 184 +++++++++++++-------------------------
1 file changed, 62 insertions(+), 122 deletions(-)
diff --git a/lib/markdown-renderer.tsx b/lib/markdown-renderer.tsx
index 5a1d63a..065efb7 100644
--- a/lib/markdown-renderer.tsx
+++ b/lib/markdown-renderer.tsx
@@ -1,128 +1,68 @@
-interface MarkdownRendererProps {
- content: string
-}
+import ReactMarkdown from "react-markdown"
+import type { Components } from "react-markdown"
+import matter from "gray-matter"
-// Conditionally set BASE_PATH based on environment
const BASE_PATH = process.env.NODE_ENV === "production" ? "/StableViewpoints" : ""
-export function MarkdownRenderer({ content }: MarkdownRendererProps) {
- // Enhanced markdown to HTML conversion
- const renderMarkdown = (text: string) => {
- let html = text
-
- // Headers
- html = html.replace(/^### (.*$)/gim, '$1
')
- html = html.replace(/^## (.*$)/gim, '$1
')
- html = html.replace(/^# (.*$)/gim, '$1
')
-
- // Bold and italic
- html = html.replace(/\*\*(.*?)\*\*/g, "$1")
- html = html.replace(/\*(.*?)\*/g, "$1")
-
- // Code blocks
- html = html.replace(
- /```([\s\S]*?)```/g,
- '$1
'
- )
-
- // Inline code
- html = html.replace(/`([^`]+)`/g, '$1')
-
- // Images - handle base path for GitHub Pages
- html = html.replace(
- /!\[(.*?)\]\((.*?)\)/gim,
- (match, alt, src) => {
- // Add base path to image src if it starts with /
- const imageSrc = src.startsWith('/') ? `${BASE_PATH}${src}` : src
- return ``
- }
- )
-
- // Links
- html = html.replace(
- /\[(.*?)\]\((.*?)\)/gim,
- '$1',
- )
-
- // Blockquotes
- html = html.replace(
- /^> (.*$)/gim,
- '$1
',
+const components: Components = {
+ h1: ({ children }) => {children}
,
+ h2: ({ children }) => {children}
,
+ h3: ({ children }) => {children}
,
+ h4: ({ children }) => {children}
,
+
+ p: ({ children }) => {children}
,
+ strong: ({ children }) => {children},
+ em: ({ children }) => {children},
+
+ ul: ({ children }) => ,
+ ol: ({ children }) => {children}
,
+ li: ({ children }) => {children},
+
+ blockquote: ({ children }) => (
+
+ {children}
+
+ ),
+
+ hr: () =>
,
+
+ code: ({ className, children, ...props }) => {
+ const isBlock = Boolean(className)
+ if (isBlock) return {children}
+ return {children}
+ },
+
+ pre: ({ children }) => (
+ {children}
+ ),
+
+ a: ({ href, children }) => (
+
+ {children}
+
+ ),
+
+ img: ({ src, alt }) => {
+ const resolvedSrc = typeof src === "string" && src.startsWith("/") ? `${BASE_PATH}${src}` : src
+ return (
+
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+

+
)
+ },
+}
- // Horizontal rules
- html = html.replace(/^---$/gm, '
')
-
- // Lists - improved list handling
- const lines = html.split("\n")
- let inList = false
- let inOrderedList = false
- const processedLines: string[] = []
-
- for (let i = 0; i < lines.length; i++) {
- const line = lines[i]
- const trimmed = line.trim()
-
- if (trimmed.startsWith("- ")) {
- if (!inList) {
- processedLines.push('')
- inList = true
- }
- processedLines.push(`- ${trimmed.substring(2)}
`)
- } else if (trimmed.match(/^\d+\. /)) {
- if (!inOrderedList) {
- processedLines.push('')
- inOrderedList = true
- }
- processedLines.push(`- ${trimmed.replace(/^\d+\. /, '')}
`)
- } else {
- if (inList) {
- processedLines.push("
")
- inList = false
- }
- if (inOrderedList) {
- processedLines.push("")
- inOrderedList = false
- }
- processedLines.push(line)
- }
- }
-
- if (inList) {
- processedLines.push("")
- }
- if (inOrderedList) {
- processedLines.push("")
- }
-
- html = processedLines.join("\n")
-
- // Paragraphs
- const finalLines = html.split("\n")
- const finalProcessedLines = finalLines.map((line) => {
- const trimmed = line.trim()
- if (trimmed === "") return ""
- if (
- trimmed.startsWith("") ||
- trimmed.startsWith("") ||
- trimmed.startsWith("- ${trimmed}
`
- })
-
- return finalProcessedLines.join("\n")
- }
-
- return
+interface MarkdownRendererProps {
+ content: string
}
+
+export function MarkdownRenderer({ content }: MarkdownRendererProps) {
+ const { content: body } = matter(content)
+
+ return (
+
+ {body}
+
+ )
+}
\ No newline at end of file