✅ 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
9.0 KiB
9.0 KiB
✅ HTML as Single Source of Truth - IMPLEMENTED! 🚀
Problem Solved! 🎉
Before: Confusing data flow with markdown/HTML/blocks competing After: Clean architecture with HTML as the single source of truth
The Architecture
HTML = Source of Truth
┌─────────────────────────────────────────┐
│ DATABASE (HTML) │
│ Single source of truth for all content │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ EDITOR STATE (htmlContent) │
│ Always contains the current HTML │
└─────────────────────────────────────────┘
↓
┌───────────┴───────────┐
↓ ↓
┌──────────────┐ ┌──────────────┐
│ Code Mode │ │ Visual Mode │
│ (HTML view) │ │ (Blocks view)│
└──────────────┘ └──────────────┘
How It Works
1. Loading Template
// Load from database
const template = await fetchTemplate();
// One-time conversion: Markdown → HTML (if needed)
let html = template.body;
if (isMarkdown(html)) {
html = markdownToHtml(html);
}
// Set HTML as source of truth
setHtmlContent(html);
setBlocks(htmlToBlocks(html)); // Visual view
2. Editing in Visual Mode
// User edits blocks
handleBlocksChange(newBlocks) {
setBlocks(newBlocks); // Update visual view
setHtmlContent(blocksToHTML(newBlocks)); // Sync to HTML
}
// HTML is always up-to-date!
3. Editing in Code Mode
// User edits HTML directly
handleHtmlChange(newHtml) {
setHtmlContent(newHtml); // Update HTML directly
}
// HTML is the source, no conversion needed!
4. Switching Modes
// Switching TO code mode
if (!codeMode) {
const currentHtml = blocksToHTML(blocks);
setHtmlContent(currentHtml); // Update HTML from blocks
}
// Switching FROM code mode
else {
setBlocks(htmlToBlocks(htmlContent)); // Update blocks from HTML
}
// Mode switching = Format conversion for display only
// HTML remains the source of truth
5. Saving
// Always save HTML (source of truth)
await saveTemplate({
subject,
body: htmlContent, // Just save it!
});
// No conversion needed, no confusion!
Data Flow
Visual Mode → Code Mode:
User edits blocks
↓
Blocks updated
↓
HTML updated (blocksToHTML)
↓
User switches to code mode
↓
Show HTML in editor
↓
✅ All changes preserved!
Code Mode → Visual Mode:
User edits HTML
↓
HTML updated directly
↓
User switches to visual mode
↓
Convert HTML → Blocks
↓
✅ All changes preserved!
Visual Mode → Save:
User edits blocks
↓
HTML updated continuously
↓
User clicks save
↓
Save HTML to database
↓
✅ Perfect sync!
Code Mode → Save:
User edits HTML
↓
HTML updated directly
↓
User clicks save
↓
Save HTML to database
↓
✅ Perfect sync!
What Changed
Before (Confusing):
// Multiple sources of truth
const [body, setBody] = useState(''); // HTML?
const [blocks, setBlocks] = useState([]); // Blocks?
const [markdown, setMarkdown] = useState(''); // Markdown?
// Confusing save logic
const htmlBody = codeMode ? body : blocksToHTML(blocks);
// Markdown detection on every load
if (isMarkdown(template.body)) {
// Convert...
}
// Lost changes when switching modes!
After (Clean):
// Single source of truth
const [htmlContent, setHtmlContent] = useState(''); // HTML!
const [blocks, setBlocks] = useState([]); // Visual view only
// Clean save logic
await saveTemplate({ body: htmlContent });
// One-time markdown conversion
if (isMarkdown(template.body)) {
html = markdownToHtml(template.body);
// After this, always HTML
}
// Changes always preserved!
Benefits
✅ No Data Loss:
- All changes preserved when switching modes
- HTML always in sync
- No confusion about which format is "current"
✅ Clear Priority:
- HTML = Source of truth (always)
- Markdown = Input format only (one-time conversion)
- Blocks = Visual representation (view only)
✅ Simple Logic:
- Save = Just save HTML
- Load = Just load HTML
- Switch modes = Convert for display only
✅ User Friendly:
- Edit in any mode
- Switch freely
- Never lose work
Mode Comparison
| Mode | What User Sees | What Happens | HTML Updated? |
|---|---|---|---|
| Visual | Blocks/Cards | Edit blocks → HTML synced | ✅ Yes (continuous) |
| Code | HTML code | Edit HTML directly | ✅ Yes (direct) |
| Preview | Rendered email | View only | ❌ No |
Markdown Handling
One-Time Conversion:
// On template load
if (detectContentType(template.body) === 'markdown') {
html = markdownToHtml(template.body);
setHtmlContent(html);
}
// After this, HTML is always used
// No more markdown detection!
Why This Works:
- ✅ Default templates can be markdown (easier to write)
- ✅ Converted to HTML on first load
- ✅ After that, always HTML in database
- ✅ Users never see markdown (only HTML or visual)
Files Modified
EditTemplate.tsx
Changes:
- ✅ Renamed
body→htmlContent(clarity) - ✅ Made
htmlContentthe single source of truth - ✅ Updated
handleBlocksChangeto sync HTML - ✅ Added
handleHtmlChangefor code mode - ✅ Fixed
handleCodeModeToggleto convert properly - ✅ Updated
handleSaveto always save HTML - ✅ Updated JSX to use
htmlContent - ✅ Removed markdown mode (HTML only in code mode)
Result:
- Clean, simple, no confusion
- All changes preserved
- HTML always in sync
Testing Checklist
✅ Visual Mode:
- Edit blocks
- Switch to code mode
- See HTML with all changes
- Switch back to visual
- All changes still there
✅ Code Mode:
- Edit HTML
- Switch to visual mode
- See blocks with all changes
- Switch back to code
- All changes still there
✅ Save:
- Edit in visual mode
- Save
- Reload page
- All changes preserved
- Edit in code mode
- Save
- Reload page
- All changes preserved
✅ Preview:
- Edit in any mode
- Switch to preview
- See rendered email
- All content displays
Summary
| Feature | Before | After |
|---|---|---|
| Source of truth | Unclear | ✅ HTML |
| Mode switching | Lost changes | ✅ Preserved |
| Save logic | Complex | ✅ Simple |
| Data flow | Confusing | ✅ Clear |
| Markdown handling | Every load | ✅ One-time |
| User experience | Frustrating | ✅ Smooth |
What Users See
Visual Mode:
┌─────────────────────────────┐
│ [Add Block ▼] │
├─────────────────────────────┤
│ ┌───────────────────────┐ │
│ │ 🎨 Hero Card │ │
│ │ ## Welcome! │ │
│ │ Content here... │ │
│ └───────────────────────┘ │
│ ┌───────────────────────┐ │
│ │ 📄 Default Card │ │
│ │ More content... │ │
│ └───────────────────────┘ │
└─────────────────────────────┘
Code Mode:
┌─────────────────────────────┐
│ <div class="card card-hero">│
│ <h2>Welcome!</h2> │
│ <p>Content here...</p> │
│ </div> │
│ <div class="card"> │
│ <p>More content...</p> │
│ </div> │
└─────────────────────────────┘
Behind the Scenes:
Both modes editing the SAME HTML!
✅ No data loss
✅ Perfect sync
✅ Simple architecture
🎉 COMPLETE! HTML is now the single source of truth! 🚀
Test it:
- Hard refresh (Cmd+Shift+R)
- Edit in visual mode
- Switch to code mode → See your changes
- Edit in code mode
- Switch to visual mode → See your changes
- Save → All changes preserved!
No more confusion! No more lost changes! 🎊