diff --git a/packages/terminal-core/src/ansi.test.ts b/packages/terminal-core/src/ansi.test.ts index 2b5f86a03b3..f43061776a7 100644 --- a/packages/terminal-core/src/ansi.test.ts +++ b/packages/terminal-core/src/ansi.test.ts @@ -60,4 +60,13 @@ describe("terminal ansi helpers", () => { expect(truncateToVisibleWidth("表文", 1)).toBe(""); expect(visibleWidth(truncateToVisibleWidth("表文", 1))).toBe(0); }); + + it("reuses the ANSI scanner across truncation calls", () => { + expect(truncateToVisibleWidth("\u001B[31mabc\u001B[0m", 2)).toBe("\u001B[31mab\u001B[0m"); + expect(truncateToVisibleWidth("plain", 3)).toBe("pla"); + expect( + truncateToVisibleWidth("\u001B]8;;https://openclaw.ai\u001B\\link\u001B]8;;\u001B\\", 2), + ).toBe("\u001B]8;;https://openclaw.ai\u001B\\li\u001B]8;;\u001B\\"); + expect(truncateToVisibleWidth("\u001B[32mxy\u001B[0m", 1)).toBe("\u001B[32mx\u001B[0m"); + }); }); diff --git a/packages/terminal-core/src/ansi.ts b/packages/terminal-core/src/ansi.ts index bb5c164d87c..0739344df7a 100644 --- a/packages/terminal-core/src/ansi.ts +++ b/packages/terminal-core/src/ansi.ts @@ -6,6 +6,7 @@ const ANSI_OSC_PATTERN = "\\x1b\\][^\\x07\\x1b]*(?:\\x1b\\\\|\\x07)"; const ANSI_CSI_REGEX = new RegExp(ANSI_CSI_PATTERN, "g"); const ANSI_OSC_REGEX = new RegExp(ANSI_OSC_PATTERN, "g"); +const ANSI_SEQUENCE_REGEX = new RegExp(`${ANSI_OSC_PATTERN}|${ANSI_CSI_PATTERN}`, "g"); const graphemeSegmenter = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter(undefined, { granularity: "grapheme" }) @@ -134,7 +135,7 @@ export function truncateToVisibleWidth(input: string, maxWidth: number): string if (visibleWidth(input) <= maxWidth) { return input; } - const ansi = new RegExp(`${ANSI_OSC_PATTERN}|${ANSI_CSI_PATTERN}`, "g"); + ANSI_SEQUENCE_REGEX.lastIndex = 0; let out = ""; let used = 0; let pos = 0; @@ -157,7 +158,7 @@ export function truncateToVisibleWidth(input: string, maxWidth: number): string } }; let match: RegExpExecArray | null; - while ((match = ansi.exec(input)) !== null) { + while ((match = ANSI_SEQUENCE_REGEX.exec(input)) !== null) { appendVisible(input.slice(pos, match.index)); out += match[0]; pos = match.index + match[0].length;