ttcMD: adds frontmatter extraction and token,

fixes inline-code... again
This commit is contained in:
Emmaline Autumn 2024-03-14 09:26:15 -06:00
parent 606a90b050
commit 0d839fbf37
5 changed files with 96 additions and 10 deletions

View File

@ -31,7 +31,7 @@
}
.heading {
@apply pb-8 border-b border-b-primary-500 dark:border-b-dark-500 min-w-full;
@apply pb-6 border-b border-b-primary-500 dark:border-b-dark-500 min-w-full;
}
.heading h1 {
@apply text-5xl font-bold;
@ -68,7 +68,7 @@
@apply rounded-t-md;
}
.accordion:has(+ .accordion) {
@apply border-b border-black/20;
@apply border-b border-black/20 dark:border-white/20;
}
.accordion:not(:has(+ .accordion)) {
@apply rounded-b-md;

View File

@ -1,7 +1,8 @@
"use client";
import { TTCMD, TTCMDRenderer } from "@/components/ttcmd";
import { FC, use, useCallback, useState } from "react";
import { extractFrontMatter } from "@/lib/tcmd";
import { FC, use, useCallback, useEffect, useState } from "react";
export const HelpClient: FC<{ body: Promise<string>; title: string }> = ({
body: bodyPromise,
@ -14,21 +15,47 @@ export const HelpClient: FC<{ body: Promise<string>; title: string }> = ({
return true;
}, []);
const [frontMatter, setFrontMatter] = useState<FrontMatter>({});
const [cleanBody, setBody] = useState<string>("");
useEffect(() => {
if (!body) return;
const [frontmatter, clean] = extractFrontMatter(body);
setFrontMatter(frontmatter);
setBody(clean);
}, [body]);
return (
<>
<section className="heading">
<h2 className="strapline">Help</h2>
<h1>{decodeURIComponent(title)}</h1>
<h1>{frontMatter.title || decodeURIComponent(title)}</h1>
<div className="text-white/50">
{!!frontMatter.date && (
<div className="text-white/50">
Published: {frontMatter.date}
{!!frontMatter.updated && (
<span className="text-white/50">
, Last updated: {frontMatter.updated}
</span>
)}
</div>
)}
{!!frontMatter.author && (
<div className="italic text-white/50">By: {frontMatter.author}</div>
)}
</div>
</section>
<section className="grid grid-cols-3 gap-x-8 gap-y-6 my-6">
<div className="col-span-2">
<TTCMD
body={body}
body={cleanBody}
escapeTOC={escapeTOC}
/>
</div>
{toc && (
<div className="sticky top-8 h-min">
<div className="sticky top-6 h-min">
<TTCMDRenderer tokens={toc} />
</div>
)}

View File

@ -1,3 +1,5 @@
import { parse } from "path";
type TokenIdentifier = {
rx: RegExp;
parse: (s: string) => Token;
@ -63,8 +65,13 @@ TokenIdentifiers.set("card", {
return search(s, start, end, rx, crx);
},
parse(s) {
const rx = /\[{2}(!?)\n+([\s\S]*)\n+\]{2}/;
const [_, isBlock, content] = s.match(rx) || ["", "Unable to parse card"];
const rx = /\[{2}(!?)\s*?\n+([\s\S]*)\n+\]{2}/;
const match = s.match(rx);
if (!match) debugger;
const [_, isBlock, content] = match ||
["", "", s];
// if (!_) debugger;
return {
content: content.trim(),
@ -74,6 +81,7 @@ TokenIdentifiers.set("card", {
},
type: "card",
uuid: crypto.randomUUID(),
rendersChildrenOnly,
};
},
});
@ -187,7 +195,7 @@ TokenIdentifiers.set("anchor", {
},
});
TokenIdentifiers.set("inline-code", {
rx: /\s?`(.{3,}?|[a-z0-9]*?)`[^`a-z0-9\n]/gi,
rx: /(?<=\s|^)`(.*?)`(?=[\s,.!?)]|$)/gi,
parse(s) {
return {
// content: inline,
@ -307,6 +315,21 @@ TokenIdentifiers.set("comment", {
},
});
TokenIdentifiers.set("frontmatter", {
rx: /^---([\s\S]*?)---/g,
parse(s) {
return {
content: "",
metadata: {
frontmatterString: s.match(this.rx)?.at(0) || "",
},
raw: "",
type: "frontmatter",
uuid: "frontmatter",
};
},
});
function findMatchingClosedParenthesis(
str: string,
openRegex: RegExp,

View File

@ -155,9 +155,20 @@ const contentToChildren = (token: Token) => {
content: c.replaceAll("\n", ""),
metadata: {},
raw: c,
type: "text",
type: token.rendersChildrenOnly ? "p" : "text",
uuid: crypto.randomUUID(),
rendersContentOnly: true,
children: token.rendersChildrenOnly && c.replaceAll("\n", "")
? [
{
content: c.replaceAll("\n", ""),
metadata: {},
raw: c,
type: "text",
uuid: crypto.randomUUID(),
},
]
: undefined,
})),
token.children || [],
).filter((c) => c.children?.length || (c.rendersContentOnly && c.content));
@ -239,3 +250,26 @@ function processChunks(chunks: Token[][]) {
// Filter out chunks that were merged into others
return chunks.filter((c) => !mergedChunks.find((c2) => c === c2));
}
/**
* @description Extracts the frontmatter of a markdown document and returns it as an object and with the body with the frontmatter removed from it
*
* @returns a tuple containing the body and the frontmatter object
*/
export function extractFrontMatter(body: string): [FrontMatter, string] {
const rx = /^---([\s\S]*?)---/;
const [_, frontmatterString] = body.match(rx) || ["", ""];
body = body.replace(rx, "");
const frontMatter: FrontMatter = {};
for (const line of frontmatterString.split("\n")) {
if (!line) continue;
const [key, value] = line.split(": ");
frontMatter[key] = value;
}
return [frontMatter, body];
}

2
types.d.ts vendored
View File

@ -58,6 +58,8 @@ type TokenMarker = {
token: Token;
};
type FrontMatter = Record<string, string>;
type MultilineCfg = {
rx: RegExp;
closeRx?: RegExp;