feat(viewer): Add md-embed component
This commit is contained in:
parent
f19c7ff77c
commit
f734da6acd
|
|
@ -53,3 +53,5 @@ bler
|
|||
|
||||
> [!CAUTION]
|
||||
> Negative potential consequences of an action.
|
||||
|
||||
:md-embed[./style-test.md]
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
:md-font[Pacifico]{source=google} hello?
|
||||
|
|
@ -5,6 +5,7 @@ import "./md-link";
|
|||
import "./md-pins";
|
||||
import "./md-bg";
|
||||
import "./md-font";
|
||||
import "./md-embed";
|
||||
import "./md-deck";
|
||||
import "./md-commander/index";
|
||||
import "./md-yarn-spinner";
|
||||
|
|
@ -23,6 +24,7 @@ export type { DiceProps } from "./md-dice";
|
|||
export type { TableProps } from "./md-table";
|
||||
export type { BgProps } from "./md-bg";
|
||||
export type { FontProps } from "./md-font";
|
||||
export type { EmbedProps } from "./md-embed";
|
||||
export type { YarnSpinnerProps } from "./md-yarn-spinner";
|
||||
export type { TokenProps } from "./md-token";
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
import { customElement, noShadowDOM } from "solid-element";
|
||||
import { createResource, Show, createMemo, 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);
|
||||
|
||||
// 计算嵌入文件的 iconPrefix,用 createMemo 包装避免每个 embed 都重新计算
|
||||
const iconPrefix = createMemo(() => resolvePath(resolvedPath, "./assets"));
|
||||
|
||||
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()!, iconPrefix())}
|
||||
/>
|
||||
</Show>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
Loading…
Reference in New Issue