]*>(.*?)<\/li>/is, '$1').trim();
return `${index + 1}. ${text}`;
}).join('\n') + '\n\n';
});
// Paragraphs - preserve text-align by using placeholders
const alignedParagraphs: { [key: string]: string } = {};
let alignIndex = 0;
markdown = markdown.replace(/]*)>(.*?)<\/p>/gis, (match, attrs, content) => {
// Check for text-align in style attribute
const alignMatch = attrs.match(/text-align:\s*(center|right)/i);
if (alignMatch) {
const align = alignMatch[1].toLowerCase();
// Use double-bracket placeholder that won't be matched by HTML regex
const placeholder = `[[ALIGN${alignIndex}]]`;
alignedParagraphs[placeholder] = `
${content}
`;
alignIndex++;
return placeholder + '\n\n';
}
// No alignment, convert to plain text
return `${content}\n\n`;
});
// Line breaks
markdown = markdown.replace(/
/gi, '\n');
// Horizontal rules
markdown = markdown.replace(/
/gi, '\n---\n\n');
// Remove remaining HTML tags
markdown = markdown.replace(/<[^>]+>/g, '');
// Restore aligned paragraphs
Object.entries(alignedParagraphs).forEach(([placeholder, html]) => {
markdown = markdown.replace(placeholder, html);
});
// Restore aligned headings
Object.entries(alignedHeadings).forEach(([placeholder, html]) => {
markdown = markdown.replace(placeholder, html);
});
// Clean up excessive newlines
markdown = markdown.replace(/\n{3,}/g, '\n\n');
// Trim
markdown = markdown.trim();
return markdown;
}