const RICH_NOTE_PREFIX = 'sky-note-html-v1:' const HTML_ENTITIES: Record = { amp: '&', apos: "'", gt: '>', lt: '<', nbsp: ' ', quot: '"', } function decodeHtmlEntities(value: string): string { return value.replace(/&(#x?[\da-f]+|[a-z]+);/gi, (entity, code: string) => { if (code[0] === '#') { const hexadecimal = code[1]?.toLowerCase() === 'x' const numeric = Number.parseInt( code.slice(hexadecimal ? 2 : 1), hexadecimal ? 16 : 10, ) return Number.isFinite(numeric) ? String.fromCodePoint(numeric) : entity } return HTML_ENTITIES[code.toLowerCase()] ?? entity }) } function escapeHtml(value: string): string { return value .split('&') .join('&') .split('<') .join('<') .split('>') .join('>') .split('"') .join('"') .split("'") .join(''') } export function isRichNoteBody(body: string): boolean { return body.startsWith(RICH_NOTE_PREFIX) } export function noteBodyToEditorHtml(body: string): string { if (isRichNoteBody(body)) return body.slice(RICH_NOTE_PREFIX.length) if (!body) return '

' return body .split(/\n{2,}/) .map( (paragraph) => `

${escapeHtml(paragraph).split('\n').join('
')}

`, ) .join('') } export function noteBodyToPlainText(body: string): string { if (!isRichNoteBody(body)) return body return decodeHtmlEntities( body .slice(RICH_NOTE_PREFIX.length) .replace(//gi, '\n') .replace(/<\/p\s*>/gi, '\n') .replace(/<\/h[1-6]\s*>/gi, '\n') .replace(/]*)?>/gi, '• ') .replace(/<\/li\s*>/gi, '\n') .replace(/<\/blockquote\s*>/gi, '\n') .replace(/<[^>]*>/g, ''), ) .replace(/\u00a0/g, ' ') .replace(/[ \t]+\n/g, '\n') .replace(/\n{3,}/g, '\n\n') .trim() } export function serializeRichNoteBody(html: string): string { return `${RICH_NOTE_PREFIX}${html}` }