fix(terminal): preserve UTF-8 replay chunks (#1181)

* fix(terminal): preserve UTF-8 replay chunks

* fix(terminal): avoid replay trim prepends

---------

Co-authored-by: Isaac Sanchez <isanchez-hawkins@arize.com>
This commit is contained in:
Isaac Sanchez-Hawkins
2026-05-12 11:05:03 +03:00
committed by GitHub
co-authored by Isaac Sanchez
parent 2a9c7e6bca
commit 323347f50e
2 changed files with 23 additions and 2 deletions
@@ -10,8 +10,20 @@ const trimTerminalOutputChunkToMaxBytes = (data, maxBytes) => {
return data;
}
const trimmedBuffer = Buffer.from(data, 'utf8').subarray(-maxBytes);
return trimmedBuffer.toString('utf8');
const kept = [];
let trimmedBytes = 0;
const characters = Array.from(data);
for (let index = characters.length - 1; index >= 0; index -= 1) {
const character = characters[index];
const characterBytes = Buffer.byteLength(character, 'utf8');
if (trimmedBytes + characterBytes > maxBytes) {
break;
}
kept.push(character);
trimmedBytes += characterBytes;
}
return kept.reverse().join('');
};
export const createTerminalOutputReplayBuffer = () => ({
@@ -56,6 +56,15 @@ describe('terminal output replay buffer', () => {
expect(bufferState.totalBytes).toBe(4);
});
it('does not split multibyte characters when trimming oversized chunks', () => {
const bufferState = createTerminalOutputReplayBuffer();
const chunk = appendTerminalOutputReplayChunk(bufferState, '🙂x', 2);
expect(chunk?.data).toBe('x');
expect(chunk?.bytes).toBe(1);
expect(bufferState.totalBytes).toBe(1);
});
it('uses the default max bytes when not provided', () => {
const bufferState = createTerminalOutputReplayBuffer();
const chunk = appendTerminalOutputReplayChunk(bufferState, 'ok');