diff --git a/client/src/components/ui/MarkdownViewer.tsx b/client/src/components/ui/MarkdownViewer.tsx
new file mode 100644
index 0000000..2cb99c4
--- /dev/null
+++ b/client/src/components/ui/MarkdownViewer.tsx
@@ -0,0 +1,139 @@
+import ReactMarkdown from 'react-markdown';
+import remarkGfm from 'remark-gfm';
+import type { Components } from 'react-markdown';
+
+const components: Components = {
+ // ── Headings ────────────────────────────────────────────────────────────────
+ h1: ({ children }) => (
+
+ {children}
+
+ ),
+ h2: ({ children }) => (
+
+
+ {children}
+
+ ),
+ h3: ({ children }) => (
+ {children}
+ ),
+ h4: ({ children }) => (
+ {children}
+ ),
+
+ // ── Paragraph ───────────────────────────────────────────────────────────────
+ p: ({ children }) => (
+ {children}
+ ),
+
+ // ── Links ───────────────────────────────────────────────────────────────────
+ a: ({ href, children }) => (
+
+ {children}
+
+ ),
+
+ // ── Strong / Em ─────────────────────────────────────────────────────────────
+ strong: ({ children }) => (
+ {children}
+ ),
+ em: ({ children }) => (
+ {children}
+ ),
+
+ // ── Horizontal rule ─────────────────────────────────────────────────────────
+ hr: () =>
,
+
+ // ── Blockquote ──────────────────────────────────────────────────────────────
+ blockquote: ({ children }) => (
+
+ {children}
+
+ ),
+
+ // ── Lists ───────────────────────────────────────────────────────────────────
+ ul: ({ children }) => (
+
+ ),
+ ol: ({ children }) => (
+ {children}
+ ),
+ li: ({ children }) => (
+
+
+ {children}
+
+ ),
+
+ // ── Inline code ─────────────────────────────────────────────────────────────
+ code: ({ className, children, ...props }) => {
+ const isBlock = Boolean(className);
+ if (isBlock) return {children};
+ return (
+
+ {children}
+
+ );
+ },
+
+ // ── Code block ──────────────────────────────────────────────────────────────
+ pre: ({ children }) => (
+
+
+
+ {children}
+
+
+ ),
+
+ // ── Tables ──────────────────────────────────────────────────────────────────
+ table: ({ children }) => (
+
+ ),
+ thead: ({ children }) => (
+ {children}
+ ),
+ tbody: ({ children }) => (
+ {children}
+ ),
+ tr: ({ children }) => (
+ {children}
+ ),
+ th: ({ children }) => (
+
+ {children}
+ |
+ ),
+ td: ({ children }) => (
+ {children} |
+ ),
+};
+
+interface Props {
+ content: string;
+}
+
+export default function MarkdownViewer({ content }: Props) {
+ return (
+
+
+ {content}
+
+
+ );
+}
diff --git a/client/src/pages/ProjectDetail.tsx b/client/src/pages/ProjectDetail.tsx
index e59be9c..7414322 100644
--- a/client/src/pages/ProjectDetail.tsx
+++ b/client/src/pages/ProjectDetail.tsx
@@ -1,7 +1,6 @@
import { useState, useEffect, useRef } from 'react';
import { useParams, useNavigate, Link } from 'react-router-dom';
-import ReactMarkdown from 'react-markdown';
-import remarkGfm from 'remark-gfm';
+import MarkdownViewer from '../components/ui/MarkdownViewer';
import {
ArrowLeft, ExternalLink, Pencil, Trash2, Upload, FileText,
X, Save, FolderOpen, Eye, Code2
@@ -244,18 +243,7 @@ export default function ProjectDetail() {
{docViewMode === 'preview' ? (
-
- {docContent}
-
+
) : (
{docContent}
)}