ttcMD: adds frontmatter extraction and token,
fixes inline-code... again
This commit is contained in:
parent
606a90b050
commit
0d839fbf37
@ -31,7 +31,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.heading {
|
.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 {
|
.heading h1 {
|
||||||
@apply text-5xl font-bold;
|
@apply text-5xl font-bold;
|
||||||
@ -68,7 +68,7 @@
|
|||||||
@apply rounded-t-md;
|
@apply rounded-t-md;
|
||||||
}
|
}
|
||||||
.accordion:has(+ .accordion) {
|
.accordion:has(+ .accordion) {
|
||||||
@apply border-b border-black/20;
|
@apply border-b border-black/20 dark:border-white/20;
|
||||||
}
|
}
|
||||||
.accordion:not(:has(+ .accordion)) {
|
.accordion:not(:has(+ .accordion)) {
|
||||||
@apply rounded-b-md;
|
@apply rounded-b-md;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { TTCMD, TTCMDRenderer } from "@/components/ttcmd";
|
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 }> = ({
|
export const HelpClient: FC<{ body: Promise<string>; title: string }> = ({
|
||||||
body: bodyPromise,
|
body: bodyPromise,
|
||||||
@ -14,21 +15,47 @@ export const HelpClient: FC<{ body: Promise<string>; title: string }> = ({
|
|||||||
return true;
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<section className="heading">
|
<section className="heading">
|
||||||
<h2 className="strapline">Help</h2>
|
<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>
|
||||||
<section className="grid grid-cols-3 gap-x-8 gap-y-6 my-6">
|
<section className="grid grid-cols-3 gap-x-8 gap-y-6 my-6">
|
||||||
<div className="col-span-2">
|
<div className="col-span-2">
|
||||||
<TTCMD
|
<TTCMD
|
||||||
body={body}
|
body={cleanBody}
|
||||||
escapeTOC={escapeTOC}
|
escapeTOC={escapeTOC}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{toc && (
|
{toc && (
|
||||||
<div className="sticky top-8 h-min">
|
<div className="sticky top-6 h-min">
|
||||||
<TTCMDRenderer tokens={toc} />
|
<TTCMDRenderer tokens={toc} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { parse } from "path";
|
||||||
|
|
||||||
type TokenIdentifier = {
|
type TokenIdentifier = {
|
||||||
rx: RegExp;
|
rx: RegExp;
|
||||||
parse: (s: string) => Token;
|
parse: (s: string) => Token;
|
||||||
@ -63,8 +65,13 @@ TokenIdentifiers.set("card", {
|
|||||||
return search(s, start, end, rx, crx);
|
return search(s, start, end, rx, crx);
|
||||||
},
|
},
|
||||||
parse(s) {
|
parse(s) {
|
||||||
const rx = /\[{2}(!?)\n+([\s\S]*)\n+\]{2}/;
|
const rx = /\[{2}(!?)\s*?\n+([\s\S]*)\n+\]{2}/;
|
||||||
const [_, isBlock, content] = s.match(rx) || ["", "Unable to parse card"];
|
const match = s.match(rx);
|
||||||
|
if (!match) debugger;
|
||||||
|
const [_, isBlock, content] = match ||
|
||||||
|
["", "", s];
|
||||||
|
|
||||||
|
// if (!_) debugger;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: content.trim(),
|
content: content.trim(),
|
||||||
@ -74,6 +81,7 @@ TokenIdentifiers.set("card", {
|
|||||||
},
|
},
|
||||||
type: "card",
|
type: "card",
|
||||||
uuid: crypto.randomUUID(),
|
uuid: crypto.randomUUID(),
|
||||||
|
rendersChildrenOnly,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -187,7 +195,7 @@ TokenIdentifiers.set("anchor", {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
TokenIdentifiers.set("inline-code", {
|
TokenIdentifiers.set("inline-code", {
|
||||||
rx: /\s?`(.{3,}?|[a-z0-9]*?)`[^`a-z0-9\n]/gi,
|
rx: /(?<=\s|^)`(.*?)`(?=[\s,.!?)]|$)/gi,
|
||||||
parse(s) {
|
parse(s) {
|
||||||
return {
|
return {
|
||||||
// content: inline,
|
// 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(
|
function findMatchingClosedParenthesis(
|
||||||
str: string,
|
str: string,
|
||||||
openRegex: RegExp,
|
openRegex: RegExp,
|
||||||
|
@ -155,9 +155,20 @@ const contentToChildren = (token: Token) => {
|
|||||||
content: c.replaceAll("\n", ""),
|
content: c.replaceAll("\n", ""),
|
||||||
metadata: {},
|
metadata: {},
|
||||||
raw: c,
|
raw: c,
|
||||||
type: "text",
|
type: token.rendersChildrenOnly ? "p" : "text",
|
||||||
uuid: crypto.randomUUID(),
|
uuid: crypto.randomUUID(),
|
||||||
rendersContentOnly: true,
|
rendersContentOnly: true,
|
||||||
|
children: token.rendersChildrenOnly && c.replaceAll("\n", "")
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
content: c.replaceAll("\n", ""),
|
||||||
|
metadata: {},
|
||||||
|
raw: c,
|
||||||
|
type: "text",
|
||||||
|
uuid: crypto.randomUUID(),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: undefined,
|
||||||
})),
|
})),
|
||||||
token.children || [],
|
token.children || [],
|
||||||
).filter((c) => c.children?.length || (c.rendersContentOnly && c.content));
|
).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
|
// Filter out chunks that were merged into others
|
||||||
return chunks.filter((c) => !mergedChunks.find((c2) => c === c2));
|
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;
|
token: Token;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type FrontMatter = Record<string, string>;
|
||||||
|
|
||||||
type MultilineCfg = {
|
type MultilineCfg = {
|
||||||
rx: RegExp;
|
rx: RegExp;
|
||||||
closeRx?: RegExp;
|
closeRx?: RegExp;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user