tcmd: token uuids, fixes a few issues with poppables

This commit is contained in:
Emmaline Autumn 2024-02-29 01:38:30 -07:00
parent ce83bdf7af
commit 2e8ddd8ed2
14 changed files with 96 additions and 28 deletions

View File

@ -42,6 +42,6 @@
}
.poppable {
@apply card bg-mixed-300 p-2 rounded-lg transition-opacity data-[visible=true]:z-10 data-[visible=true]:opacity-100 data-[visible=false]:opacity-0 -z-10 max-w-[400px] absolute
@apply card dark:bg-mixed-300 bg-primary-600 p-2 rounded-lg transition-opacity data-[visible=true]:z-10 data-[visible=true]:opacity-100 data-[visible=false]:opacity-0 -z-10 max-w-[400px] absolute
}
}

View File

@ -13,7 +13,7 @@ import { Suspense } from "react";
export default function Home() {
return (
<>
<section className="pb-8 border-b border-b-dark-500 min-w-full">
<section className="pb-8 border-b border-b-primary-500 dark:border-b-dark-500 min-w-full">
<h2 className="strapline">Tabletop Commander</h2>
<h1 className="text-5xl font-bold">How does it work?</h1>
</section>

BIN
bun.lockb

Binary file not shown.

View File

@ -3,10 +3,11 @@
import { Accordion, AccordionContent } from "@/lib/accordion";
import { Poppable } from "@/lib/poppables/components/poppable";
import { createElements } from "@/lib/tcmd";
import { tokenizeInline } from "@/lib/tcmd/tokenizeInline";
import Link from "next/link";
import React, { FC, Fragment, ReactNode, use, useMemo } from "react";
import { sanitize } from "isomorphic-dompurify";
export const TCMD: FC<{ body: Promise<string> }> = ({ body }) => {
const text = use(body);
const elements = useMemo(() => createElements(text), [text]);
@ -15,9 +16,8 @@ export const TCMD: FC<{ body: Promise<string> }> = ({ body }) => {
// <pre>{JSON.stringify(elements,null,2)}</pre>
// </div>
<div>
{elements.map((e, i) => (
<Fragment key={"tcmd-block" + i}>{renderBlock(e)}</Fragment>
))}
{elements.map((e, i) => <Fragment key={e.uuid}>{renderBlock(e)}
</Fragment>)}
</div>
);
};
@ -26,7 +26,7 @@ const renderBlock = (block: BlockChildren): ReactNode => {
switch (block.type) {
case "block":
return block.children.map((e, i) => (
<Fragment key={"tcmd-block" + i}>{renderBlock(e)}</Fragment>
<Fragment key={e.uuid}>{renderBlock(e)}</Fragment>
));
case "grid":
return (
@ -37,7 +37,7 @@ const renderBlock = (block: BlockChildren): ReactNode => {
className="grid grid-cols-dynamic gap-x-8 gap-y-6 mb-6"
>
{block.children.map((c, i) => (
<div key={"block-grid" + c.type + i}>
<div key={c.uuid}>
{renderBlock(c)}
</div>
))}
@ -47,7 +47,7 @@ const renderBlock = (block: BlockChildren): ReactNode => {
return (
<div className="card">
{block.children.map((e, i) => (
<Fragment key={"card-block" + i + e.type}>
<Fragment key={e.uuid}>
{renderBlock(e)}
</Fragment>
))}
@ -60,7 +60,7 @@ const renderBlock = (block: BlockChildren): ReactNode => {
>
<AccordionContent>
{block.children.map((e, i) => (
<Fragment key={"accordion" + e.type + "i"}>
<Fragment key={e.uuid}>
{renderBlock(e)}
</Fragment>
))}
@ -80,7 +80,7 @@ const renderParagraph = (p: ParagraphToken) => {
return (
<div className="p">
{p.content.map((e, i) => (
<Fragment key={"p-paragraph" + i + e.type}>
<Fragment key={e.uuid}>
{renderToken(e)}
</Fragment>
))}
@ -88,7 +88,7 @@ const renderParagraph = (p: ParagraphToken) => {
);
case "code":
return (
<pre className="whitespace-pre-wrap">
<pre className="whitespace-pre-wrap bg-black/20 p-2 rounded-md">
{p.content.map((c) => c.line.toString()).join("\n\n")}
</pre>
);
@ -143,7 +143,7 @@ const renderToken = (t: Token) => {
return (
<div className="p">
{t.lines.map((e, i) => (
<Fragment key={"p-line" + i + e.type}>
<Fragment key={e.uuid}>
{renderInlineToken(e.line)}
</Fragment>
))}
@ -159,7 +159,7 @@ const renderToken = (t: Token) => {
case "list1":
return <li className="list-disc ml-6">{renderInlineToken(t.line)}</li>;
case "list2":
return <li className="list-disc ml-8">{renderInlineToken(t.line)}</li>;
return <li className="list-disc ml-12">{renderInlineToken(t.line)}</li>;
default:
return (
<div className="text-white bg-red-500 p">
@ -174,7 +174,7 @@ const renderInlineToken = (l: Line) => {
if (typeof l === "string") return l;
return l.map((token) => (
<Fragment key={"inline_token" + token.content}>
<Fragment key={token.uuid}>
{(() => {
switch (token.type) {
case "text":
@ -183,13 +183,30 @@ const renderInlineToken = (l: Line) => {
return <span className="font-bold">{token.content}</span>;
case "anchor":
return (
<Link className="text-primary-600" href={token.data.href}>
<Link
className="dark:text-primary-600 underline dark:no-underline"
href={token.data.href}
>
{token.content}
</Link>
);
case "image":
case "image": {
token.data.src = token.data.src as string;
if (token.data.src.startsWith("<svg")) {
return (
<div
dangerouslySetInnerHTML={{
__html: sanitize(token.data.src, {
USE_PROFILES: { svg: true },
}),
}}
>
</div>
);
}
// eslint-disable-next-line @next/next/no-img-element
return <img src={token.data.src} alt={token.content} />;
}
case "popover":
return (
<Poppable

View File

@ -1,3 +1,5 @@
"use client";
import {
FC,
PropsWithChildren,

View File

@ -1,7 +1,6 @@
import { zipArrays } from "../zip";
import { singleLineTokens, tokenizeLine } from "./tokenizeLine";
import { tokenizeLine } from "./tokenizeLine";
import { tokenizeBlock } from "./tokenizeBlock";
import { tokenizeInline } from "./tokenizeInline";
import { tokenizeParagraph } from "./tokenizeParagraph";
export const createElements = (body: string) => {
@ -43,6 +42,7 @@ const tokenize = (body: string) => {
closed: false,
metadata: {},
type: "block",
uuid: crypto.randomUUID(),
};
blockTokens.push(openBT);
}
@ -66,6 +66,7 @@ const tokenize = (body: string) => {
line: paragraph,
raw: paragraph,
type: "text",
uuid: crypto.randomUUID(),
});
}
@ -78,6 +79,7 @@ const tokenize = (body: string) => {
content: [],
metadata: {},
type: "p",
uuid: crypto.randomUUID(),
};
openBT.children.push(openP);
paragraphTokens.push(openP);

View File

@ -26,6 +26,7 @@ const blockTokens: {
},
children: [],
closed: false,
uuid: crypto.randomUUID(),
};
},
},
@ -38,6 +39,7 @@ const blockTokens: {
metadata: {},
children: [],
closed: false,
uuid: crypto.randomUUID(),
};
},
},
@ -51,6 +53,7 @@ const blockTokens: {
metadata: { title },
children: [],
closed: false,
uuid: crypto.randomUUID(),
};
},
},

View File

@ -1,7 +1,6 @@
import { zipArrays } from "../zip";
export const tokenizeInline = (line: string, recursive?: boolean) => {
if (recursive) console.log("recursive call");
export const tokenizeInline = (line: string) => {
line = line.trim();
const originalLine = line;
const insertMarker = "\u{03A9}";
@ -43,6 +42,7 @@ export const tokenizeInline = (line: string, recursive?: boolean) => {
line.split(new RegExp(insertMarker + "{2,}")).map((t): InlineToken => ({
content: t,
type: "text",
uuid: crypto.randomUUID(),
})),
tokens,
).filter((t) => t.content);
@ -69,6 +69,7 @@ export const inlineTokens: {
type: "bold",
end,
start,
uuid: crypto.randomUUID(),
});
},
replace(l) {
@ -87,6 +88,7 @@ export const inlineTokens: {
},
start,
end,
uuid: crypto.randomUUID(),
});
},
replace(l) {
@ -106,6 +108,7 @@ export const inlineTokens: {
data: {
src,
},
uuid: crypto.randomUUID(),
});
},
replace(l) {
@ -116,7 +119,6 @@ export const inlineTokens: {
rx: /\^\[(.*?)\]<<(.*?)>>/gm,
create(content, start, end, tokens) {
const [_, text, popover] = content;
// tokenizeInline("", true);
tokens.push({
content: text,
end,
@ -125,6 +127,7 @@ export const inlineTokens: {
data: {
popover: tokenizeInline(popover),
},
uuid: crypto.randomUUID(),
});
},
replace(l) {

View File

@ -26,6 +26,7 @@ export const tokenizeLine = (
line: tokenizeInline(line),
type: "text",
raw: line,
uuid: crypto.randomUUID(),
};
};
@ -33,28 +34,53 @@ export const singleLineTokens: SingleLineCfg[] = [
{
rx: /^#\s/,
create(line) {
return ({ type: "h1", line, raw: line, cfg: this });
return ({
type: "h1",
line,
raw: line,
cfg: this,
uuid: crypto.randomUUID(),
});
},
replaceRx: /^#\s/,
},
{
rx: /^##\s/,
create(line) {
return ({ type: "h2", line, raw: line, cfg: this });
return ({
type: "h2",
line,
raw: line,
cfg: this,
uuid: crypto.randomUUID(),
});
},
replaceRx: /^##\s/,
},
{
rx: /^###\s/,
create(line) {
return ({ type: "h3", line, raw: line, cfg: this });
return ({
type: "h3",
line,
raw: line,
cfg: this,
uuid: crypto.randomUUID(),
});
},
replaceRx: /^###\s/,
},
{
rx: /^-\s/,
create(line) {
return ({ type: "list1", line, raw: line, mends: true, cfg: this });
return ({
type: "list1",
line,
raw: line,
mends: true,
cfg: this,
uuid: crypto.randomUUID(),
});
},
replaceRx: /^-\s/,
shouldMendNextLine: true,
@ -62,7 +88,14 @@ export const singleLineTokens: SingleLineCfg[] = [
{
rx: /^[\t\s]{2}-\s/,
create(line) {
return ({ type: "list2", line, raw: line, mends: true, cfg: this });
return ({
type: "list2",
line,
raw: line,
mends: true,
cfg: this,
uuid: crypto.randomUUID(),
});
},
replaceRx: /^[\t\s]{2}-\s/,
shouldMendNextLine: true,

View File

@ -34,8 +34,10 @@ const blockTokens: {
line: line.replace(/```.*?\n/g, "").replace(/\n```/, ""),
type: "text",
raw: line,
uuid: crypto.randomUUID(),
}],
allowsInline: false,
uuid: crypto.randomUUID(),
};
},
},

View File

@ -10,6 +10,7 @@
},
"dependencies": {
"@heroicons/react": "^2.1.1",
"isomorphic-dompurify": "^2.4.0",
"next": "14.1.0",
"react": "^18",
"react-dom": "^18"

View File

@ -7,7 +7,6 @@ const config: Config = {
"./lib/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {

View File

@ -85,6 +85,8 @@ this is the test of a single accordion
![Goofy](https://yt3.ggpht.com/a/AATXAJwbIW0TwEhqdT2ZPeSB1AtdtWD2ZXam80oijg=s900-c-k-c0xffffffff-no-rj-mo)
![svg test](<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="1.5rem" height="1.5rem"><path stroke-linecap="round" stroke-linejoin="round" d="M7.5 7.5h-.75A2.25 2.25 0 0 0 4.5 9.75v7.5a2.25 2.25 0 0 0 2.25 2.25h7.5a2.25 2.25 0 0 0 2.25-2.25v-7.5a2.25 2.25 0 0 0-2.25-2.25h-.75m-6 3.75 3 3m0 0 3-3m-3 3V1.5m6 9h.75a2.25 2.25 0 0 1 2.25 2.25v7.5a2.25 2.25 0 0 1-2.25 2.25h-7.5a2.25 2.25 0 0 1-2.25-2.25v-.75" /></svg>)
[/accordion]
]]

4
types.d.ts vendored
View File

@ -2,6 +2,7 @@ type InlineToken = {
type: "text" | "bold" | "anchor" | "image" | "popover";
content: string;
data?: any;
uuid: string;
};
type InlineTokenInsert = {
@ -29,6 +30,7 @@ type SingleLineToken = {
raw: string;
mends?: boolean;
cfg?: SingleLineCfg;
uuid: string;
};
type Token = SingleLineToken | MultilineToken;
@ -47,6 +49,7 @@ type BlockToken = {
children: BlockChildren[];
parent?: string;
closed: boolean;
uuid: string;
};
type BlockChildren = ParagraphToken | BlockToken | SingleLineToken;
@ -57,4 +60,5 @@ type ParagraphToken = {
type: "p" | "code";
metadata: any;
closed: boolean;
uuid: string;
};