/** * Minimal builders for Lexical's SerializedEditorState JSON — just enough to * seed richText fields from plain paragraph/heading strings (see lib/texte.ts). * Shape verified against node_modules/lexical's Serialized*Node types. */ type TextNode = { type: "text"; version: 1; text: string; format: 0; detail: 0; mode: "normal"; style: ""; }; type ParagraphNode = { type: "paragraph"; version: 1; children: TextNode[]; direction: "ltr"; format: ""; indent: 0; }; type HeadingNode = { type: "heading"; tag: "h2" | "h3"; version: 1; children: TextNode[]; direction: "ltr"; format: ""; indent: 0; }; function textNode(text: string): TextNode { return { type: "text", version: 1, text, format: 0, detail: 0, mode: "normal", style: "" }; } function paragraphNode(text: string): ParagraphNode { return { type: "paragraph", version: 1, children: [textNode(text)], direction: "ltr", format: "", indent: 0, }; } function headingNode(text: string, tag: "h2" | "h3" = "h2"): HeadingNode { return { type: "heading", tag, version: 1, children: [textNode(text)], direction: "ltr", format: "", indent: 0, }; } function root(children: (ParagraphNode | HeadingNode)[]) { return { root: { type: "root", children, direction: "ltr" as const, format: "" as const, indent: 0, version: 1, }, }; } export function lexicalFromParagraphs(paragraphs: readonly string[]) { return root(paragraphs.map((p) => paragraphNode(p))); } export function lexicalFromSections( intro: readonly string[], sections?: readonly { heading: string; paragraphs: readonly string[] }[], ) { const children: (ParagraphNode | HeadingNode)[] = intro.map((p) => paragraphNode(p)); if (sections) { for (const section of sections) { children.push(headingNode(section.heading)); children.push(...section.paragraphs.map((p) => paragraphNode(p))); } } return root(children); }