ttrpg-tools/src/components/md-embed.tsx

64 lines
2.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { customElement, noShadowDOM } from "solid-element";
import { createResource, Show, createEffect } from "solid-js";
import { getIndexedData } from "../data-loader/file-index";
import { extractSection } from "../data-loader";
import { parseMarkdown } from "../markdown";
import { resolvePath } from "./utils/path";
import mermaid from "mermaid";
export interface EmbedProps {
/** 是否在嵌入内容的每一级标题前提升 heading 层级 */
headingBase?: number;
}
customElement("md-embed", { headingBase: 0 }, (props, { element }) => {
noShadowDOM();
const rawPath = element?.textContent?.trim() || "";
if (element) element.textContent = "";
// 解析 section支持 path#section 语法)
const hashIndex = rawPath.indexOf("#");
const filePath = hashIndex >= 0 ? rawPath.slice(0, hashIndex) : rawPath;
const section = hashIndex >= 0 ? rawPath.slice(hashIndex + 1) : undefined;
// 从父节点 article 获取路径上下文
const articleEl = element?.closest("article[data-src]") as HTMLElement;
const articlePath = articleEl?.getAttribute("data-src") || "";
const resolvedPath = resolvePath(articlePath, filePath);
const [content] = createResource(
() => ({ path: resolvedPath, section }),
async ({ path, section }) => {
const text = await getIndexedData(path);
return section ? extractSection(text, section) : text;
},
);
createEffect(() => {
if (content() && !content.loading) {
void mermaid.run();
}
});
return (
<div class="md-embed not-prose">
<Show when={content.loading}>
<div class="text-gray-400 italic">...</div>
</Show>
<Show when={content.error}>
<div class="text-red-500">
{content.error?.message || "未知错误"}
</div>
</Show>
<Show when={!content.loading && !content.error && content()}>
{/* biome-ignore lint/security/noDangerouslySetInnerHtml: markdown render */}
<div
class="prose"
innerHTML={parseMarkdown(content()!, resolvedPath)}
/>
</Show>
</div>
);
});