72 lines
2.1 KiB
TypeScript
72 lines
2.1 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { marked, Renderer } from 'marked';
|
|
import { CustomSlugger } from '../utils/markdownUtils.ts';
|
|
|
|
interface TocItem {
|
|
id: string;
|
|
level: number;
|
|
text: string;
|
|
}
|
|
|
|
interface HeadingToken {
|
|
type: 'heading';
|
|
raw: string;
|
|
depth: number;
|
|
text: string;
|
|
}
|
|
|
|
export const useMarkdown = (fileUrl: string) => {
|
|
const [markdownHtml, setMarkdownHtml] = useState('');
|
|
const [toc, setToc] = useState<TocItem[]>([]);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [error, setError] = useState('');
|
|
|
|
useEffect(() => {
|
|
const renderer = new Renderer();
|
|
const rendererSlugger = new CustomSlugger();
|
|
|
|
(renderer as any).heading = (text: string, level: number, raw: string) => {
|
|
let headingContent = text;
|
|
if (typeof text !== 'string') {
|
|
headingContent = marked.parseInline(String(raw || ''), { async: false }) as string;
|
|
}
|
|
const id = rendererSlugger.slug(raw || text);
|
|
return `<h${level} id="${id}" class="scroll-mt-24">${headingContent}</h${level}>`;
|
|
};
|
|
|
|
const fetchAndParseMarkdown = async () => {
|
|
try {
|
|
const response = await fetch(fileUrl);
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to load ${fileUrl}: ${response.statusText}`);
|
|
}
|
|
const text = await response.text();
|
|
|
|
const tokens = marked.lexer(text);
|
|
const tocSlugger = new CustomSlugger();
|
|
const tocItems = tokens
|
|
.filter((token): token is HeadingToken => (token as any).type === 'heading')
|
|
.map((token) => ({
|
|
id: tocSlugger.slug(token.raw || token.text),
|
|
level: token.depth,
|
|
text: token.text,
|
|
}));
|
|
|
|
setToc(tocItems);
|
|
|
|
const html = await marked.parse(text, { renderer, gfm: true });
|
|
setMarkdownHtml(html);
|
|
} catch (err: any) {
|
|
console.error(`Error loading ${fileUrl}:`, err);
|
|
setError(err.message);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
fetchAndParseMarkdown();
|
|
}, [fileUrl]);
|
|
|
|
return { markdownHtml, toc, isLoading, error };
|
|
};
|