fix(tui): stop wide input slice overflow closes #1982 (#2006)

This commit is contained in:
Fero
2026-03-09 23:19:26 +01:00
committed by GitHub
parent fd0c7c3bdb
commit 15e0957b04
3 changed files with 22 additions and 9 deletions

View File

@@ -4,7 +4,7 @@
### Fixed ### Fixed
- Fixed `Input` horizontal scrolling for wide Unicode text (CJK, fullwidth characters) to use visual column width instead of string length, preventing rendered line overflow and TUI crashes ([#1982](https://github.com/badlogic/pi-mono/issues/1982)) - Fixed `Input` horizontal scrolling for wide Unicode text (CJK, fullwidth characters) to use visual column width and strict slice boundaries, preventing rendered line overflow and TUI crashes ([#1982](https://github.com/badlogic/pi-mono/issues/1982))
## [0.57.1] - 2026-03-07 ## [0.57.1] - 2026-03-07

View File

@@ -468,8 +468,8 @@ export class Input implements Component, Focusable {
startCol = Math.max(0, cursorCol - halfWidth); startCol = Math.max(0, cursorCol - halfWidth);
} }
visibleText = sliceByColumn(this.value, startCol, scrollWidth); visibleText = sliceByColumn(this.value, startCol, scrollWidth, true);
const beforeCursor = sliceByColumn(this.value, startCol, Math.max(0, cursorCol - startCol)); const beforeCursor = sliceByColumn(this.value, startCol, Math.max(0, cursorCol - startCol), true);
cursorDisplay = beforeCursor.length; cursorDisplay = beforeCursor.length;
} else { } else {
visibleText = ""; visibleText = "";

View File

@@ -43,15 +43,28 @@ describe("Input component", () => {
"这是一段测试文本,用于验证中文字符在终端中的显示宽度是否被正确计算,如果不正确就会导致用户界面崩溃的问题", "这是一段测试文本,用于验证中文字符在终端中的显示宽度是否被正确计算,如果不正确就会导致用户界面崩溃的问题",
"", "",
]; ];
const cursorPositions = [
{ label: "start", move: (_input: Input) => {} },
{
label: "middle",
move: (input: Input) => {
for (let i = 0; i < 10; i++) input.handleInput("\x1b[C");
},
},
{ label: "end", move: (input: Input) => input.handleInput("\x05") },
];
for (const text of cases) { for (const text of cases) {
const input = new Input(); for (const { label, move } of cursorPositions) {
input.setValue(text); const input = new Input();
input.focused = true; input.setValue(text);
input.focused = true;
move(input);
const [line] = input.render(width); const [line] = input.render(width);
assert.ok(line); assert.ok(line);
assert.ok(visibleWidth(line) <= width, `rendered line overflowed for ${text}`); assert.ok(visibleWidth(line) <= width, `rendered line overflowed for ${text} at ${label}`);
}
} }
}); });