diff --git a/packages/tui/CHANGELOG.md b/packages/tui/CHANGELOG.md index bb7634ec6..23cb8dc2f 100644 --- a/packages/tui/CHANGELOG.md +++ b/packages/tui/CHANGELOG.md @@ -11,6 +11,7 @@ - Fixed fullscreen double-click selection splitting paths and tokens at `~`, `.`, `\`, and `+`. Word characters now match iTerm2's defaults ([#10980](https://github.com/earendil-works/pi/issues/10980)) - Fixed `fuzzyMatch()` ranking a query scattered over an earlier part of the text above a contiguous match later in it. It now also scores each contiguous occurrence of the query and keeps the best alignment ([#11220](https://github.com/earendil-works/pi/issues/11220), [#11296](https://github.com/earendil-works/pi/pull/11296)) - Fixed `ProcessTerminal.setTitle()` writing control characters from the title, which could end the title sequence and send the rest to the terminal as escape sequences +- Fixed display math wider than the content width being wrapped row by row, which split fractions and sums apart. It now falls back to the raw LaTeX source ([#11483](https://github.com/earendil-works/pi/issues/11483)) ## [1.4.0] - 2026-10-21 diff --git a/packages/tui/src/components/markdown.ts b/packages/tui/src/components/markdown.ts index 9194c2251..a3b526d73 100644 --- a/packages/tui/src/components/markdown.ts +++ b/packages/tui/src/components/markdown.ts @@ -536,10 +536,14 @@ export class Markdown implements Component { case "latexBlock": { const latexToken = token as LatexToken; - const rendered = + let rendered = !latexToken.pending && this.options.renderLatex !== false - ? (renderLatex(latexToken.text, { display: true }) ?? latexToken.raw.trim()) - : latexToken.raw.trim(); + ? renderLatex(latexToken.text, { display: true }) + : undefined; + // A 2D layout cannot be wrapped row by row; fall back to the raw source when it does not fit. + if (rendered === undefined || rendered.split("\n").some((line) => visibleWidth(line) > width)) { + rendered = latexToken.raw.trim(); + } for (const line of rendered.split("\n")) { lines.push(this.applyDefaultStyle(line)); } diff --git a/packages/tui/test/markdown.test.ts b/packages/tui/test/markdown.test.ts index 38e6ceb5a..b20d84dfe 100644 --- a/packages/tui/test/markdown.test.ts +++ b/packages/tui/test/markdown.test.ts @@ -966,6 +966,29 @@ Inline \(A_{sf}\)`, ); }); + // Regression test for https://github.com/earendil-works/pi/issues/11483 + it("falls back to raw source when display math is wider than the content width", () => { + const source = [ + "\\[", + String.raw`\frac{\partial^2 u}{\partial x^2} + \frac{\partial^2 u}{\partial y^2} = \sum_{i=1}^{n} \frac{1}{i^2} + \int_0^1 f(x)\,dx`, + "\\]", + ].join("\n"); + const render = (width: number) => + new Markdown(source, 0, 0, defaultMarkdownTheme).render(width).map((line) => stripAnsi(line).trimEnd()); + + assert.deepStrictEqual(render(40), [ + "∂² u ∂² u n 1 1", + "──── + ──── = ∑ ── +∫ f(x) dx", + "∂ x² ∂ y² i=1 i² 0", + ]); + + const narrow = render(30); + assert.strictEqual(narrow[0], "\\["); + assert.strictEqual(narrow[narrow.length - 1], "\\]"); + assert.ok(!narrow.some((line) => line.includes("─"))); + assert.ok(narrow.every((line) => line.length <= 30)); + }); + // Regression tests for https://github.com/earendil-works/pi/issues/11159 describe("unclosed display math before tables (#11159)", () => { const table = ["| A | B |", "|---|---|", "| 1 | 2 |"];