2023-11-27 15:29:19 +00:00
|
|
|
import {
|
|
|
|
clientStore,
|
|
|
|
editor,
|
|
|
|
markdown,
|
|
|
|
system,
|
|
|
|
} from "$sb/silverbullet-syscall/mod.ts";
|
|
|
|
import { renderToText, traverseTree, traverseTreeAsync } from "$sb/lib/tree.ts";
|
2023-12-27 17:05:47 +00:00
|
|
|
import { CodeWidgetContent } from "$sb/types.ts";
|
2023-11-25 12:40:56 +00:00
|
|
|
|
|
|
|
const hideTOCKey = "hideTOC";
|
|
|
|
const headerThreshold = 3;
|
|
|
|
|
|
|
|
type Header = {
|
|
|
|
name: string;
|
|
|
|
pos: number;
|
|
|
|
level: number;
|
|
|
|
};
|
|
|
|
|
|
|
|
let cachedTOC: string | undefined;
|
|
|
|
|
|
|
|
export async function toggleTOC() {
|
|
|
|
cachedTOC = undefined;
|
|
|
|
let hideTOC = await clientStore.get(hideTOCKey);
|
|
|
|
hideTOC = !hideTOC;
|
|
|
|
await clientStore.set(hideTOCKey, hideTOC);
|
|
|
|
await renderTOC(); // This will hide it if needed
|
|
|
|
}
|
|
|
|
|
2023-11-27 15:29:19 +00:00
|
|
|
async function markdownToHtml(text: string): Promise<string> {
|
|
|
|
return system.invokeFunction("markdown.markdownToHtml", text);
|
|
|
|
}
|
|
|
|
|
2023-12-27 17:05:47 +00:00
|
|
|
export async function renderTOC(
|
|
|
|
reload = false,
|
|
|
|
): Promise<CodeWidgetContent | null> {
|
2023-11-25 12:40:56 +00:00
|
|
|
if (await clientStore.get(hideTOCKey)) {
|
2023-12-27 17:05:47 +00:00
|
|
|
return null;
|
2023-11-25 12:40:56 +00:00
|
|
|
}
|
|
|
|
const page = await editor.getCurrentPage();
|
|
|
|
const text = await editor.getText();
|
|
|
|
const tree = await markdown.parseMarkdown(text);
|
|
|
|
const headers: Header[] = [];
|
2023-11-27 15:29:19 +00:00
|
|
|
await traverseTreeAsync(tree, async (n) => {
|
2023-11-25 12:40:56 +00:00
|
|
|
if (n.type?.startsWith("ATXHeading")) {
|
|
|
|
headers.push({
|
2023-12-27 17:05:47 +00:00
|
|
|
name: n.children!.slice(1).map(renderToText).join("").trim(),
|
2023-11-25 12:40:56 +00:00
|
|
|
pos: n.from!,
|
|
|
|
level: +n.type[n.type.length - 1],
|
|
|
|
});
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
if (headers.length < headerThreshold) {
|
2023-12-27 17:05:47 +00:00
|
|
|
console.log("Not enough headers, not showing TOC", headers.length);
|
|
|
|
return null;
|
2023-11-25 12:40:56 +00:00
|
|
|
}
|
2023-12-27 17:05:47 +00:00
|
|
|
// console.log("Headers", headers);
|
|
|
|
const renderedMd = "# Table of Contents\n" +
|
|
|
|
headers.map((header) =>
|
|
|
|
`${
|
|
|
|
" ".repeat((header.level - 1) * 2)
|
|
|
|
}* [[${page}@${header.pos}|${header.name}]]`
|
|
|
|
).join("\n");
|
|
|
|
// console.log("Markdown", renderedMd);
|
|
|
|
return {
|
|
|
|
markdown: renderedMd,
|
|
|
|
};
|
2023-11-25 12:40:56 +00:00
|
|
|
}
|