Generate web slides from Markdoc
1import {
2 Config,
3 Node,
4 nodes,
5 RenderableTreeNode,
6 Schema,
7 Tag,
8} from "@markdoc/markdoc";
9import { codeToHast } from "shiki";
10import { isElement } from "hast-util-is-element";
11import type { Node as HastNode } from "hast";
12import type { Includes } from "../types.ts";
13
14function hastToRenderNode(node: HastNode): RenderableTreeNode {
15 let tag: Tag;
16
17 if (isElement(node)) {
18 tag = new Tag(
19 node.tagName,
20 node.properties,
21 node.children.map(hastToRenderNode),
22 );
23 } else if ("value" in node) {
24 return node.value as string;
25 } else {
26 tag = new Tag(
27 "div",
28 undefined,
29 "children" in node
30 ? (node.children as HastNode[]).map(hastToRenderNode)
31 : undefined,
32 );
33 }
34
35 return tag;
36}
37
38export function createFenceNode(includes: Includes): Schema {
39 return {
40 render: "pre",
41 attributes: {
42 content: { type: "String", render: false },
43 language: { type: "String", render: false },
44 },
45 async transform(node: Node, config: Config) {
46 const { content, language = "text" } = node.attributes;
47
48 if (language === "mermaid") {
49 includes.add("mermaid");
50
51 // Bail out for Mermaid to handle later
52 const base = (nodes.fence.transform!)(node, config);
53 (base as Tag).attributes.class = "mermaid";
54 return base;
55 }
56
57 const hast = await codeToHast(content, {
58 lang: language,
59 theme: "rose-pine",
60 });
61
62 return hastToRenderNode(hast);
63 },
64 };
65}