ttcmd: adds hr element

help pages: adds a way to extract the table of contents to the parent,
smoothes out the rough loading on help pages
This commit is contained in:
2024-03-13 02:52:59 -06:00
parent 009e988a38
commit d0cb74bea8
12 changed files with 199 additions and 70 deletions

30
components/loader.tsx Normal file
View File

@@ -0,0 +1,30 @@
import { FC } from "react";
export const Loader: FC = () => {
const tragedy = [
"Did you ever hear the tragedy of Darth Plagueis the Wise?",
"No.",
"I thought not. It's not a story the Jedi would tell you. It's a Sith legend. Darth Plagueis... was a Dark Lord of the Sith so powerful and so wise, he could use the Force to influence the midi-chlorians... to create... life. He had such a knowledge of the dark side, he could even keep the ones he cared about... from dying.",
"He could actually... save people from death?",
"The dark side of the Force is a pathway to many abilities... some consider to be unnatural.",
"Wh What happened to him?",
"He became so powerful, the only thing he was afraid of was... losing his power. Which eventually, of course, he did. Unfortunately, he taught his apprentice everything he knew. Then his apprentice killed him in his sleep. It's ironic. He could save others from death, but not himself.",
"Is it possible to learn this power?",
"Not from a Jedi.",
];
return (
<>
<section className="heading">
<h2 className="strapline skeleton">Do It</h2>
<h1 className="skeleton">Kill him. Kill him now.</h1>
</section>
<section>
{tragedy.map((t) => <p key={t} className=".skeleton">{t}</p>)}
</section>
</>
// <div className="mx-auto text-9xl flex justify-center py-24">
// Wrangling Cats...
// </div>
);
};

View File

@@ -4,36 +4,89 @@ import { Accordion, AccordionContent } from "@/lib/accordion";
import { Poppable } from "@/lib/poppables/components/poppable";
import { createElements } from "@/lib/tcmd";
import Link from "next/link";
import React, { FC, Fragment, ReactNode, use, useMemo } from "react";
import React, {
FC,
Fragment,
Suspense,
use,
useEffect,
useMemo,
useState,
} from "react";
import { sanitize } from "isomorphic-dompurify";
import StaticGenerationSearchParamsBailoutProvider from "next/dist/client/components/static-generation-searchparams-bailout-provider";
import { Loader } from "../loader";
export const TTCMD: FC<
{ body: string; escapeTOC?: (tokens: Token[]) => boolean }
> = ({ body, escapeTOC = () => false }) => {
const elements = useMemo(() => createElements(body), [body]);
const [toc, start, end] = useMemo(() => {
const tocHead = elements.findIndex((t) =>
t.content.includes("Table of Contents")
);
if (tocHead > -1) {
const hr = elements.slice(tocHead).findIndex((t) => t.type === "hr");
if (hr > -1) {
const end = hr + 1;
return [elements.slice(tocHead, end), tocHead, end - tocHead];
}
}
return [[], 0, 0];
}, [elements]);
// const hasEscapedTOC = useMemo(() => toc && escapeTOC(toc), [escapeTOC, toc])
const [hasEscapedTOC, setHasEscapedTOC] = useState<boolean>();
useEffect(() => {
setHasEscapedTOC(escapeTOC(toc));
}, [escapeTOC, toc]);
export const TTCMD: FC<{ body: Promise<string> }> = ({ body }) => {
"use client";
const text = use(body);
const [elements, tabSpacing] = useMemo(() => createElements(text), [text]);
const tada = useMemo(
() => <>{renderer(elements.map((e) => e.token))}</>,
[elements],
);
return (
<div className="text-lg col-span-2">
<div>
<button
className="btn-primary"
onClick={() =>
navigator.clipboard.writeText(JSON.stringify(elements, null, 2))}
>
copy ast
</button>
</div>
{/* {elements.map((e, i) => <Fragment key={e.uuid}>{render(e)}</Fragment>)} */}
<Suspense fallback={<Loader />}>
{
/* <button
className="btn-primary"
onClick={() =>
navigator.clipboard.writeText(JSON.stringify(elements, null, 2))}
>
copy ast
</button> */
}
{hasEscapedTOC !== undefined &&
(
<TTCMDRenderer
tokens={hasEscapedTOC
? [...elements].toSpliced(start, end)
: elements}
/>
)}
</Suspense>
);
};
export const TTCMDRenderer: FC<{ tokens: Token[] }> = ({ tokens }) => {
const tada = useMemo(
() => (
<>
{renderer(tokens)}
</>
),
[tokens],
);
if (!tokens.length) {
const audio = new Audio(
"https://assets.mixkit.co/active_storage/sfx/221/221-preview.mp3",
);
audio.onload = () => {
audio.play();
};
}
return (
<div className="text-lg">
{tada}
</div>
// <div className="grid grid-cols-3">
// {/* <pre suppressHydrationWarning>{JSON.stringify(elements,null,2)}</pre> */}
// </div>
);
};
@@ -214,6 +267,8 @@ function render(token: Token, usedIds: string[]) {
{token.children?.map((c) => render(c, usedIds))}
</li>
);
case "hr":
return <div className="w-full border-b border-mixed-500 my-3"></div>;
default:
return (
<div className="p bg-red-600 text-white">