From 9c4a3f35183991cb6e8ed4fb1eb2a06892df3d06 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Sat, 30 May 2026 01:02:48 +0200 Subject: [PATCH] Fix ANSI wrapping stack overflow closes #5185 --- packages/tui/CHANGELOG.md | 4 ++++ packages/tui/src/utils.ts | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/tui/CHANGELOG.md b/packages/tui/CHANGELOG.md index c4eb0d08..97a30a79 100644 --- a/packages/tui/CHANGELOG.md +++ b/packages/tui/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed ANSI text wrapping to avoid stack overflows on very long wrapped lines ([#5185](https://github.com/earendil-works/pi/issues/5185)). + ## [0.77.0] - 2026-05-28 ### Fixed diff --git a/packages/tui/src/utils.ts b/packages/tui/src/utils.ts index 97f93f65..d613a76d 100644 --- a/packages/tui/src/utils.ts +++ b/packages/tui/src/utils.ts @@ -670,7 +670,10 @@ export function wrapTextWithAnsi(text: string, width: number): string[] { for (const inputLine of inputLines) { // Prepend active ANSI codes from previous lines (except for first line) const prefix = result.length > 0 ? tracker.getActiveCodes() : ""; - result.push(...wrapSingleLine(prefix + inputLine, width)); + const wrappedLines = wrapSingleLine(prefix + inputLine, width); + for (const wrappedLine of wrappedLines) { + result.push(wrappedLine); + } // Update tracker with codes from this line for next iteration updateTrackerFromText(inputLine, tracker); } @@ -714,7 +717,9 @@ function wrapSingleLine(line: string, width: number): string[] { // Break long token - breakLongWord handles its own resets const broken = breakLongWord(token, width, tracker); - wrapped.push(...broken.slice(0, -1)); + for (let i = 0; i < broken.length - 1; i++) { + wrapped.push(broken[i]!); + } currentLine = broken[broken.length - 1]; currentVisibleLength = visibleWidth(currentLine); continue;