Files
WooNooW/HTML_SOURCE_OF_TRUTH.md
dwindown 4471cd600f 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: ![alt](url)

 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
2025-11-15 20:05:50 +07:00

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:

  1. Renamed bodyhtmlContent (clarity)
  2. Made htmlContent the single source of truth
  3. Updated handleBlocksChange to sync HTML
  4. Added handleHtmlChange for code mode
  5. Fixed handleCodeModeToggle to convert properly
  6. Updated handleSave to always save HTML
  7. Updated JSX to use htmlContent
  8. 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:

  1. Hard refresh (Cmd+Shift+R)
  2. Edit in visual mode
  3. Switch to code mode → See your changes
  4. Edit in code mode
  5. Switch to visual mode → See your changes
  6. Save → All changes preserved!

No more confusion! No more lost changes! 🎊