diff --git a/web/src/utils/chat.ts b/web/src/utils/chat.ts index cc0024950..879289d3b 100644 --- a/web/src/utils/chat.ts +++ b/web/src/utils/chat.ts @@ -56,14 +56,25 @@ const BLOCK_MATH_RE = /\\\[([\s\S]*?)(? { - const blockProcessedContent = content.replace( + const normalizedContent = content + .replace(/\\\\\[/g, '\\[') + .replace(/\\\\\(/g, '\\(') + .replace(/\\\\\]/g, '\\]') + .replace(/\\\\\)/g, '\\)') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/&/g, '&'); + + const blockProcessedContent = normalizedContent.replace( BLOCK_MATH_RE, (_, equation) => `$$${equation}$$`, ); + const inlineProcessedContent = blockProcessedContent.replace( INLINE_MATH_RE, (_, equation) => `$${equation}$`, ); + return inlineProcessedContent; }; diff --git a/web/src/utils/tests/chat.test.ts b/web/src/utils/tests/chat.test.ts new file mode 100644 index 000000000..82b478071 --- /dev/null +++ b/web/src/utils/tests/chat.test.ts @@ -0,0 +1,26 @@ +import { preprocessLaTeX } from '../chat'; + +test('handles double-escaped inline LaTeX', () => { + const result = preprocessLaTeX('\\\\(\\\\Delta = b^2\\\\)'); + expect(result).toBe('$\\Delta = b^2$'); +}); + +test('handles double-escaped block LaTeX', () => { + const result = preprocessLaTeX('\\\\[E = mc^2\\\\]'); + expect(result).toBe('$$E = mc^2$$'); +}); + +test('decodes HTML entities', () => { + const result = preprocessLaTeX('a < b & c > d'); + expect(result).toBe('a < b & c > d'); +}); + +test('handles mixed double-escaped delimiters with HTML entities', () => { + const result = preprocessLaTeX('\\\\(x < y\\\\)'); + expect(result).toBe('$x < y$'); +}); + +test('passes through already correct single-escaped delimiters unchanged', () => { + const result = preprocessLaTeX('\\(x = 1\\)'); + expect(result).toBe('$x = 1$'); +}); \ No newline at end of file