diff --git a/web/src/utils/__tests__/chat.test.ts b/web/src/utils/__tests__/chat.test.ts new file mode 100644 index 0000000000..fc3488e9dd --- /dev/null +++ b/web/src/utils/__tests__/chat.test.ts @@ -0,0 +1,34 @@ +import { preprocessLaTeX } from '../chat'; + +describe('preprocessLaTeX', () => { + it('converts block \\[ \\] to $$ $$', () => { + expect(preprocessLaTeX('\\[ x + y \\]')).toBe('$$x + y$$'); + }); + + it('converts inline \\( \\) to $ $', () => { + expect(preprocessLaTeX('\\( a \\)')).toBe('$a$'); + }); + + it('does not cut block math at \\right] (Closes #13134)', () => { + const content = '\\[ C_{seq}(y|x) = \\frac{1}{|y|} \\sum_{t=1}^{|y|} \\right] \\]'; + const result = preprocessLaTeX(content); + expect(result).toContain('\\right]'); + expect(result).toContain('\\frac{1}{|y|}'); + expect(result).toBe( + '$$ C_{seq}(y|x) = \\frac{1}{|y|} \\sum_{t=1}^{|y|} \\right] $$', + ); + }); + + it('does not cut inline math at \\big) or nested parens', () => { + const content = '\\( f(x) + \\big) \\)'; + const result = preprocessLaTeX(content); + expect(result).toContain('\\big)'); + expect(result).toBe('$ f(x) + \\big) $'); + }); + + it('handles multiple block equations', () => { + const content = 'First \\[ a \\] then \\[ b \\right] c \\]'; + const result = preprocessLaTeX(content); + expect(result).toBe('First $$a$$ then $$ b \\right] c $$'); + }); +}); diff --git a/web/src/utils/chat.ts b/web/src/utils/chat.ts index 39c91d6693..3ffd884556 100644 --- a/web/src/utils/chat.ts +++ b/web/src/utils/chat.ts @@ -39,14 +39,24 @@ export const buildMessageUuidWithRole = ( // Preprocess LaTeX equations to be rendered by KaTeX // ref: https://github.com/remarkjs/react-markdown/issues/785 +// +// Delimiter matching: we only treat \] and \) as block/inline endings when they +// are not part of a LaTeX command (e.g. \right], \big), \left)). Use a negative +// lookbehind (? { const blockProcessedContent = content.replace( - /\\\[([\s\S]*?)\\\]/g, + BLOCK_MATH_RE, (_, equation) => `$$${equation}$$`, ); const inlineProcessedContent = blockProcessedContent.replace( - /\\\(([\s\S]*?)\\\)/g, + INLINE_MATH_RE, (_, equation) => `$${equation}$`, ); return inlineProcessedContent;