import { zipArrays } from "@/lib/zip"; import { FC, PropsWithChildren, useMemo } from "react"; export const TCMD: FC<{ body: string }> = ({ body }) => { const elements = useMemo(() => createElements(body), [body]); return ( <>
{JSON.stringify(elements,null,2)}
); }; const createElements = (body: string) => { const tokens = tokenize(body); return tokens; }; type InlineToken = { type: "text" | "bold"; content: string; }; type Line = string | InlineToken[]; type MultilineToken = { type: "code"; lines: Token[]; }; type Token = { type: "h1" | "h2" | "h3" | "p"; line: Line; }; const tokenize = (md: string) => { const tokens: (Token | MultilineToken)[] = []; md = md.replace(/(?<=[a-z])\n(?=[a-z])/g, " "); const lines = md.split("\n"); const multilineFlags = { heading: 0, }; const tokenMatches = [ { rx: /^\s*#\s/, create: (line: Line) => tokens.push({ type: "h1", line }), }, { rx: /^\s*##\s/, create: (line: Line) => tokens.push({ type: "h2", line }), }, { rx: /^\s*###\s/, create: (line: Line) => tokens.push({ type: "h3", line }), }, ]; for (let line of lines) { let foundLine = false; token: for (const token of tokenMatches) { if (!token.rx.test(line)) continue token; foundLine = true; line = line.replace(token.rx, "").trim(); const lineContent = tokenizeInline(line); token.create(lineContent); } if (foundLine) continue; tokens.push({ type: "p", line: tokenizeInline(line), }); } console.log(tokens); return tokens.filter((t) => (t as Token).line || (t as MultilineToken).lines); }; const tokenizeInline = (line: string) => { line = line.trim(); const originalLine = line; const insertMarker = "{^}"; const tokens: InlineToken[] = []; const tokenMatches = [ { rx: /\*\*(.*?)\*\*/g, create: (content: string) => tokens.push({ content, type: "bold", }), }, ]; for (const token of tokenMatches) { let match; let last = 0; while ((match = token.rx.exec(line)) !== null) { const tokenStart = match.index; const tokenEnd = match.index + match[0].length; console.log(tokenEnd, token.rx.lastIndex); token.create(line.substring(tokenStart, tokenEnd)); line = line.slice(last, tokenStart) + "{^}" + line.slice(tokenEnd, line.length); last = tokenEnd; } } if (tokens.length) { return zipArrays( line.split(insertMarker).map((t): InlineToken => ({ content: t, type: "text", })), tokens, ).filter((t) => t.content); } return originalLine; };