92 lines
2.2 KiB
TypeScript
92 lines
2.2 KiB
TypeScript
import { createElements } from "@/lib/tcmd";
|
|
import React, { FC, Suspense, useEffect, useMemo, useState } from "react";
|
|
|
|
import { MDSkeletonLoader } from "../loader";
|
|
import { DevTool } from "../devtools/Toolbox";
|
|
|
|
interface Props {
|
|
body: string;
|
|
escapeTOC?: (tokens: Token[]) => boolean;
|
|
parserId: string;
|
|
title: string;
|
|
}
|
|
|
|
export const TTCMD: FC<Props> = (
|
|
{ body, parserId, escapeTOC = () => false, title },
|
|
) => {
|
|
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]);
|
|
|
|
return (
|
|
<Suspense fallback={<MDSkeletonLoader />}>
|
|
<DevTool id={parserId}>
|
|
<button
|
|
className="btn-primary"
|
|
onClick={() =>
|
|
navigator.clipboard.writeText(JSON.stringify(elements, null, 2))}
|
|
>
|
|
Copy AST for {title}
|
|
</button>
|
|
</DevTool>
|
|
{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>
|
|
);
|
|
};
|
|
|
|
function renderer(tokens: Token[]) {
|
|
return tokens.map((t) => <div className="p" key={t.uuid}>{t.render(t)}</div>);
|
|
}
|