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"; 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
; } 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}

; } }; export type Delta = { insert: string; attributes?: { strong?: {}; code?: {}; em?: {}; underline?: {}; strikethrough?: {}; highlight?: { color: string }; link?: { href: string }; }; }; 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; } export function YJSFragmentToString( node: XmlElement | XmlText | XmlHook, ): string { if (node.constructor === XmlElement) { // Handle hard_break nodes specially if (node.nodeName === "hard_break") { return "\n"; } return node .toArray() .map((f) => YJSFragmentToString(f)) .join(""); } if (node.constructor === XmlText) { return (node.toDelta() as Delta[]) .map((d) => { return d.insert; }) .join(""); } return ""; }