Bug: Block math equation is falsely converted to inline equation on MarkdownShortcutPlugin
Typing an equation like "$$x^2 + y^2 = z^2$$" via "Insert Equation" and then pressing the MarkdownShortcutPlugin changes the equation to inline ($...$).
Lexical version: Current Playground version
Steps To Reproduce
- Enter an equation, e.g. "x^2 + y^2 = z^2" via the toolbar (Insert -> Insert Equation) and uncheck "inline".
- Press the Markdown button
Link to code example: https://playground.lexical.dev/
The current behavior
Block math equations like "$$...$$" are converted to inline "$...$" when using the ##MarkdownShortcutPlugin.
The expected behavior
Block math equations like "$$...$$" should stay the same when using the MarkdownShortcutPlugin.
Potential Solutions
Possibly quick: A new BLOCK_EQUATION transformer could be added to address the "$$" scenario. MarkdownTransformers Way more complicated: One might migrate "$, $$" in general to modern LaTex syntax like "[...]"
Impact of fix
Users who rely on markdown capabilities are type safe with equations.
I may be wrong but I dont think there is a need for a new transformer... The issue might stem from EQUATION only handling inline. if its possible to accommodate both inline and block.
i tried to rewrite the EQUATION but it seems buggy and crashed the playground editor lol.
export const EQUATION: TextMatchTransformer = {
dependencies: [EquationNode],
export: (node) => {
if (!$isEquationNode(node)) {
return null;
}
const equation = node.getEquation();
const isInline = node.isInline();
return isInline ? `$${equation}$` : `$$${equation}$$`;
},
importRegExp: /\$([^$]+?)\$|\$\$([^$]+?)\$\$/,
regExp: /\$([^$]+?)\$$|\$\$([^$]+?)\$\$$/,
replace: (textNode, match) => {
const [, inlineEquation, blockEquation] = match;
const equation = inlineEquation || blockEquation;
const isInline = !!inlineEquation;
const equationNode = $createEquationNode(equation, isInline);
textNode.replace(equationNode);
},
trigger: '$',
type: 'text-match',
};
I agree that a single transformer could probably handle both cases, but it needs to be implemented correctly. I would recommend writing up some unit tests, it's probably an issue with your regexes (it usually is, that syntax is tricky).
@etrepum I hope following code resolves your issue
export const EQUATION: TextMatchTransformer = {
dependencies: [EquationNode],
export: (node) => {
if (!$isEquationNode(node)) {
return null;
}
const equation = node.getEquation().trim();
const isInline = node.isInline();
// For block equations, ensure proper spacing
if (!isInline) {
return `\n\$\$\n${equation}\n\$\$\n`;
}
// For inline equations, keep them in the same line
return `\$\{equation\}\$`;
},
// Modified regex to better handle both inline and block equations
importRegExp: /\$\$([^$]+?)\$\$|\$([^$\n]+?)\$/,
regExp: /(\$\$([^$]+?)\$\$|\$([^$\n]+?)\$)$/,
replace: (textNode, match) => {
const fullMatch = match[0];
const blockEq = match[1];
const inlineEq = match[2];
// If the equation has $$ it's a block equation
const isBlock = fullMatch.startsWith('$$') && fullMatch.endsWith('$$');
const equation = isBlock ? blockEq : inlineEq;
if (!equation) {
return;
}
const equationNode = $createEquationNode(equation.trim(), !isBlock);
textNode.replace(equationNode);
},
trigger: '$',
type: 'text-match',
};
If you’d like to have that included in lexical you should submit a PR
Sure, i'll make a PR