import { Doc, applyUpdate, XmlElement, XmlHook, XmlText } from "yjs"; import { nodes, marks } from "prosemirror-schema-basic"; import { CSSProperties, Fragment } from "react"; import { theme } from "tailwind.config"; import * as base64 from "base64-js"; import { didToBlueskyUrl } from "src/utils/mentionUtils"; import { AtMentionLink } from "components/AtMentionLink"; import { Delta } from "src/utils/yjsFragmentToString"; type BlockElements = "h1" | "h2" | "h3" | null | "blockquote" | "p"; export function RenderYJSFragment({ value, wrapper, attrs, }: { value: string; wrapper: BlockElements; attrs?: { [k: string]: any }; }) { if (!value) return ; let doc = new Doc(); const update = base64.toByteArray(value); applyUpdate(doc, update); let [node] = doc.getXmlElement("prosemirror").toArray(); if (node.constructor === XmlElement) { switch (node.nodeName as keyof typeof nodes) { case "paragraph": { let children = node.toArray(); return ( {children.length === 0 ? (
) : ( node.toArray().map((node, index) => { if (node.constructor === XmlText) { let deltas = node.toDelta() as Delta[]; if (deltas.length === 0) return
; return ( {deltas.map((d, index) => { if (d.attributes?.link) return ( {d.insert} ); return ( {d.insert} ); })} ); } if (node.constructor === XmlElement && node.nodeName === "hard_break") { return
; } // Handle didMention inline nodes if (node.constructor === XmlElement && node.nodeName === "didMention") { const did = node.getAttribute("did") || ""; const text = node.getAttribute("text") || ""; return ( {text} ); } // Handle atMention inline nodes if (node.constructor === XmlElement && node.nodeName === "atMention") { const atURI = node.getAttribute("atURI") || ""; const text = node.getAttribute("text") || ""; return ( {text} ); } return null; }) )}
); } case "hard_break": return
; default: return null; } } return
; } const BlockWrapper = (props: { wrapper: BlockElements; children?: React.ReactNode; attrs?: { [k: string]: any }; }) => { if (props.wrapper === null && props.children === null) return
; if (props.wrapper === null) return <>{props.children}; switch (props.wrapper) { case "p": return

{props.children}

; case "blockquote": return
{props.children}
; case "h1": return

{props.children}

; case "h2": return

{props.children}

; case "h3": return

{props.children}

; } }; function attributesToStyle(d: Delta) { let props = { style: {}, className: "", } as { style: CSSProperties; className: string } & { [s: `data-${string}`]: any; }; if (d.attributes?.code) props.className += " inline-code"; if (d.attributes?.strong) props.style.fontWeight = "700"; if (d.attributes?.em) props.style.fontStyle = "italic"; if (d.attributes?.underline) props.style.textDecoration = "underline"; if (d.attributes?.strikethrough) { (props.style.textDecoration = "line-through"), (props.style.textDecorationColor = theme.colors.tertiary); } if (d.attributes?.highlight) { props.className += " highlight"; props["data-color"] = d.attributes.highlight.color; props.style.backgroundColor = d.attributes?.highlight.color === "1" ? theme.colors["highlight-1"] : d.attributes.highlight.color === "2" ? theme.colors["highlight-2"] : theme.colors["highlight-3"]; } return props; }