import type { ValueSchema } from "@wrnexus/blocks"; export interface TextRun { text: string; bold?: boolean; italic?: boolean; underline?: boolean } export const richTextSchema: ValueSchema = { kind: "rich-text", parse(value = [], path = "richText") { if (!Array.isArray(value) || value.length > 4000 || value.some(run => !run || typeof run !== "object" || typeof run.text !== "string" || Object.keys(run).some(key => !["text", "bold", "italic", "underline"].includes(key)) || ["bold", "italic", "underline"].some(key => run[key] !== undefined && typeof run[key] !== "boolean")) || value.reduce((size, run) => size + run.text.length, 0) > 200000) return { ok: false, errors: [{code: "email.rich-text", message: "Invalid or oversized rich text.", severity: "error", path}] }; return { ok: true, value }; }, }; const escape = (value: string) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'").replace(/\r\n?|\n/g, "
"); /** Ignore stale formatting if a plain-text inspector or importer changed the text. */ export function renderRichText(props: Record): string { const parsed = richTextSchema.parse(props.richText); const text = String(props.text ?? ""); if (!parsed.ok || !parsed.value.length || parsed.value.map(run => run.text).join("") !== text) return escape(text); return parsed.value.map(run => { let result = escape(run.text); if (run.underline) result = `${result}`; if (run.italic) result = `${result}`; if (run.bold) result = `${result}`; return result; }).join(""); }