Add callout as a seperate type

This commit is contained in:
Peaceultime 2024-12-04 17:59:28 +01:00
parent be5f48feba
commit cccfccc299
1 changed files with 50 additions and 81 deletions

View File

@ -4,14 +4,27 @@ import { toHast } from "mdast-util-to-hast"
import { toHtml } from "hast-util-to-html"
import { toString } from 'mdast-util-to-string';
import { FindAndReplaceList, findAndReplace as mdastFindReplace } from "mdast-util-find-and-replace"
import type { Callout, Root, Tag } from "mdast";
declare module 'mdast'
{
interface Tag extends Literal {
interface Tag extends Literal
{
type: "tag";
data?: TagData | undefined;
}
interface TagData extends Data {}
interface Callout extends Parent
{
type: "callout";
data?: CalloutData | undefined;
}
interface CalloutData extends Data
{
type: string;
title?: string;
fold?: boolean;
}
interface PhrasingContentMap
{
tag: Tag;
@ -19,6 +32,7 @@ declare module 'mdast'
interface RootContentMap
{
tag: Tag;
callout: Callout;
}
}
@ -139,9 +153,9 @@ function pathToRoot(slug: string) {
}
export default function ofm() {
return function (tree: any, file: any) {
return function (tree: Root, file: any) {
const base = pathToRoot(file?.data?.slug ?? '');
const replacements: FindAndReplaceList = []
const replacements: FindAndReplaceList = [];
replacements.push([
wikilinkRegex,
@ -164,7 +178,7 @@ export default function ofm() {
],
}
},
])
]);
replacements.push([
highlightRegex,
@ -175,7 +189,7 @@ export default function ofm() {
value: `<span class="text-highlight">${inner}</span>`,
}
},
])
]);
replacements.push([
commentRegex,
@ -186,7 +200,7 @@ export default function ofm() {
value: `<span class="text-comment">${inner}</span>`,
}
},
])
]);
replacements.push([
tagRegex,
@ -210,11 +224,11 @@ export default function ofm() {
{ type: 'text', value: tag }
]
}
}
} as Tag;
},
])
]);
visit(tree, "html", (node) => {
/*visit(tree, "html", (node) => {
for (const [regex, replace] of replacements) {
if (typeof replace === "string") {
node.value = node.value.replace(regex, replace)
@ -233,93 +247,48 @@ export default function ofm() {
})
}
}
})
})*/
mdastFindReplace(tree, replacements);
visit(tree, "blockquote", (node) => {
if (node.children.length === 0) {
return
visit(tree, "blockquote", (node, index, parent) => {
if (node.children.length === 0 || parent === null || index === null) {
return;
}
// find first line
const firstChild = node.children[0]
const firstChild = node.children[0];
if (firstChild.type !== "paragraph" || firstChild.children[0]?.type !== "text") {
return
return;
}
const text = firstChild.children[0].value
const restChildren = firstChild.children.slice(1)
const [firstLine, ...remainingLines] = text.split("\n")
const remainingText = remainingLines.join("\n")
const text = firstChild.children[0].value;
const [firstLine, ...remainingLines] = text.split("\n");
const match = firstLine.match(/^\[\!(\w+)\]([+-]?)/);
if (match && match.input) {
const [calloutDirective, typeString, collapseChar] = match
const calloutType = canonicalizeCallout(
const [calloutTitle, typeString, foldChar] = match;
const type = canonicalizeCallout(
typeString.toLowerCase() as keyof typeof calloutMapping,
)
const collapse = collapseChar === "+" || collapseChar === "-"
const defaultState = collapseChar === "-" ? "collapsed" : "expanded"
const titleContent =
match.input.slice(calloutDirective.length).trim() || capitalize(calloutType)
const titleNode = {
type: "paragraph",
children: [{ type: "text", value: titleContent + " " }, ...restChildren],
}
const title = mdastToHtml(titleNode)
);
const fold = foldChar === "+" || foldChar === "-" ? foldChar === "-" : undefined;
const title = match.input.slice(calloutTitle.length).trim() || capitalize(type);
const toggleIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="fold">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>`
const titleHtml = {
type: "html",
value: `<div
class="callout-title"
>
<div class="callout-icon">${callouts[calloutType]}</div>
<div class="callout-title-inner">${title}</div>
${collapse ? toggleIcon : ""}
</div>`,
}
const blockquoteContent: any[] = [titleHtml]
if (remainingText.length > 0) {
blockquoteContent.push({
type: "paragraph",
hProperties: { className: "callout-content" },
children: [
{
type: "text",
value: remainingText,
},
],
})
}
// replace first line of blockquote with title and rest of the paragraph text
node.children.splice(0, 1, ...blockquoteContent)
// add properties to base blockquote
node.data = {
firstChild.children[0].value = remainingLines.join('\n');
parent.children[index] = {
type: 'callout',
data: {
type,
fold,
title,
hName: 'callout',
hProperties: {
...(node.data?.hProperties ?? {}),
className: `callout ${collapse ? "is-collapsible" : ""} ${defaultState === "collapsed" ? "is-collapsed" : ""
}`,
"data-callout": calloutType,
"data-callout-fold": collapse,
type,
fold,
title,
},
}
}
})
visit(tree, "code", (node) => {
if (node.lang === "mermaid") {
node.data = {
hProperties: {
className: ["mermaid"],
},
children: firstChild.children,
}
}
})