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 }) => ( +
    + {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}
    )}