ttcMD: adds frontmatter extraction and token,
fixes inline-code... again
This commit is contained in:
parent
606a90b050
commit
0d839fbf37
@ -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;
|
||||
|
@ -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>
|
||||
)}
|
||||
|
@ -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,
|
||||
|
@ -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
2
types.d.ts
vendored
@ -58,6 +58,8 @@ type TokenMarker = {
|
||||
token: Token;
|
||||
};
|
||||
|
||||
type FrontMatter = Record<string, string>;
|
||||
|
||||
type MultilineCfg = {
|
||||
rx: RegExp;
|
||||
closeRx?: RegExp;
|
||||
|
Loading…
x
Reference in New Issue
Block a user