/** * TipTap content validation helpers shared by the admin template editor * and the document-template service. Surfaces unsupported node types so * the admin gets a clear validation error before saving, and exports the * `TEMPLATE_VARIABLES` catalog so the editor can offer token suggestions. * * Note: this file was the remaining sliver of the legacy 571-line * `tiptap-to-pdfme.ts` bridge. The pdfme rendering path it once fed has * been removed (see the PDF stack overhaul spec); only the validation + * variable catalog survive because they're still useful for the * Documenso-template-body editor UX. */ export interface TipTapMark { type: string; attrs?: Record; } export interface TipTapNode { type: string; content?: TipTapNode[]; text?: string; marks?: TipTapMark[]; attrs?: Record; } const UNSUPPORTED_NODES = new Set([ 'blockquote', 'codeBlock', 'horizontalRule', 'taskList', 'taskItem', ]); export const TEMPLATE_VARIABLES: Array<{ key: string; label: string; example: string }> = [ { key: 'client.name', label: 'Client Full Name', example: 'John Smith' }, { key: 'client.company', label: 'Company Name', example: 'Smith Holdings' }, { key: 'client.email', label: 'Client Email', example: 'john@smithholdings.com' }, { key: 'client.phone', label: 'Client Phone', example: '+61 400 000 000' }, { key: 'interest.stage', label: 'Pipeline Stage', example: 'Signed EOI/NDA' }, { key: 'interest.berthNumber', label: 'Berth Number (from interest)', example: 'A23' }, { key: 'berth.mooring_number', label: 'Berth Number', example: 'A23' }, { key: 'berth.price', label: 'Berth Price', example: '$45,000' }, { key: 'berth.tenure_type', label: 'Tenure Type', example: 'Freehold' }, { key: 'port.name', label: 'Port Name', example: 'Port Nimara' }, { key: 'port.currency', label: 'Port Currency', example: 'AUD' }, { key: 'date.today', label: "Today's Date", example: '2026-03-15' }, { key: 'date.year', label: 'Current Year', example: '2026' }, ]; /** * Recursively walks a TipTap node tree and collects any unsupported node types. * Returns an array of unsupported type names found, or empty array if valid. */ export function validateTipTapDocument(doc: TipTapNode): string[] { const found = new Set(); function walk(node: TipTapNode): void { if (UNSUPPORTED_NODES.has(node.type)) { found.add(node.type); } if (node.content) { for (const child of node.content) { walk(child); } } } walk(doc); return Array.from(found); }