Skip to content

Commit bcd576a

Browse files
committed
fix: LaTeX processing within blockquotes
1 parent 78f9523 commit bcd576a

File tree

2 files changed

+58
-0
lines changed

2 files changed

+58
-0
lines changed

tools/server/webui/src/lib/utils/latex-protection.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,4 +318,38 @@ $$\n\\pi_n(\\mathbb{S}^3) = \\begin{cases}
318318
// mhchem-escape would insert a backslash here.
319319
expect(output).toBe('mchem pu:\n$\\pu{-572 kJ mol^{-1}}$');
320320
});
321+
322+
test('LaTeX in blockquotes with display math', () => {
323+
const input =
324+
'> **Definition (limit):** \n> \\[\n> \\lim_{x\\to a} f(x) = L\n> \\]\n> means that as \\(x\\) gets close to \\(a\\).';
325+
const output = preprocessLaTeX(input);
326+
327+
// Blockquote markers should be preserved, LaTeX should be converted
328+
expect(output).toContain('> **Definition (limit):**');
329+
expect(output).toContain('$$');
330+
expect(output).toContain('$x$');
331+
expect(output).not.toContain('\\[');
332+
expect(output).not.toContain('\\]');
333+
expect(output).not.toContain('\\(');
334+
expect(output).not.toContain('\\)');
335+
});
336+
337+
test('LaTeX in blockquotes with inline math', () => {
338+
const input =
339+
"> The derivative \\(f'(x)\\) at point \\(x=a\\) measures slope.\n> Formula: \\(f'(a)=\\lim_{h\\to 0}\\frac{f(a+h)-f(a)}{h}\\)";
340+
const output = preprocessLaTeX(input);
341+
342+
// Blockquote markers should be preserved, inline LaTeX converted to $...$
343+
expect(output).toContain("> The derivative $f'(x)$ at point $x=a$ measures slope.");
344+
expect(output).toContain("> Formula: $f'(a)=\\lim_{h\\to 0}\\frac{f(a+h)-f(a)}{h}$");
345+
});
346+
347+
test('Mixed content with blockquotes and regular text', () => {
348+
const input =
349+
'Regular text with \\(x^2\\).\n\n> Quote with \\(y^2\\).\n\nMore text with \\(z^2\\).';
350+
const output = preprocessLaTeX(input);
351+
352+
// All LaTeX should be converted, blockquote markers preserved
353+
expect(output).toBe('Regular text with $x^2$.\n\n> Quote with $y^2$.\n\nMore text with $z^2$.');
354+
});
321355
});

tools/server/webui/src/lib/utils/latex-protection.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,20 @@ export function preprocessLaTeX(content: string): string {
148148
// See also:
149149
// https://github.com/danny-avila/LibreChat/blob/main/client/src/utils/latex.ts
150150

151+
// Step 0: Temporarily remove blockquote markers (>) to process LaTeX correctly
152+
// Store the structure so we can restore it later
153+
const blockquoteMarkers: Map<number, string> = new Map();
154+
const lines = content.split('\n');
155+
const processedLines = lines.map((line, index) => {
156+
const match = line.match(/^(>\s*)/);
157+
if (match) {
158+
blockquoteMarkers.set(index, match[1]);
159+
return line.slice(match[1].length);
160+
}
161+
return line;
162+
});
163+
content = processedLines.join('\n');
164+
151165
// Step 1: Protect code blocks
152166
const codeBlocks: string[] = [];
153167

@@ -239,5 +253,15 @@ export function preprocessLaTeX(content: string): string {
239253
}
240254
);
241255

256+
// Step 7: Restore blockquote markers
257+
if (blockquoteMarkers.size > 0) {
258+
const finalLines = content.split('\n');
259+
const restoredLines = finalLines.map((line, index) => {
260+
const marker = blockquoteMarkers.get(index);
261+
return marker ? marker + line : line;
262+
});
263+
content = restoredLines.join('\n');
264+
}
265+
242266
return content;
243267
}

0 commit comments

Comments
 (0)