feat: Complete markdown syntax refinement and variable protection
✅ New cleaner syntax implemented: - [card:type] instead of [card type='type'] - [button:style](url)Text[/button] instead of [button url='...' style='...'] - Standard markdown images:  ✅ Variable protection from markdown parsing: - Variables with underscores (e.g., {order_items_table}) now protected - HTML comment placeholders prevent italic/bold parsing - All variables render correctly in preview ✅ Button rendering fixes: - Buttons work in Visual mode inside cards - Buttons work in Preview mode - Button clicks prevented in visual editor - Proper styling for solid and outline buttons ✅ Backward compatibility: - Old syntax still supported - No breaking changes ✅ Bug fixes: - Fixed order_item_table → order_items_table naming - Fixed button regex to match across newlines - Added button/image parsing to parseMarkdownBasics - Prevented button clicks on .button and .button-outline classes 📚 Documentation: - NEW_MARKDOWN_SYNTAX.md - Complete user guide - MARKDOWN_SYNTAX_AND_VARIABLES.md - Technical analysis
This commit is contained in:
64
admin-spa/src/lib/html-to-markdown.ts
Normal file
64
admin-spa/src/lib/html-to-markdown.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Convert HTML to Markdown
|
||||
* Simple converter for rich text editor output
|
||||
*/
|
||||
|
||||
export function htmlToMarkdown(html: string): string {
|
||||
if (!html) return '';
|
||||
|
||||
let markdown = html;
|
||||
|
||||
// Headings
|
||||
markdown = markdown.replace(/<h1>(.*?)<\/h1>/gi, '# $1\n\n');
|
||||
markdown = markdown.replace(/<h2>(.*?)<\/h2>/gi, '## $1\n\n');
|
||||
markdown = markdown.replace(/<h3>(.*?)<\/h3>/gi, '### $1\n\n');
|
||||
markdown = markdown.replace(/<h4>(.*?)<\/h4>/gi, '#### $1\n\n');
|
||||
|
||||
// Bold
|
||||
markdown = markdown.replace(/<strong>(.*?)<\/strong>/gi, '**$1**');
|
||||
markdown = markdown.replace(/<b>(.*?)<\/b>/gi, '**$1**');
|
||||
|
||||
// Italic
|
||||
markdown = markdown.replace(/<em>(.*?)<\/em>/gi, '*$1*');
|
||||
markdown = markdown.replace(/<i>(.*?)<\/i>/gi, '*$1*');
|
||||
|
||||
// Links
|
||||
markdown = markdown.replace(/<a\s+href="([^"]+)"[^>]*>(.*?)<\/a>/gi, '[$2]($1)');
|
||||
|
||||
// Lists
|
||||
markdown = markdown.replace(/<ul[^>]*>(.*?)<\/ul>/gis, (match, content) => {
|
||||
const items = content.match(/<li[^>]*>(.*?)<\/li>/gis) || [];
|
||||
return items.map((item: string) => {
|
||||
const text = item.replace(/<li[^>]*>(.*?)<\/li>/is, '$1').trim();
|
||||
return `- ${text}`;
|
||||
}).join('\n') + '\n\n';
|
||||
});
|
||||
|
||||
markdown = markdown.replace(/<ol[^>]*>(.*?)<\/ol>/gis, (match, content) => {
|
||||
const items = content.match(/<li[^>]*>(.*?)<\/li>/gis) || [];
|
||||
return items.map((item: string, index: number) => {
|
||||
const text = item.replace(/<li[^>]*>(.*?)<\/li>/is, '$1').trim();
|
||||
return `${index + 1}. ${text}`;
|
||||
}).join('\n') + '\n\n';
|
||||
});
|
||||
|
||||
// Paragraphs - convert to double newlines
|
||||
markdown = markdown.replace(/<p[^>]*>(.*?)<\/p>/gis, '$1\n\n');
|
||||
|
||||
// Line breaks
|
||||
markdown = markdown.replace(/<br\s*\/?>/gi, '\n');
|
||||
|
||||
// Horizontal rules
|
||||
markdown = markdown.replace(/<hr\s*\/?>/gi, '\n---\n\n');
|
||||
|
||||
// Remove remaining HTML tags
|
||||
markdown = markdown.replace(/<[^>]+>/g, '');
|
||||
|
||||
// Clean up excessive newlines
|
||||
markdown = markdown.replace(/\n{3,}/g, '\n\n');
|
||||
|
||||
// Trim
|
||||
markdown = markdown.trim();
|
||||
|
||||
return markdown;
|
||||
}
|
||||
Reference in New Issue
Block a user