Files
tolaria/apps/mobile/src/mobileEditorDocument.ts
2026-05-04 10:18:47 +02:00

73 lines
1.9 KiB
TypeScript

import { splitFrontmatter } from '@tolaria/markdown'
export type MobileEditorBlock = {
id: string
kind: 'bullet' | 'paragraph'
text: string
}
export type MobileEditorDocument = {
title: string
blocks: MobileEditorBlock[]
}
export type MobileEditorDocumentInput = {
id: string
title: string
content: string
}
export function createMobileEditorDocument(input: MobileEditorDocumentInput): MobileEditorDocument {
const [, body] = splitFrontmatter(input.content)
return {
title: input.title,
blocks: createBlocks({ body, title: input.title }),
}
}
export function createMobileEditorHtml(document: MobileEditorDocument) {
return `<h1>${escapeHtml({ value: document.title })}</h1>${document.blocks.map(blockToHtml).join('')}`
}
function createBlocks({ body, title }: { body: string; title: string }) {
return body
.split('\n')
.map((line) => line.trim())
.filter((line) => line && !isTitleHeading({ line, title }))
.map((line, index) => createBlock({ index, line }))
}
function createBlock({ index, line }: { index: number; line: string }): MobileEditorBlock {
const bulletText = bulletContent({ line })
return {
id: `${index}:${line}`,
kind: bulletText ? 'bullet' : 'paragraph',
text: bulletText ?? line,
}
}
function bulletContent({ line }: { line: string }) {
const match = /^[-*]\s+(.+)$/.exec(line)
return match?.[1] ?? null
}
function isTitleHeading({ line, title }: { line: string; title: string }) {
return line === `# ${title}`
}
function blockToHtml(block: MobileEditorBlock) {
const text = escapeHtml({ value: block.text })
return block.kind === 'bullet' ? `<ul><li>${text}</li></ul>` : `<p>${text}</p>`
}
function escapeHtml({ value }: { value: string }) {
return value
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
}