1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69import { useEffect, useState } from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import rehypeRaw from 'rehype-raw';
import rehypeHighlight from 'rehype-highlight';
interface MarkdownPageProps {
filePath: string;
}
export const MarkdownPage = ({ filePath }: MarkdownPageProps) => {
const [content, setContent] = useState<string>('');
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
setLoading(true);
setError(null);
const modules = import.meta.glob('../content/*.md', { query: '?raw', import: 'default' });
const key = `../content/${filePath}.md`;
if (modules[key]) {
(modules[key]() as Promise<string>)
.then(text => {
setContent(text);
setLoading(false);
})
.catch(() => {
setError('Failed to load document.');
setLoading(false);
});
} else {
setError(`Document "${filePath}" not found.`);
setLoading(false);
}
}, [filePath]);
if (loading) {
return (
<div className="animate-pulse space-y-4">
<div className="h-8 bg-gray-200 rounded w-3/4"></div>
<div className="h-4 bg-gray-200 rounded w-full"></div>
<div className="h-4 bg-gray-200 rounded w-5/6"></div>
<div className="h-4 bg-gray-200 rounded w-4/6"></div>
</div>
);
}
if (error) {
return (
<div className="text-center py-12">
<p className="text-gray-500 text-lg">{error}</p>
</div>
);
}
return (
<article className="prose prose-lg max-w-none prose-headings:text-uic-blue prose-a:text-uic-red prose-a:no-underline hover:prose-a:underline prose-code:text-sm prose-table:border-collapse prose-th:bg-gray-100 prose-th:border prose-th:border-gray-300 prose-th:px-3 prose-th:py-2 prose-td:border prose-td:border-gray-300 prose-td:px-3 prose-td:py-2">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw, rehypeHighlight]}
>
{content}
</ReactMarkdown>
</article>
);
};