diff --git a/app/globals.css b/app/globals.css index 361f316..8998ba2 100644 --- a/app/globals.css +++ b/app/globals.css @@ -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 } } diff --git a/app/page.tsx b/app/page.tsx index 83360dc..793017d 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -13,7 +13,7 @@ import { Suspense } from "react"; export default function Home() { return ( <> -
+

Tabletop Commander

How does it work?

diff --git a/bun.lockb b/bun.lockb index ac17f92..8384bf0 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/tcmd/index.tsx b/components/tcmd/index.tsx index 00b370b..2f5476c 100644 --- a/components/tcmd/index.tsx +++ b/components/tcmd/index.tsx @@ -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 }> = ({ body }) => { const text = use(body); const elements = useMemo(() => createElements(text), [text]); @@ -15,9 +16,8 @@ export const TCMD: FC<{ body: Promise }> = ({ body }) => { //
{JSON.stringify(elements,null,2)}
//
- {elements.map((e, i) => ( - {renderBlock(e)} - ))} + {elements.map((e, i) => {renderBlock(e)} + )}
); }; @@ -26,7 +26,7 @@ const renderBlock = (block: BlockChildren): ReactNode => { switch (block.type) { case "block": return block.children.map((e, i) => ( - {renderBlock(e)} + {renderBlock(e)} )); 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) => ( -
+
{renderBlock(c)}
))} @@ -47,7 +47,7 @@ const renderBlock = (block: BlockChildren): ReactNode => { return (
{block.children.map((e, i) => ( - + {renderBlock(e)} ))} @@ -60,7 +60,7 @@ const renderBlock = (block: BlockChildren): ReactNode => { > {block.children.map((e, i) => ( - + {renderBlock(e)} ))} @@ -80,7 +80,7 @@ const renderParagraph = (p: ParagraphToken) => { return (
{p.content.map((e, i) => ( - + {renderToken(e)} ))} @@ -88,7 +88,7 @@ const renderParagraph = (p: ParagraphToken) => { ); case "code": return ( -
+        
           {p.content.map((c) => c.line.toString()).join("\n\n")}
         
); @@ -143,7 +143,7 @@ const renderToken = (t: Token) => { return (
{t.lines.map((e, i) => ( - + {renderInlineToken(e.line)} ))} @@ -159,7 +159,7 @@ const renderToken = (t: Token) => { case "list1": return
  • {renderInlineToken(t.line)}
  • ; case "list2": - return
  • {renderInlineToken(t.line)}
  • ; + return
  • {renderInlineToken(t.line)}
  • ; default: return (
    @@ -174,7 +174,7 @@ const renderInlineToken = (l: Line) => { if (typeof l === "string") return l; return l.map((token) => ( - + {(() => { switch (token.type) { case "text": @@ -183,13 +183,30 @@ const renderInlineToken = (l: Line) => { return {token.content}; case "anchor": return ( - + {token.content} ); - case "image": + case "image": { + token.data.src = token.data.src as string; + if (token.data.src.startsWith(" +
    + ); + } // eslint-disable-next-line @next/next/no-img-element return {token.content}; + } case "popover": return ( { @@ -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); diff --git a/lib/tcmd/tokenizeBlock.ts b/lib/tcmd/tokenizeBlock.ts index 33bd3d2..6975abd 100644 --- a/lib/tcmd/tokenizeBlock.ts +++ b/lib/tcmd/tokenizeBlock.ts @@ -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(), }; }, }, diff --git a/lib/tcmd/tokenizeInline.ts b/lib/tcmd/tokenizeInline.ts index a55e0c8..73cca93 100644 --- a/lib/tcmd/tokenizeInline.ts +++ b/lib/tcmd/tokenizeInline.ts @@ -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) { diff --git a/lib/tcmd/tokenizeLine.ts b/lib/tcmd/tokenizeLine.ts index 6288a08..af73f7e 100644 --- a/lib/tcmd/tokenizeLine.ts +++ b/lib/tcmd/tokenizeLine.ts @@ -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, diff --git a/lib/tcmd/tokenizeParagraph.ts b/lib/tcmd/tokenizeParagraph.ts index d567e32..01c1537 100644 --- a/lib/tcmd/tokenizeParagraph.ts +++ b/lib/tcmd/tokenizeParagraph.ts @@ -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(), }; }, }, diff --git a/package.json b/package.json index 93df53d..439a382 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@heroicons/react": "^2.1.1", + "isomorphic-dompurify": "^2.4.0", "next": "14.1.0", "react": "^18", "react-dom": "^18" diff --git a/tailwind.config.ts b/tailwind.config.ts index 1a868f0..5f31dae 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -7,7 +7,6 @@ const config: Config = { "./lib/**/*.{js,ts,jsx,tsx,mdx}", "./app/**/*.{js,ts,jsx,tsx,mdx}", ], - theme: { extend: { backgroundImage: { diff --git a/test.md b/test.md index 1dc5259..8f24cdb 100644 --- a/test.md +++ b/test.md @@ -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]() + [/accordion] ]] diff --git a/types.d.ts b/types.d.ts index 21f524b..922c0ec 100644 --- a/types.d.ts +++ b/types.d.ts @@ -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; };