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 `
${alt}
` - } - ) - - // 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 */} + {alt +
    ) + }, +} - // 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 = 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