import {
IdentifiedToken,
Token,
TokenAttributes,
TokenRenderer,
} from "@/types";
import { sanitize } from "isomorphic-dompurify";
import Link from "next/link";
import { Fragment } from "react";
import { Poppable } from "../poppables/components/poppable";
import { Accordion, AccordionContent } from "../accordion";
type SearchFunction = (s: string, start: number, end: number) => {
start: number;
end: number;
text: string;
lastIndex: number;
};
type TokenIdentifier = {
rx: RegExp;
parse: (s: string) => Token;
search?: SearchFunction;
};
type TokenIdentifierMap = Map<
string,
TokenIdentifier
>;
export const TokenRenderers = new Map<
string,
TokenRenderer
>();
type IdentifierRegistration = (
type: string,
match: RegExp,
parseFunction: (s: string, rx: RegExp) => IdentifiedToken,
renderFunction: TokenRenderer,
openTagRx?: RegExp,
closeTagRx?: RegExp,
) => void;
export function buildIdentifierMap(): [
TokenIdentifierMap,
IdentifierRegistration,
] {
const TokenIdentifiers = new Map<
string,
TokenIdentifier
>();
function registerIdentifier(
type: string,
match: RegExp,
parseFunction: (s: string, rx: RegExp) => IdentifiedToken,
renderFunction: TokenRenderer,
): void;
function registerIdentifier(
type: string,
match: RegExp,
parseFunction: (s: string, rx: RegExp) => IdentifiedToken,
renderFunction: TokenRenderer,
openTagRx: RegExp,
closeTagRx: RegExp,
): void;
function registerIdentifier(
type: string,
match: RegExp,
parseFunction: (s: string, rx: RegExp) => IdentifiedToken,
renderFunction: TokenRenderer,
openTagRx?: RegExp,
closeTagRx?: RegExp,
) {
TokenIdentifiers.set(type, {
rx: match,
parse(s) {
const identifiedToken = parseFunction(s, this.rx);
const token: TokenAttributes = {
render: renderFunction,
type,
};
return { ...token, ...identifiedToken };
},
search: (openTagRx && closeTagRx)
? (s, start, end) => {
return search(
s,
start,
end,
new RegExp(openTagRx, "g"),
new RegExp(closeTagRx, "g"),
);
}
: undefined,
});
TokenRenderers.set(type, renderFunction);
}
return [TokenIdentifiers, registerIdentifier];
}
export const buildOnlyDefaultElements = () => {
const [TokenIdentifiers, registerIdentifier] = buildIdentifierMap();
TokenRenderers.set("text", (t) => {
debugger;
return (
{t.content.replaceAll("\\n", "\n")}
);
});
const rendersContentOnly = true;
const rendersChildrenOnly = true;
// grid
registerIdentifier(
"grid",
/(? {
const rx = /((?:\[\])+)\n+([\s\S]*)\n+\/\[\]/;
const [_, columns, content] = s.match(rx) ||
["", "..", "Unable to parse grid"];
return {
content,
raw: s,
metadata: {
columns: (columns.length / 2).toString(),
},
uuid: crypto.randomUUID(),
rendersChildrenOnly,
};
},
(token) => {
const { content, children, metadata, uuid } = token;
return (
{children?.map((c, i) => (
{c.render(c)}
))}
);
},
/(? {
const rx = /\[{2}(!?)\s*?\n+([\s\S]*)\n+\]{2}/;
const match = s.match(rx);
const [_, isBlock, content] = match || ["", "", s];
return {
content: content.trim(),
raw: s,
metadata: {
isBlock,
},
uuid: crypto.randomUUID(),
rendersChildrenOnly,
};
},
(token) => {
const { children, metadata, uuid } = token;
return (
{children?.map((e) => (
{e.render(e)}
))}
);
},
/\[\[/g,
/\]\]/g,
);
// fenced code block
registerIdentifier("code", /`{3}\n+((?:.|\n)*?)\n+`{3}/g, (s, rx) => {
return {
content: s.match(new RegExp(rx, ""))?.at(1) ||
"Unable to parse code",
raw: s,
metadata: {},
uuid: crypto.randomUUID(),
rendersContentOnly,
};
}, (token) => {
return (
{token.content}
);
});
// list
registerIdentifier(
"list",
/^\s*-\s([\s\S]*?)\n\n/gm,
(s, rx) => {
return {
content: s.match(new RegExp(rx, ""))?.at(1) ||
"Unable to parse list",
raw: s,
metadata: {
initialDepth:
s.replace("\n", "").split("-").at(0)?.length.toString() ||
"1",
},
uuid: crypto.randomUUID(),
rendersChildrenOnly,
};
},
(token) => {
const { children, metadata, uuid } = token;
return (
<>
{children?.map((c) => {
return (
-
{c.children?.map((c) => (
{c.render(c)}
))}
);
})}
>
);
},
);
// list-item
registerIdentifier(
"list-item",
/^\s*-\s(.*?)$/gm,
(s, rx) => {
return {
content: s.match(new RegExp(rx, ""))?.at(1) ||
"Unable to parse list-item",
raw: s,
metadata: {
initialDepth:
s.replace("\n", "").split("-").at(0)?.length.toString() ||
"1",
},
uuid: crypto.randomUUID(),
};
},
(token) => {
const { children, metadata, uuid } = token;
return (
{children?.map((c) => (
(c.render(c))
))}
);
},
);
// heading
registerIdentifier("heading", /^#+\s(.*?)$/gm, (s, rx) => {
return {
content: s.match(new RegExp(rx, ""))?.at(1) ||
"Unable to parse heading",
raw: s,
metadata: {
strength: s.match(/#/g)?.length.toString() || "1",
},
uuid: crypto.randomUUID(),
rendersContentOnly,
};
}, (token) => {
return (
{token.content}
);
});
// image
registerIdentifier("image", /\!\[(.*?)\]\((.*?)\)/g, (s, rx) => {
const [_, title, src] = s.match(new RegExp(rx, ""))!;
return {
// content: inline,
content: title.trim(),
raw: s,
metadata: {
src,
},
uuid: crypto.randomUUID(),
rendersContentOnly,
};
}, (token) => {
const { metadata } = token;
metadata.src = metadata.src as string;
if (metadata.src.startsWith("