import { ReadTransaction, Replicache } from "replicache"; import type { Fact, ReplicacheMutators } from "src/replicache"; import { scanIndex } from "src/replicache/utils"; import { renderToStaticMarkup } from "react-dom/server"; import { RenderYJSFragment } from "components/Blocks/TextBlock/RenderYJSFragment"; import { Block } from "components/Blocks/Block"; import { List, parseBlocksToList } from "./parseBlocksToList"; import Katex from "katex"; export async function getBlocksAsHTML( rep: Replicache, selectedBlocks: Block[], ) { let data = await rep?.query(async (tx) => { let result: string[] = []; let parsed = parseBlocksToList(selectedBlocks); for (let pb of parsed) { if (pb.type === "block") result.push(await renderBlock(pb.block, tx)); else result.push( ``, ); } return result; }); return data; } async function renderList(l: List, tx: ReadTransaction): Promise { let children = ( await Promise.all(l.children.map(async (c) => await renderList(c, tx))) ).join("\n"); let [checked] = await scanIndex(tx).eav(l.block.value, "block/check-list"); return `
  • ${await renderBlock(l.block, tx)} ${ l.children.length > 0 ? `
      ${children}
    ` : "" }
  • `; } async function getAllFacts( tx: ReadTransaction, entity: string, ): Promise>> { let facts = await scanIndex(tx).eav(entity, ""); let childFacts = ( await Promise.all( facts.map((f) => { if ( f.data.type === "reference" || f.data.type === "ordered-reference" || f.data.type === "spatial-reference" ) { return getAllFacts(tx, f.data.value); } return []; }), ) ).flat(); return [...facts, ...childFacts]; } const BlockTypeToHTML: { [K in Fact<"block/type">["data"]["value"]]: ( b: Block, tx: ReadTransaction, alignment?: Fact<"block/text-alignment">["data"]["value"], ) => Promise; } = { datetime: async () => null, rsvp: async () => null, mailbox: async () => null, poll: async () => null, embed: async () => null, "bluesky-post": async () => null, math: async (b, tx, a) => { let [math] = await scanIndex(tx).eav(b.value, "block/math"); const html = Katex.renderToString(math?.data.value || "", { displayMode: true, throwOnError: false, macros: { "\\f": "#1f(#2)", }, }); return renderToStaticMarkup(
    , ); }, "horizontal-rule": async () =>
    , image: async (b, tx, a) => { let [src] = await scanIndex(tx).eav(b.value, "block/image"); if (!src) return ""; return ; }, code: async (b, tx, a) => { let [code] = await scanIndex(tx).eav(b.value, "block/code"); let [lang] = await scanIndex(tx).eav(b.value, "block/code-language"); return
    {code?.data.value || ""}
    ; }, button: async (b, tx, a) => { let [text] = await scanIndex(tx).eav(b.value, "button/text"); let [url] = await scanIndex(tx).eav(b.value, "button/url"); if (!text || !url) return ""; return ( {text.data.value} ); }, blockquote: async (b, tx, a) => { let [value] = await scanIndex(tx).eav(b.value, "block/text"); return ( ); }, heading: async (b, tx, a) => { let [value] = await scanIndex(tx).eav(b.value, "block/text"); let [headingLevel] = await scanIndex(tx).eav( b.value, "block/heading-level", ); let wrapper = ("h" + (headingLevel?.data.value || 1)) as "h1" | "h2" | "h3"; return ( ); }, link: async (b, tx, a) => { let [url] = await scanIndex(tx).eav(b.value, "link/url"); let [title] = await scanIndex(tx).eav(b.value, "link/title"); if (!url) return ""; return ( {title.data.value} ); }, card: async (b, tx, a) => { let [card] = await scanIndex(tx).eav(b.value, "block/card"); let facts = await getAllFacts(tx, card.data.value); return (
    ); }, text: async (b, tx, a) => { let [value] = await scanIndex(tx).eav(b.value, "block/text"); let [textSize] = await scanIndex(tx).eav(b.value, "block/text-size"); return ( ); }, }; async function renderBlock(b: Block, tx: ReadTransaction) { let [alignment] = await scanIndex(tx).eav(b.value, "block/text-alignment"); let toHtml = BlockTypeToHTML[b.type]; let element = await toHtml(b, tx, alignment?.data.value); return renderToStaticMarkup(element); }