traceability
This commit is contained in:
213
src/components/documentation/MarkdownViewer.tsx
Normal file
213
src/components/documentation/MarkdownViewer.tsx
Normal file
@@ -0,0 +1,213 @@
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { MermaidDiagram } from './MermaidDiagram';
|
||||
import { PlantUMLDiagram } from './PlantUMLDiagram';
|
||||
|
||||
interface MarkdownViewerProps {
|
||||
content: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function MarkdownViewer({ content, className = '' }: MarkdownViewerProps) {
|
||||
return (
|
||||
<ScrollArea className={`h-[500px] ${className}`}>
|
||||
<div className="prose prose-sm dark:prose-invert max-w-none p-4">
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
components={{
|
||||
h1: ({ children }) => (
|
||||
<h1 className="text-2xl font-bold text-foreground mb-4 pb-2 border-b border-border">
|
||||
{children}
|
||||
</h1>
|
||||
),
|
||||
h2: ({ children }) => (
|
||||
<h2 className="text-xl font-semibold text-foreground mt-6 mb-3">
|
||||
{children}
|
||||
</h2>
|
||||
),
|
||||
h3: ({ children }) => (
|
||||
<h3 className="text-lg font-medium text-foreground mt-4 mb-2">
|
||||
{children}
|
||||
</h3>
|
||||
),
|
||||
h4: ({ children }) => (
|
||||
<h4 className="text-base font-medium text-foreground mt-3 mb-2">
|
||||
{children}
|
||||
</h4>
|
||||
),
|
||||
p: ({ children }) => (
|
||||
<p className="text-muted-foreground mb-3 leading-relaxed">
|
||||
{children}
|
||||
</p>
|
||||
),
|
||||
ul: ({ children }) => (
|
||||
<ul className="list-disc pl-6 space-y-1 mb-4 text-muted-foreground">
|
||||
{children}
|
||||
</ul>
|
||||
),
|
||||
ol: ({ children }) => (
|
||||
<ol className="list-decimal pl-6 space-y-1 mb-4 text-muted-foreground">
|
||||
{children}
|
||||
</ol>
|
||||
),
|
||||
li: ({ children }) => (
|
||||
<li className="text-muted-foreground pl-1">{children}</li>
|
||||
),
|
||||
code: ({ className, children, ...props }) => {
|
||||
const match = /language-(\w+)/.exec(className || '');
|
||||
const language = match ? match[1] : '';
|
||||
const codeContent = String(children).replace(/\n$/, '');
|
||||
|
||||
// Handle Mermaid diagrams
|
||||
if (language === 'mermaid') {
|
||||
return <MermaidDiagram chart={codeContent} />;
|
||||
}
|
||||
|
||||
// Handle PlantUML diagrams
|
||||
if (language === 'plantuml' || language === 'puml') {
|
||||
return <PlantUMLDiagram code={codeContent} />;
|
||||
}
|
||||
|
||||
// Inline code (no language specified and short)
|
||||
const isInline = !className && !String(children).includes('\n');
|
||||
if (isInline) {
|
||||
return (
|
||||
<code className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono text-foreground" {...props}>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
}
|
||||
|
||||
// Code block
|
||||
return (
|
||||
<code className="block bg-muted p-4 rounded-lg text-sm font-mono overflow-x-auto mb-4" {...props}>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
},
|
||||
pre: ({ children, ...props }) => {
|
||||
// Check if the child is a Mermaid or PlantUML diagram (already rendered)
|
||||
const childElement = children as React.ReactElement;
|
||||
if (childElement?.type === MermaidDiagram || childElement?.type === PlantUMLDiagram) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
return (
|
||||
<pre className="bg-muted p-4 rounded-lg overflow-x-auto mb-4" {...props}>
|
||||
{children}
|
||||
</pre>
|
||||
);
|
||||
},
|
||||
blockquote: ({ children }) => (
|
||||
<blockquote className="border-l-4 border-primary pl-4 italic text-muted-foreground my-4">
|
||||
{children}
|
||||
</blockquote>
|
||||
),
|
||||
// Enhanced table support for HTML tables
|
||||
table: ({ children }) => (
|
||||
<div className="overflow-x-auto mb-4">
|
||||
<table className="min-w-full border-collapse border border-border rounded-lg">
|
||||
{children}
|
||||
</table>
|
||||
</div>
|
||||
),
|
||||
thead: ({ children }) => (
|
||||
<thead className="bg-muted">{children}</thead>
|
||||
),
|
||||
tbody: ({ children }) => (
|
||||
<tbody className="divide-y divide-border">{children}</tbody>
|
||||
),
|
||||
tr: ({ children }) => (
|
||||
<tr className="hover:bg-muted/50 transition-colors">{children}</tr>
|
||||
),
|
||||
th: ({ children, style }) => (
|
||||
<th
|
||||
className="px-4 py-2 text-left font-medium text-foreground border border-border bg-muted"
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
</th>
|
||||
),
|
||||
td: ({ children, style }) => (
|
||||
<td
|
||||
className="px-4 py-2 text-muted-foreground border border-border"
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
</td>
|
||||
),
|
||||
hr: () => <hr className="my-6 border-border" />,
|
||||
a: ({ href, children }) => (
|
||||
<a
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-primary hover:underline"
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
strong: ({ children }) => (
|
||||
<strong className="font-semibold text-foreground">{children}</strong>
|
||||
),
|
||||
em: ({ children }) => (
|
||||
<em className="italic">{children}</em>
|
||||
),
|
||||
// Support for definition lists (HTML)
|
||||
dl: ({ children }) => (
|
||||
<dl className="mb-4 space-y-2">{children}</dl>
|
||||
),
|
||||
dt: ({ children }) => (
|
||||
<dt className="font-medium text-foreground">{children}</dt>
|
||||
),
|
||||
dd: ({ children }) => (
|
||||
<dd className="ml-4 text-muted-foreground">{children}</dd>
|
||||
),
|
||||
// Support for figures and captions
|
||||
figure: ({ children }) => (
|
||||
<figure className="my-4">{children}</figure>
|
||||
),
|
||||
figcaption: ({ children }) => (
|
||||
<figcaption className="text-center text-sm text-muted-foreground mt-2">
|
||||
{children}
|
||||
</figcaption>
|
||||
),
|
||||
// Support for images
|
||||
img: ({ src, alt, ...props }) => (
|
||||
<img
|
||||
src={src}
|
||||
alt={alt || ''}
|
||||
className="max-w-full h-auto rounded-lg my-4"
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
// Support for details/summary
|
||||
details: ({ children }) => (
|
||||
<details className="my-4 border border-border rounded-lg p-4 bg-card">
|
||||
{children}
|
||||
</details>
|
||||
),
|
||||
summary: ({ children }) => (
|
||||
<summary className="font-medium cursor-pointer text-foreground hover:text-primary">
|
||||
{children}
|
||||
</summary>
|
||||
),
|
||||
// Div support for custom HTML blocks
|
||||
div: ({ className, children, ...props }) => (
|
||||
<div className={className} {...props}>{children}</div>
|
||||
),
|
||||
// Span support
|
||||
span: ({ className, children, style, ...props }) => (
|
||||
<span className={className} style={style} {...props}>{children}</span>
|
||||
),
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user