Skip to content

Commit c21129b

Browse files
author
xlx1212
committed
fix(markdown-editor): preserve LaTeX backslashes for KaTeX rendering
analyzeMarkdownEditability canonicalizes markdown through a tiptap round-trip that calls escapeMarkdownPlainText, which doubles every backslash. This turns LaTeX commands like \int into \\int, \frac into \\frac, etc. KaTeX interprets \\ as a line break, breaking math rendering in the file viewer preview. Fix: use raw markdown content instead of canonicalMarkdown in both content normalization paths (toNormalizedMarkdown and initialContent loading). The tiptap editor normalizes content internally, so the canonical form is not needed for display. Add a root-cause test in tiptapMarkdown.test.ts documenting that canonicalMarkdown doubles backslashes in LaTeX math content. Closes #1952
1 parent 6f89691 commit c21129b

2 files changed

Lines changed: 21 additions & 5 deletions

File tree

src/web-ui/src/tools/editor/components/MarkdownEditor.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,10 @@ const MarkdownEditor: React.FC<MarkdownEditorProps> = ({
143143

144144
const toNormalizedMarkdown = useCallback((raw: string) => {
145145
const nextEditability = analyzeMarkdownEditability(raw);
146-
const nextContent =
147-
nextEditability.mode === 'unsafe' ? raw : nextEditability.canonicalMarkdown;
146+
// Use raw content instead of canonicalMarkdown to avoid doubling
147+
// backslashes in LaTeX commands (e.g. \int → \\int), which breaks
148+
// KaTeX rendering. The tiptap editor normalizes content internally.
149+
const nextContent = raw;
148150
return { nextEditability, nextContent };
149151
}, []);
150152

@@ -262,9 +264,9 @@ const MarkdownEditor: React.FC<MarkdownEditorProps> = ({
262264
}
263265
} else if (initialContent !== undefined) {
264266
const nextEditability = analyzeMarkdownEditability(initialContent);
265-
const nextContent = nextEditability.mode === 'unsafe'
266-
? initialContent
267-
: nextEditability.canonicalMarkdown;
267+
// Use raw content instead of canonicalMarkdown to avoid doubling
268+
// backslashes in LaTeX commands, which breaks KaTeX rendering.
269+
const nextContent = initialContent;
268270

269271
setEditability(nextEditability);
270272
setContent(nextContent);

src/web-ui/src/tools/editor/meditor/utils/tiptapMarkdown.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,4 +334,18 @@ describe('tiptap markdown compatibility', () => {
334334
expect(analysis.containsRawHtmlBlocks).toBe(true);
335335
expect(doc.content?.[0]?.type).toBe('rawHtmlBlock');
336336
});
337+
338+
it('doubles backslashes in LaTeX math during canonical round-trip', () => {
339+
const markdown = String.raw`$$\int_0^1 x^2 dx = \frac13$$`;
340+
const analysis = analyzeMarkdownEditability(markdown);
341+
342+
// Root cause of #1952: escapeMarkdownPlainText doubles every backslash
343+
// during canonical serialization, turning \int into \\int, \frac into
344+
// \\frac, etc. KaTeX interprets \\ as a line break, so the math renders
345+
// incorrectly. This is why MarkdownEditor uses raw content instead of
346+
// canonicalMarkdown for display.
347+
expect(analysis.canonicalMarkdown).toContain(String.raw`\\int`);
348+
expect(analysis.canonicalMarkdown).toContain(String.raw`\\frac`);
349+
expect(analysis.canonicalMarkdown).not.toBe(markdown);
350+
});
337351
});

0 commit comments

Comments
 (0)