import "./markdown.css"; import { ReactNode, forwardRef, useMemo } from "react"; import { Token, Tokens, marked } from "marked"; import { HyperText } from "./hypertext"; import { Text } from "./text"; interface MarkdownProps { content: string; tags?: Array>; // Render plain text directly without parsing nostr/http links plainText?: boolean; } const Markdown = forwardRef((props: MarkdownProps, ref) => { function renderToken(t: Token, key: number): ReactNode { try { switch (t.type) { case "paragraph": { return

{t.tokens ? t.tokens.map(renderToken) : t.raw}

; } case "image": { return ; } case "heading": { switch (t.depth) { case 1: return

{t.tokens ? t.tokens.map(renderToken) : t.raw}

; case 2: return

{t.tokens ? t.tokens.map(renderToken) : t.raw}

; case 3: return

{t.tokens ? t.tokens.map(renderToken) : t.raw}

; case 4: return

{t.tokens ? t.tokens.map(renderToken) : t.raw}

; case 5: return
{t.tokens ? t.tokens.map(renderToken) : t.raw}
; case 6: return
{t.tokens ? t.tokens.map(renderToken) : t.raw}
; } throw new Error("Invalid heading"); } case "codespan": { return {t.raw}; } case "code": { return
{t.raw}
; } case "br": { return
; } case "hr": { return
; } case "blockquote": { return
{t.tokens ? t.tokens.map(renderToken) : t.raw}
; } case "link": { return ( {t.tokens ? t.tokens.map(renderToken) : t.raw} ); } case "list": { if (t.ordered) { return
    {t.items.map(renderToken)}
; } else { return ; } } case "list_item": { return
  • {t.tokens ? t.tokens.map(renderToken) : t.raw}
  • ; } case "em": { return {t.tokens ? t.tokens.map(renderToken) : t.raw}; } case "del": { return {t.tokens ? t.tokens.map(renderToken) : t.raw}; } case "table": { return ( {(t.header as Tokens.TableCell[]).map((v, h_key) => ( ))} {(t.rows as Tokens.TableCell[][]).map((v, r_key) => ( {v.map((d, d_key) => ( ))} ))}
    {v.tokens ? v.tokens.map(renderToken) : v.text}
    {d.tokens ? d.tokens.map(renderToken) : d.text}
    ); } default: { if ("tokens" in t) { return (t.tokens as Array).map(renderToken); } if (props.plainText ?? false) { return t.raw; } return ; } } } catch (e) { console.error(e); } } const parsed = useMemo(() => { return marked.lexer(props.content); }, [props.content, props.tags]); return (
    {parsed.filter(a => a.type !== "footnote" && a.type !== "footnotes").map(a => renderToken(a))}
    ); }); export default Markdown;