64 lines
2.1 KiB
TypeScript
64 lines
2.1 KiB
TypeScript
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>
|
||
);
|
||
});
|