diff --git a/src/components/project/MarkdownContent.tsx b/src/components/project/MarkdownContent.tsx index 2f55ea6..9ca261a 100644 --- a/src/components/project/MarkdownContent.tsx +++ b/src/components/project/MarkdownContent.tsx @@ -1,10 +1,8 @@ -'use client' - +import React from 'react' import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' import rehypeRaw from 'rehype-raw' import rehypeSanitize from 'rehype-sanitize' -import { useState, useEffect } from 'react' import type { Components } from 'react-markdown' interface MarkdownContentProps { @@ -25,6 +23,14 @@ function generateHeadingId(text: string): string { .replace(/-+$/, '') // Trim - from end } +// Helper function to escape HTML +function escapeHtml(code: string): string { + return code + .replace(/&/g, '&') + .replace(//g, '>') +} + // Custom GitHub-style components const components: Components = { // Headings with anchor links @@ -81,12 +87,26 @@ const components: Components = { ), - // Paragraphs - p: ({ children, ...props }) => ( -
- {children} -
- ), + // Paragraphs - skip wrapping if contains block-level elements like pre + p: ({ children, node, ...props }: any) => { + // Check if the paragraph node contains block-level elements in its children + // This uses the AST node data from react-markdown + const hasBlockElement = node?.children?.some((child: any) => { + const tagName = child?.tagName + // Check if any direct child is a block-level element + return ['pre', 'table', 'blockquote', 'ul', 'ol', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'img', 'hr'].includes(tagName) + }) + + if (hasBlockElement) { + return <>{children}> + } + + return ( ++ {children} +
+ ) + }, // Lists ul: ({ children, className, ...props }) => ( @@ -111,9 +131,24 @@ const components: Components = { ), - // Code blocks with syntax highlighting + // Code blocks (pre element wrapper) + pre: ({ children, ...props }: any) => { + return ( +
+ {children}
+
+ )
+ },
+
+ // Code elements (both inline and in code blocks)
code: ({ inline, className, children, ...props }: any) => {
- if (inline) {
+ // If inline is explicitly true, or if there's no language class and no newlines, treat as inline
+ const hasLanguageClass = className && typeof className === 'string' && className.startsWith('language-')
+ const childStr = String(children)
+ const hasNewlines = childStr.includes('\n')
+ const isInline = inline === true || (!hasLanguageClass && !hasNewlines)
+
+ if (isInline) {
return (
+
+ {children}
+
)
},
@@ -202,44 +238,12 @@ const components: Components = {
checked={checked}
readOnly
className="mr-2 h-4 w-4 rounded border-gray-300"
+ name="task-item"
{...props}
/>
) : null,
}
-// Syntax highlighter wrapper (will use Shiki for server-side, Prism for client)
-function SyntaxHighlighterWrapper({ language, code }: { language: string; code: string }) {
- const [highlighted, setHighlighted] = useState
- {code}
-
- )
- }
-
- return (
-
-
-
- )
-}
-
export function MarkdownContent({ content, className = '' }: MarkdownContentProps) {
if (!content || content.trim() === '') {