ttcMD: fixes for lists, adds ordered lists

This commit is contained in:
Emmaline Autumn 2024-06-11 05:07:08 -06:00
parent 3a5fe1911a
commit 71bf62b622
2 changed files with 157 additions and 100 deletions

View File

@ -8,7 +8,7 @@ export const TokenRenderers = new Map<string, TokenRenderer<any>>();
export function buildIdentifierMap(): [
TokenIdentifierMap,
IdentifierRegistration,
IdentifierRegistration
] {
const TokenIdentifiers = new Map<string, TokenIdentifier<any>>();
@ -16,7 +16,7 @@ export function buildIdentifierMap(): [
type: string,
match: RegExp,
parseFunction: (s: string, rx: RegExp) => IdentifiedToken<M>,
renderFunction: TokenRenderer<M>,
renderFunction: TokenRenderer<M>
): void;
function registerIdentifier<M>(
type: string,
@ -24,7 +24,7 @@ export function buildIdentifierMap(): [
parseFunction: (s: string, rx: RegExp) => IdentifiedToken<M>,
renderFunction: TokenRenderer<M>,
openTagRx: RegExp,
closeTagRx: RegExp,
closeTagRx: RegExp
): void;
function registerIdentifier<M = Record<string, string>>(
type: string,
@ -32,7 +32,7 @@ export function buildIdentifierMap(): [
parseFunction: (s: string, rx: RegExp) => IdentifiedToken<M>,
renderFunction: TokenRenderer<M>,
openTagRx?: RegExp,
closeTagRx?: RegExp,
closeTagRx?: RegExp
) {
TokenIdentifiers.set(type, {
rx: match,
@ -45,14 +45,15 @@ export function buildIdentifierMap(): [
return { ...token, ...identifiedToken } as Token<M>;
},
search: openTagRx && closeTagRx
search:
openTagRx && closeTagRx
? (s, start, end) => {
return search(
s,
start,
end,
new RegExp(openTagRx, "g"),
new RegExp(closeTagRx, "g"),
new RegExp(closeTagRx, "g")
);
}
: undefined,
@ -67,7 +68,6 @@ export const buildOnlyDefaultElements = () => {
const [TokenIdentifiers, registerIdentifier] = buildIdentifierMap();
TokenRenderers.set("text", (t: Token<any>) => {
debugger;
return (
<span className="whitespace-pre-wrap">
{t.content.replaceAll(/\\n ?/g, "\n")}
@ -105,20 +105,26 @@ export const buildOnlyDefaultElements = () => {
const { children, metadata } = token;
return (
<div
style={{
style={
{
"--grid-cols": metadata.columns,
} as React.CSSProperties}
} as React.CSSProperties
}
className="grid grid-cols-dynamic gap-x-8 mb-6"
>
{children?.map((c) => {
const Comp = c.metadata.span ? Fragment : "div";
return <Comp className="p" key={c.uuid}>{c.render(c)}</Comp>;
return (
<Comp className="p" key={c.uuid}>
{c.render(c)}
</Comp>
);
})}
</div>
);
},
/(?<!\/)(?:\[\])+/g,
/\/\[\]/g,
/\/\[\]/g
);
// card
@ -149,18 +155,21 @@ export const buildOnlyDefaultElements = () => {
return (
<div
data-block={!!metadata.isBlock}
style={{
style={
{
"--v-span": metadata.span || 1,
} as React.CSSProperties}
} as React.CSSProperties
}
className="data-[block=false]:card data-[block=false]:mb-6 col-span-2"
>
{children?.map((e) => <Fragment key={e.uuid}>{e.render(e)}
</Fragment>)}
{children?.map((e) => (
<Fragment key={e.uuid}>{e.render(e)}</Fragment>
))}
</div>
);
},
/\[\[/g,
/\]\]/g,
/\]\]/g
);
// fenced code block
@ -182,16 +191,16 @@ export const buildOnlyDefaultElements = () => {
{token.content}
</pre>
);
},
}
);
// list
registerIdentifier(
"list",
/^\s*-\s([\s\S]*?)\n\n/gm,
/(?<=\n\n|^) *-\s([\s\S]*?)(?=\n\n|$)/g,
(s, rx) => {
return {
content: s.match(new RegExp(rx, ""))?.at(1) || "Unable to parse list",
content: s.match(new RegExp(rx, ""))?.at(0) || "Unable to parse list",
raw: s,
metadata: {
initialDepth:
@ -221,17 +230,60 @@ export const buildOnlyDefaultElements = () => {
</ul>
</>
);
},
}
);
// ordered-list
registerIdentifier(
"ordered-list",
/(?<=\n\n|^)\s*\d+\.\s([\s\S]*?)(?=\n\n|$)/g,
(s, rx) => {
// debugger;
return {
content:
s.match(new RegExp(rx, ""))?.at(0) || "Unable to parse ordered list",
raw: s,
metadata: {
// initialDepth:
// s.replace("\n", "").split(/\d+\./).at(0)?.length.toString() || "1",
},
uuid: crypto.randomUUID(),
rendersChildrenOnly,
};
},
(token) => {
const { children } = token;
debugger;
return (
<>
<ol
// data-depth={(Number(metadata.initialDepth) / 2) % 3}
className="ml-6 list-decimal"
>
{children?.map((c) => {
return (
<li key={c.uuid}>
{c.children?.map((c: Token<any>) => (
<Fragment key={c.uuid}>{c.render(c)}</Fragment>
))}
</li>
);
})}
</ol>
</>
);
}
);
// ordered list-item
// list-item
registerIdentifier(
"list-item",
/^\s*-\s(.*?)$/gm,
/(?<=^|\n) *(?:-|\d+\.)\s(.*?)(?=\n|$)/g,
(s, rx) => {
return {
content: s.match(new RegExp(rx, ""))?.at(1) ||
"Unable to parse list-item",
content:
s.match(new RegExp(rx, ""))?.at(1) || "Unable to parse list-item",
raw: s,
metadata: {
initialDepth:
@ -245,13 +297,11 @@ export const buildOnlyDefaultElements = () => {
return (
<li data-depth={metadata.initialDepth} className="ml-2">
{children?.map((c) => (
<Fragment key={c.uuid}>
(c.render(c))
</Fragment>
<Fragment key={c.uuid}>{c.render(c)}</Fragment>
))}
</li>
);
},
}
);
// heading
@ -259,8 +309,8 @@ export const buildOnlyDefaultElements = () => {
"heading",
/^#+\s(.*?)$/gm,
(s, rx) => {
const content = s.match(new RegExp(rx, ""))?.at(1) ||
"Unable to parse heading";
const content =
s.match(new RegExp(rx, ""))?.at(1) || "Unable to parse heading";
return {
content: content,
raw: s,
@ -287,7 +337,7 @@ export const buildOnlyDefaultElements = () => {
{token.content}
</div>
);
},
}
);
// image
@ -319,13 +369,12 @@ export const buildOnlyDefaultElements = () => {
USE_PROFILES: { svg: true },
}),
}}
>
</div>
></div>
);
}
// eslint-disable-next-line @next/next/no-img-element
return <img src={metadata.src} alt={token.content} />;
},
}
);
// anchor
@ -360,14 +409,16 @@ export const buildOnlyDefaultElements = () => {
const { metadata } = token;
return (
<Link
className={metadata.classes ||
"dark:text-primary-600 underline dark:no-underline"}
className={
metadata.classes ||
"dark:text-primary-600 underline dark:no-underline"
}
href={metadata.href}
>
{token.content}
</Link>
);
},
}
);
// inline-code
@ -376,8 +427,8 @@ export const buildOnlyDefaultElements = () => {
/(?<=\s|^)`(.*?)`(?=[\s,.!?)]|$)/gi,
(s, rx) => {
return {
content: s.match(new RegExp(rx, "i"))?.at(1) ||
"Unable to parse inline-code",
content:
s.match(new RegExp(rx, "i"))?.at(1) || "Unable to parse inline-code",
raw: s,
metadata: {},
uuid: crypto.randomUUID(),
@ -390,7 +441,7 @@ export const buildOnlyDefaultElements = () => {
{token.content}
</span>
);
},
}
);
// bold
@ -408,7 +459,7 @@ export const buildOnlyDefaultElements = () => {
},
(token) => {
return <span className="font-bold">{token.content}</span>;
},
}
);
// italic
@ -417,8 +468,8 @@ export const buildOnlyDefaultElements = () => {
/(?<!\*)\*([^\*]+?)\*(?!\*)/g,
(s, rx) => {
return {
content: s.match(new RegExp(rx, "i"))?.at(1) ||
"Unable to parse italic",
content:
s.match(new RegExp(rx, "i"))?.at(1) || "Unable to parse italic",
raw: s,
metadata: {},
uuid: crypto.randomUUID(),
@ -427,7 +478,7 @@ export const buildOnlyDefaultElements = () => {
},
(token) => {
return <span className="italic">{token.content}</span>;
},
}
);
// popover
@ -449,9 +500,11 @@ export const buildOnlyDefaultElements = () => {
const { children, metadata, uuid } = token;
return (
<Poppable
content={children?.map((c) => (
content={
children?.map((c) => (
<Fragment key={uuid}>{c.render(c)}</Fragment>
)) || token.content}
)) || token.content
}
preferredAlign="centered"
preferredEdge="bottom"
className="cursor-pointer mx-2"
@ -461,7 +514,7 @@ export const buildOnlyDefaultElements = () => {
</span>
</Poppable>
);
},
}
);
registerIdentifier(
@ -490,7 +543,7 @@ export const buildOnlyDefaultElements = () => {
</Accordion>
</div>
);
},
}
);
registerIdentifier(
@ -507,8 +560,6 @@ export const buildOnlyDefaultElements = () => {
(token) => {
const { children } = token;
debugger;
return (
<div className="p">
{children?.map((e) => {
@ -516,7 +567,7 @@ export const buildOnlyDefaultElements = () => {
})}
</div>
);
},
}
);
registerIdentifier(
@ -533,7 +584,7 @@ export const buildOnlyDefaultElements = () => {
},
() => {
return <div className="w-full border-b border-mixed-500 my-3"></div>;
},
}
);
registerIdentifier(
@ -550,7 +601,7 @@ export const buildOnlyDefaultElements = () => {
},
() => {
return <></>;
},
}
);
registerIdentifier(
@ -568,7 +619,7 @@ export const buildOnlyDefaultElements = () => {
},
(token) => {
return <>{token.raw}</>;
},
}
);
registerIdentifier(
@ -622,7 +673,7 @@ export const buildOnlyDefaultElements = () => {
}
const maxColumns = Math.max(
...[...headerRows, ...bodyRows, ...footerRows].map((r) => r.length),
...[...headerRows, ...bodyRows, ...footerRows].map((r) => r.length)
);
return {
@ -680,8 +731,12 @@ export const buildOnlyDefaultElements = () => {
<td
key={r.join() + i + c}
className="data-[center=true]:text-center"
data-center={!!(columnPattern?.at(i) &&
columnPattern.at(i)?.includes("^"))}
data-center={
!!(
columnPattern?.at(i) &&
columnPattern.at(i)?.includes("^")
)
}
>
{child?.render(child) || c}
</td>
@ -709,7 +764,7 @@ export const buildOnlyDefaultElements = () => {
)}
</table>
);
},
}
);
return TokenIdentifiers;
@ -718,7 +773,7 @@ export const buildOnlyDefaultElements = () => {
function findMatchingClosedParenthesis(
str: string,
openRegex: RegExp,
closedRegex: RegExp,
closedRegex: RegExp
): number | null {
let openings = 0;
let closings = 0;
@ -774,7 +829,7 @@ function search(
start: number,
end: number,
openRx: RegExp,
closeRx: RegExp,
closeRx: RegExp
): SearchResult {
const oldEnd = end;
@ -782,7 +837,7 @@ function search(
s,
// s.substring(0, end - start),
openRx,
closeRx,
closeRx
);
if (newEnd === null) throw Error("There was an issue finding a closing tag");

View File

@ -5,6 +5,7 @@ import { buildOnlyDefaultElements, TokenRenderers } from "./TokenIdentifiers";
export const createElements = (body: string): Token[] => {
const tokens = tokenize(body);
console.log(tokens);
return buildAbstractSyntaxTree(tokens).map((t) => t.token);
};
@ -109,7 +110,7 @@ type ParentChildMap = {
};
const parentChildMap: ParentChildMap = {
"list": ["list-item"],
list: ["list-item"],
// Add more mappings as needed...
};
@ -128,10 +129,8 @@ function filterOverlappingPBlocks(blocks: TokenMarker[]): TokenMarker[] {
for (const otherBlock of blocks) {
if (
otherBlock !== block &&
(
otherBlock.start === block.start ||
(otherBlock.end === block.end && otherBlock.start < block.start)
)
(otherBlock.start === block.start ||
(otherBlock.end === block.end && otherBlock.start < block.start))
) {
return false;
}
@ -155,7 +154,8 @@ const contentToChildren = (token: Token) => {
}
token.children = zipArrays(
content.split(splitMarker).map((c): Token => ({
content.split(splitMarker).map(
(c): Token => ({
content: c.replaceAll("\n", " "),
metadata: {},
raw: c,
@ -163,7 +163,8 @@ const contentToChildren = (token: Token) => {
uuid: crypto.randomUUID(),
rendersContentOnly: token.rendersChildrenOnly ? false : true,
render: TokenRenderers.get(token.rendersChildrenOnly ? "p" : "text")!,
children: token.rendersChildrenOnly && c.replaceAll("\n", "")
children:
token.rendersChildrenOnly && c.replaceAll("\n", "")
? [
{
content: c.replaceAll("\n", " "),
@ -176,8 +177,9 @@ const contentToChildren = (token: Token) => {
},
]
: undefined,
})),
token.children || [],
})
),
token.children || []
).filter((c) => c.children?.length || (c.rendersContentOnly && c.content));
};