Worklog for task "Add local Markdown compatibility component"
Legacy HTML normalization: replacing <p> with <div> on import
When migrating old content, another conflict between legacy WYSIWYG markup and the new React/SSR rendering was discovered. The old editor wrapped large HTML fragments in <p>. In the new version, such imported HTML can end up inside an already existing paragraph, causing React to receive an invalid HTML structure and warn:
In HTML, <p> cannot be a descendant of <p>. This will cause a hydration error.
Cause
The issue is not with the specific text, but with the semantics of the <p> tag: HTML does not allow nested paragraphs. Legacy WYSIWYG used <p> as a universal block wrapper, although a neutral container like <div> is safer for arbitrary nested content.
This is especially unpleasant during SSR: the browser may automatically correct the invalid HTML structure differently than React expects, and the resulting DOM structure after parsing differs from the server markup. This leads to a hydration mismatch/error.
Decision Made
A cheerio helper has been added to the importer, which replaces all <p> tags with <div> before saving legacy HTML, preserving the inner HTML and attributes:
import * as cheerio from 'cheerio'
export function replacePWithDiv(html: string): string {
const $ = cheerio.load(html, { xml: false }, false)
$('p').each((_, el) => {
const $el = $(el)
const div = $('<div></div>')
div.html($el.html() || '')
for (const attr of el.attributes) {
div.attr(attr.name, attr.value)
}
$el.replaceWith(div)
})
return $.html()
}
Why normalization is performed in the importer
This is a legacy-specific source data issue, so it is safer to fix it at the entry point to the new system rather than forcing the general Markdown/React renderer to constantly compensate for the old WYSIWYG markup.
Here, the importer acts as a normalization layer: it preserves the meaning of the old content, but removes the structure that is known to conflict with valid HTML and React hydration.
Significance for the general Markdown task
This case complements the whitespace/indentation problem: the new pipeline must take into account not only Markdown syntax, but also the quality of embedded HTML. Even if the Markdown is formally the same, legacy HTML within it can have a structure that different parser/renderer chains process differently.
The local solution remains part of the BiznesHelper compatibility layer until a more general normalization/validation pipeline appears in haih-agent.
Before refactoring Markdown in the haih-agent core, add a local renderer to BiznesHelper that is compatible with the actual Markdown saved by MDXEditor.