fix(tui): preserve ANSI order at slice boundaries

Fixes color bleeding after mouse selection and search highlights in
fullscreen mode when a styled token ends at the highlight boundary.

closes #10169
This commit is contained in:
Armin Ronacher
2026-10-01 00:05:28 +02:00
parent d850edee9c
commit 17f3dccbef
3 changed files with 26 additions and 2 deletions
+4
View File
@@ -6,6 +6,10 @@
- Added `TuiAltScreen.getScreenLines()`, which returns the lines of the last rendered frame.
### Fixed
- Fixed color bleeding past mouse selections and search highlights in fullscreen mode when a styled token ends at the highlight boundary ([#10169](https://github.com/earendil-works/pi/issues/10169))
## [0.99.2] - 2026-09-30
## [0.99.1] - 2026-09-29
+5 -2
View File
@@ -1273,8 +1273,11 @@ export function sliceWithWidth(
while (i < line.length) {
const ansi = extractAnsiCode(line, i);
if (ansi) {
if (currentCol >= startCol && currentCol < endCol) result += ansi.code;
else if (currentCol < startCol) pendingAnsi += ansi.code;
if (currentCol >= startCol && currentCol < endCol) {
// Keep original order: codes from before the range must precede codes at the boundary
result += pendingAnsi + ansi.code;
pendingAnsi = "";
} else if (currentCol < startCol) pendingAnsi += ansi.code;
i += ansi.length;
continue;
}
@@ -0,0 +1,17 @@
import assert from "node:assert";
import { describe, it } from "node:test";
import { sliceByColumn } from "../src/utils.ts";
// https://github.com/earendil-works/pi/issues/10169
describe("sliceByColumn ANSI order regression", () => {
it("keeps a reset at the slice start after earlier style codes", () => {
const line = "\x1b[32mfoo\x1b[39m bar";
assert.strictEqual(sliceByColumn(line, 3, 4, true), "\x1b[32m\x1b[39m bar");
});
it("does not leak color into text after a highlighted token", () => {
const line = "Another \x1b[35malpha\x1b[39m line with \x1b[35mbeta\x1b[39m later.";
const after = sliceByColumn(line, 13, 100, true);
assert.strictEqual(after, "\x1b[35m\x1b[39m line with \x1b[35mbeta\x1b[39m later.");
});
});