diff --git a/packages/website/mdsvex.config.js b/packages/website/mdsvex.config.js index 93e22b6d..0e0c469e 100644 --- a/packages/website/mdsvex.config.js +++ b/packages/website/mdsvex.config.js @@ -15,7 +15,7 @@ import rehypeKatexSvelte from 'rehype-katex-svelte'; // import github from "remark-github"; import rehypeSlug from 'rehype-slug'; - +// import rehypeToc from '@jsdevtools/rehype-toc'; import { createHighlighter } from "@bitmachina/highlighter"; import { parse, format } from "node:path"; @@ -92,6 +92,110 @@ const httpHighlight = { } const hrefTemplate = (/** @type {string} */ permalink) => `#${permalink}` + +// function customizeTOC(toc) { +// // console.log(toc) + +// return { +// type: 'root', +// children: [{ +// type: "element", +// // tagName: "svelte:component", +// // properties: { this: "{tocComponent}" }, +// tagName: "div", +// properties: {}, +// children: [toc], +// }] +// }; +// } +function buildNestedHeadings(headings) { + let result = []; + let stack = [{ level: 0, children: result }]; + + for (let heading of headings) { + while ( + stack.length > 1 && + heading.level <= stack[stack.length - 1].level + ) { + stack.pop(); + } + let parent = stack[stack.length - 1]; + let newHeading = { + ...heading, + children: [], + level: heading.level, + }; + parent.children.push(newHeading); + stack.push(newHeading); + } + + return result; +} +import { visit } from 'unist-util-visit'; +import { toString as mdast_tree_to_string } from 'mdast-util-to-string' + + +import GithubSlugger from 'github-slugger' +/** + * @param {{ prefix?: string; }} opts + */ +function add_toc_remark(opts) { + const slugs = new GithubSlugger() + const prefix = opts?.prefix || ""; + return async function transformer(tree, vFile) { + slugs.reset() + + vFile.data.flattenedHeadings = []; + + visit(tree, 'heading', (node) => { + let title = mdast_tree_to_string(node); + vFile.data.flattenedHeadings.push({ + level: node.depth, + title, + id: prefix + slugs.slug(title) + }); + }); + + if (!vFile.data.fm) vFile.data.fm = {}; + vFile.data.fm.flattenedHeadings = vFile.data.flattenedHeadings; + vFile.data.fm.headings = buildNestedHeadings(vFile.data.flattenedHeadings); + }; +} +import { toString as hast_tree_to_string } from 'hast-util-to-string' +/** + * Determines whether the given node is an HTML element. + */ +function isHtmlElementNode(node) { + return typeof node === "object" && + node.type === "element" && + typeof node.tagName === "string" && + "properties" in node && + typeof node.properties === "object"; +} +const HEADINGS = ["h1", "h2", "h3", "h4", "h5", "h6"] +/** + * Determines whether the given node is an HTML heading node, according to the specified options + */ +function isHeadingNode(node) { + return isHtmlElementNode(node) && HEADINGS.includes(node.tagName); +} +function add_toc_rehype(self, opts) { + return async function transformer(tree, vFile) { + // console.log(tree) + vFile.data.headings = []; + + visit(tree, isHeadingNode, (node) => { + console.log(node) + vFile.data.headings.push({ + level: node.depth, + title: hast_tree_to_string(node), + }); + }); + + if (!vFile.data.fm) vFile.data.fm = {}; + vFile.data.fm.headings = vFile.data.headings; + }; +} /** * @type {import("mdsvex").MdsvexOptions} */ @@ -104,6 +208,10 @@ const config = { dashes: "oldschool", }, + // layout: { + // _: "./src/layout.svelte" + // }, + highlight: { // @ts-ignore highlighter: await createHighlighter({ theme: "github-dark", langs: [httpHighlight] }), @@ -149,12 +257,13 @@ const config = { // }], // [remarkBibliography, { bibliography }], // [remarkMermaid, {}] + [add_toc_remark, { prefix: "h-" }] ], rehypePlugins: [ // @ts-ignore rehypeKatexSvelte, // @ts-ignore - rehypeSlug + [rehypeSlug, { prefix: "h-" }], ], }; diff --git a/packages/website/package.json b/packages/website/package.json index e25727d2..a074bd28 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -21,7 +21,10 @@ "@sveltejs/vite-plugin-svelte": "^3.1.1", "@types/node": "^20.14.2", "@types/sharedworker": "^0.0.115", + "github-slugger": "^2.0.0", "glob": "^10.4.1", + "hast-util-to-string": "^3.0.0", + "mdast-util-to-string": "^4.0.0", "mdsvex": "^0.11.2", "rehype-katex-svelte": "1.2", "rehype-slug": "^6.0.0", @@ -44,6 +47,7 @@ "tslib": "^2.6.3", "typescript": "^5.4.5", "unified": "^11.0.4", + "unist-util-visit": "^5.0.0", "vite": "^5.3.1", "vite-plugin-dynamic-import": "^1.5.0", "vite-plugin-image-optimizer": "^1.1.8" diff --git a/packages/website/src/lib/Toc.svelte b/packages/website/src/lib/Toc.svelte new file mode 100644 index 00000000..d0aa65c7 --- /dev/null +++ b/packages/website/src/lib/Toc.svelte @@ -0,0 +1,78 @@ + + + + + diff --git a/packages/website/src/lib/TocItem.svelte b/packages/website/src/lib/TocItem.svelte new file mode 100644 index 00000000..626f825a --- /dev/null +++ b/packages/website/src/lib/TocItem.svelte @@ -0,0 +1,16 @@ + + +