Pass the file's base path through the markdown parsing pipeline to allow the icon directive to resolve relative asset paths correctly using an absolute fallback chain.
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>
|
|
);
|
|
});
|