mirror of
https://github.com/unslothai/unsloth.git
synced 2026-07-09 15:58:41 +00:00
Studio: fix currency and indentation edge cases in LaTeX rendering (#6957)
* Studio: fix link, currency and indentation edge cases in LaTeX rendering Follow-up to #6914. Three fixes to studio/frontend/src/lib/latex.ts: - Skip reference-link definition URLs ([id]: url) during delimiter conversion, so escaped parens in such URLs are not rewritten as math. - Preserve the opener line's indentation when emitting a display $$ block, so a \[...\] inside a list item stays part of the list. - Stop a currency amount from pairing with a converted span's opening $, which swallowed the price into math (for example $5 + x \(y\)). * Exclude GFM footnote definitions from the reference-URL skip A footnote definition like [^1]: \(x\) had its body treated as a link destination, so leading math was left literal. Skip [^...] labels. * Merge overlapping link destination regions A reference-def token can nest inline-link spans (for example [1]: http://h/[a](b)/foo\(x\)), so the combined spans could overlap and isInRegion's binary search missed the outer one, rewriting the URL. Merge overlapping spans before the search. * Guard lineStart when the display opener is at index 0 Behavior is unchanged (lastIndexOf clamps a negative fromIndex to 0), but the explicit guard avoids relying on that implicit clamp. * Scope to indentation and currency fixes Drop the reference-link URL protection added earlier. It guards a case models effectively never emit (escaped parens in a reference-style URL), and approximating CommonMark reference definitions with a regex needs open-ended special-casing. Keep the two high-value fixes: preserve display math indentation (including multi-line bodies) inside a list item, and stop a currency amount from pairing with a converted span's opening dollar sign.
This commit is contained in:
parent
f1a2621631
commit
de60a3a994
1 changed files with 25 additions and 3 deletions
|
|
@ -173,7 +173,11 @@ function looksLikeMathBody(body: string): boolean {
|
|||
* (`**$X$**`, `__$X$__`) are always math: LLMs use that for "bold math"
|
||||
* and the heuristic would otherwise reject prose-shaped bodies like "90 - x".
|
||||
*/
|
||||
function hasInlineMathCloser(content: string, offset: number): boolean {
|
||||
function hasInlineMathCloser(
|
||||
content: string,
|
||||
offset: number,
|
||||
mathRegions: Array<[number, number]>,
|
||||
): boolean {
|
||||
const MAX_SPAN = 200;
|
||||
const limit = Math.min(content.length, offset + 1 + MAX_SPAN);
|
||||
for (let i = offset + 1; i < limit; i++) {
|
||||
|
|
@ -181,6 +185,9 @@ function hasInlineMathCloser(content: string, offset: number): boolean {
|
|||
if (c === "\n") return false;
|
||||
if (c !== "$") continue;
|
||||
if (content[i - 1] === "\\") continue;
|
||||
// A `$` opening a generated span (from `\(...\)`) is not a currency closer;
|
||||
// pairing with it would swallow the price into math (`$5 + x \(y\)`).
|
||||
if (isInRegion(i, mathRegions)) return false;
|
||||
if (content[i + 1] === "$") {
|
||||
i++;
|
||||
continue;
|
||||
|
|
@ -294,7 +301,22 @@ function convertLatexDelimiters(content: string): {
|
|||
continue;
|
||||
}
|
||||
append(content.slice(last, match.index));
|
||||
const wrapped = isDisplay ? `\n$$\n${body}\n$$\n` : `$${body}$`;
|
||||
let wrapped: string;
|
||||
if (isDisplay) {
|
||||
// Keep the opener's leading indentation so a `$$` block inside a list item
|
||||
// stays in the container instead of breaking out at column 0. Only when the
|
||||
// opener is whitespace-prefixed, so inline `text \[x\]` keeps column 0.
|
||||
const lineStart =
|
||||
match.index > 0 ? content.lastIndexOf("\n", match.index - 1) + 1 : 0;
|
||||
const prefix = content.slice(lineStart, match.index);
|
||||
const indent = /^\s*$/.test(prefix) ? prefix : "";
|
||||
// Indent every body line, not just the first, so multi-line display math
|
||||
// (`\[a\nb\]`) stays wholly inside the container.
|
||||
const inner = indent ? body.replace(/\n/g, `\n${indent}`) : body;
|
||||
wrapped = `\n${indent}$$\n${indent}${inner}\n${indent}$$\n`;
|
||||
} else {
|
||||
wrapped = `$${body}$`;
|
||||
}
|
||||
const start = append(wrapped);
|
||||
mathRegions.push([start, offset]);
|
||||
last = matchEnd;
|
||||
|
|
@ -334,7 +356,7 @@ export function preprocessLaTeX(content: string): string {
|
|||
if (isInRegion(offset, mathRegions)) {
|
||||
return match;
|
||||
}
|
||||
if (hasInlineMathCloser(text, offset)) {
|
||||
if (hasInlineMathCloser(text, offset, mathRegions)) {
|
||||
return match;
|
||||
}
|
||||
return "\\" + match;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue