export type MinimalTask = { id: string title: string description?: string parent_id?: string | null completed?: boolean list_name?: string subtasks?: MinimalTask[] } const escapeHtml = (value: string) => value .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, ''') const renderInline = (value: string) => { let out = escapeHtml(value) out = out.replace(/`([^`]+)`/g, '$1') out = out.replace(/\*\*([^*]+)\*\*/g, '$1') out = out.replace(/\*([^*]+)\*/g, '$1') out = out.replace( /\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, '$1', ) return out } export function renderMarkdown(markdown = '') { const lines = markdown.replace(/\r\n/g, '\n').split('\n') const html: string[] = [] let inList = false const closeList = () => { if (inList) { html.push('') inList = false } } for (const raw of lines) { const line = raw.trimEnd() if (!line.trim()) { closeList() continue } if (line.startsWith('# ')) { closeList() html.push(`

${renderInline(line.slice(2))}

`) } else if (line.startsWith('## ')) { closeList() html.push(`

${renderInline(line.slice(3))}

`) } else if (/^[-*] /.test(line)) { if (!inList) { html.push('